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 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4632 if (!this.rendered) {
4635 this.toggleHeaderInput(false);
4636 //this.el.setStyle('display', 'block');
4637 this.el.removeClass('hideing');
4638 this.el.dom.style.display='block';
4640 Roo.get(document.body).addClass('modal-open');
4642 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4645 this.el.addClass('show');
4646 this.el.addClass('in');
4649 this.el.addClass('show');
4650 this.el.addClass('in');
4653 // not sure how we can show data in here..
4655 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658 Roo.get(document.body).addClass("x-body-masked");
4660 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4661 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662 this.maskEl.dom.style.display = 'block';
4663 this.maskEl.addClass('show');
4668 this.fireEvent('show', this);
4670 // set zindex here - otherwise it appears to be ignored...
4671 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674 this.items.forEach( function(e) {
4675 e.layout ? e.layout() : false;
4683 if(this.fireEvent("beforehide", this) !== false){
4685 this.maskEl.removeClass('show');
4687 this.maskEl.dom.style.display = '';
4688 Roo.get(document.body).removeClass("x-body-masked");
4689 this.el.removeClass('in');
4690 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4692 if(this.animate){ // why
4693 this.el.addClass('hideing');
4694 this.el.removeClass('show');
4696 if (!this.el.hasClass('hideing')) {
4697 return; // it's been shown again...
4700 this.el.dom.style.display='';
4702 Roo.get(document.body).removeClass('modal-open');
4703 this.el.removeClass('hideing');
4707 this.el.removeClass('show');
4708 this.el.dom.style.display='';
4709 Roo.get(document.body).removeClass('modal-open');
4712 this.fireEvent('hide', this);
4715 isVisible : function()
4718 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4722 addButton : function(str, cb)
4726 var b = Roo.apply({}, { html : str } );
4727 b.xns = b.xns || Roo.bootstrap;
4728 b.xtype = b.xtype || 'Button';
4729 if (typeof(b.listeners) == 'undefined') {
4730 b.listeners = { click : cb.createDelegate(this) };
4733 var btn = Roo.factory(b);
4735 btn.render(this.getButtonContainer());
4741 setDefaultButton : function(btn)
4743 //this.el.select('.modal-footer').()
4746 resizeTo: function(w,h)
4748 this.dialogEl.setWidth(w);
4750 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4752 this.bodyEl.setHeight(h - diff);
4754 this.fireEvent('resize', this);
4757 setContentSize : function(w, h)
4761 onButtonClick: function(btn,e)
4764 this.fireEvent('btnclick', btn.name, e);
4767 * Set the title of the Dialog
4768 * @param {String} str new Title
4770 setTitle: function(str) {
4771 this.titleEl.dom.innerHTML = str;
4775 * Set the body of the Dialog
4776 * @param {String} str new Title
4778 setBody: function(str) {
4779 this.bodyEl.dom.innerHTML = str;
4782 * Set the body of the Dialog using the template
4783 * @param {Obj} data - apply this data to the template and replace the body contents.
4785 applyBody: function(obj)
4788 Roo.log("Error - using apply Body without a template");
4791 this.tmpl.overwrite(this.bodyEl, obj);
4794 getChildHeight : function(child_nodes)
4798 child_nodes.length == 0
4803 var child_height = 0;
4805 for(var i = 0; i < child_nodes.length; i++) {
4808 * for modal with tabs...
4809 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4811 var layout_childs = child_nodes[i].childNodes;
4813 for(var j = 0; j < layout_childs.length; j++) {
4815 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4817 var layout_body_childs = layout_childs[j].childNodes;
4819 for(var k = 0; k < layout_body_childs.length; k++) {
4821 if(layout_body_childs[k].classList.contains('navbar')) {
4822 child_height += layout_body_childs[k].offsetHeight;
4826 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4828 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4830 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4832 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4848 child_height += child_nodes[i].offsetHeight;
4849 // Roo.log(child_nodes[i].offsetHeight);
4852 return child_height;
4854 toggleHeaderInput : function(is_edit)
4856 if (!this.editableTitle) {
4857 return; // not editable.
4859 if (is_edit && this.is_header_editing) {
4860 return; // already editing..
4864 this.headerEditEl.dom.value = this.title;
4865 this.headerEditEl.removeClass('d-none');
4866 this.headerEditEl.dom.focus();
4867 this.titleEl.addClass('d-none');
4869 this.is_header_editing = true;
4872 // flip back to not editing.
4873 this.title = this.headerEditEl.dom.value;
4874 this.headerEditEl.addClass('d-none');
4875 this.titleEl.removeClass('d-none');
4876 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877 this.is_header_editing = false;
4878 this.fireEvent('titlechanged', this, this.title);
4887 Roo.apply(Roo.bootstrap.Modal, {
4889 * Button config that displays a single OK button
4898 * Button config that displays Yes and No buttons
4914 * Button config that displays OK and Cancel buttons
4929 * Button config that displays Yes, No and Cancel buttons
4954 * messagebox - can be used as a replace
4958 * @class Roo.MessageBox
4959 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4968 // process text value...
4972 // Show a dialog using config options:
4974 title:'Save Changes?',
4975 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976 buttons: Roo.Msg.YESNOCANCEL,
4983 Roo.bootstrap.MessageBox = function(){
4984 var dlg, opt, mask, waitTimer;
4985 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986 var buttons, activeTextEl, bwidth;
4990 var handleButton = function(button){
4992 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4996 var handleHide = function(){
4998 dlg.el.removeClass(opt.cls);
5001 // Roo.TaskMgr.stop(waitTimer);
5002 // waitTimer = null;
5007 var updateButtons = function(b){
5010 buttons["ok"].hide();
5011 buttons["cancel"].hide();
5012 buttons["yes"].hide();
5013 buttons["no"].hide();
5014 dlg.footerEl.hide();
5018 dlg.footerEl.show();
5019 for(var k in buttons){
5020 if(typeof buttons[k] != "function"){
5023 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024 width += buttons[k].el.getWidth()+15;
5034 var handleEsc = function(d, k, e){
5035 if(opt && opt.closable !== false){
5045 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046 * @return {Roo.BasicDialog} The BasicDialog element
5048 getDialog : function(){
5050 dlg = new Roo.bootstrap.Modal( {
5053 //constraintoviewport:false,
5055 //collapsible : false,
5060 //buttonAlign:"center",
5061 closeClick : function(){
5062 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065 handleButton("cancel");
5070 dlg.on("hide", handleHide);
5072 //dlg.addKeyListener(27, handleEsc);
5074 this.buttons = buttons;
5075 var bt = this.buttonText;
5076 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5081 bodyEl = dlg.bodyEl.createChild({
5083 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084 '<textarea class="roo-mb-textarea"></textarea>' +
5085 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5087 msgEl = bodyEl.dom.firstChild;
5088 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089 textboxEl.enableDisplayMode();
5090 textboxEl.addKeyListener([10,13], function(){
5091 if(dlg.isVisible() && opt && opt.buttons){
5094 }else if(opt.buttons.yes){
5095 handleButton("yes");
5099 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100 textareaEl.enableDisplayMode();
5101 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102 progressEl.enableDisplayMode();
5104 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105 var pf = progressEl.dom.firstChild;
5107 pp = Roo.get(pf.firstChild);
5108 pp.setHeight(pf.offsetHeight);
5116 * Updates the message box body text
5117 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118 * the XHTML-compliant non-breaking space character '&#160;')
5119 * @return {Roo.MessageBox} This message box
5121 updateText : function(text)
5123 if(!dlg.isVisible() && !opt.width){
5124 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5127 msgEl.innerHTML = text || ' ';
5129 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5132 Math.min(opt.width || cw , this.maxWidth),
5133 Math.max(opt.minWidth || this.minWidth, bwidth)
5136 activeTextEl.setWidth(w);
5138 if(dlg.isVisible()){
5139 dlg.fixedcenter = false;
5141 // to big, make it scroll. = But as usual stupid IE does not support
5144 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5148 bodyEl.dom.style.height = '';
5149 bodyEl.dom.style.overflowY = '';
5152 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5154 bodyEl.dom.style.overflowX = '';
5157 dlg.setContentSize(w, bodyEl.getHeight());
5158 if(dlg.isVisible()){
5159 dlg.fixedcenter = true;
5165 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5166 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169 * @return {Roo.MessageBox} This message box
5171 updateProgress : function(value, text){
5173 this.updateText(text);
5176 if (pp) { // weird bug on my firefox - for some reason this is not defined
5177 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5184 * Returns true if the message box is currently displayed
5185 * @return {Boolean} True if the message box is visible, else false
5187 isVisible : function(){
5188 return dlg && dlg.isVisible();
5192 * Hides the message box if it is displayed
5195 if(this.isVisible()){
5201 * Displays a new message box, or reinitializes an existing message box, based on the config options
5202 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203 * The following config object properties are supported:
5205 Property Type Description
5206 ---------- --------------- ------------------------------------------------------------------------------------
5207 animEl String/Element An id or Element from which the message box should animate as it opens and
5208 closes (defaults to undefined)
5209 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable Boolean False to hide the top-right close button (defaults to true). Note that
5212 progress and wait dialogs will ignore this property and always hide the
5213 close button as they can only be closed programmatically.
5214 cls String A custom CSS class to apply to the message box element
5215 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5216 displayed (defaults to 75)
5217 fn Function A callback function to execute after closing the dialog. The arguments to the
5218 function will be btn (the name of the button that was clicked, if applicable,
5219 e.g. "ok"), and text (the value of the active text field, if applicable).
5220 Progress and wait dialogs will ignore this option since they do not respond to
5221 user actions and can only be closed programmatically, so any required function
5222 should be called by the same code after it closes the dialog.
5223 icon String A CSS class that provides a background image to be used as an icon for
5224 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5226 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5227 modal Boolean False to allow user interaction with the page while the message box is
5228 displayed (defaults to true)
5229 msg String A string that will replace the existing message box body text (defaults
5230 to the XHTML-compliant non-breaking space character ' ')
5231 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5232 progress Boolean True to display a progress bar (defaults to false)
5233 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5236 title String The title text
5237 value String The string value to set into the active textbox element if displayed
5238 wait Boolean True to display a progress bar (defaults to false)
5239 width Number The width of the dialog in pixels
5246 msg: 'Please enter your address:',
5248 buttons: Roo.MessageBox.OKCANCEL,
5251 animEl: 'addAddressBtn'
5254 * @param {Object} config Configuration options
5255 * @return {Roo.MessageBox} This message box
5257 show : function(options)
5260 // this causes nightmares if you show one dialog after another
5261 // especially on callbacks..
5263 if(this.isVisible()){
5266 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5268 Roo.log("New Dialog Message:" + options.msg )
5269 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273 var d = this.getDialog();
5275 d.setTitle(opt.title || " ");
5276 d.closeEl.setDisplayed(opt.closable !== false);
5277 activeTextEl = textboxEl;
5278 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5283 textareaEl.setHeight(typeof opt.multiline == "number" ?
5284 opt.multiline : this.defaultTextHeight);
5285 activeTextEl = textareaEl;
5294 progressEl.setDisplayed(opt.progress === true);
5296 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5298 this.updateProgress(0);
5299 activeTextEl.dom.value = opt.value || "";
5301 dlg.setDefaultButton(activeTextEl);
5303 var bs = opt.buttons;
5307 }else if(bs && bs.yes){
5308 db = buttons["yes"];
5310 dlg.setDefaultButton(db);
5312 bwidth = updateButtons(opt.buttons);
5313 this.updateText(opt.msg);
5315 d.el.addClass(opt.cls);
5317 d.proxyDrag = opt.proxyDrag === true;
5318 d.modal = opt.modal !== false;
5319 d.mask = opt.modal !== false ? mask : false;
5321 // force it to the end of the z-index stack so it gets a cursor in FF
5322 document.body.appendChild(dlg.el.dom);
5323 d.animateTarget = null;
5324 d.show(options.animEl);
5330 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5331 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332 * and closing the message box when the process is complete.
5333 * @param {String} title The title bar text
5334 * @param {String} msg The message box body text
5335 * @return {Roo.MessageBox} This message box
5337 progress : function(title, msg){
5344 minWidth: this.minProgressWidth,
5351 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352 * If a callback function is passed it will be called after the user clicks the button, and the
5353 * id of the button that was clicked will be passed as the only parameter to the callback
5354 * (could also be the top-right close button).
5355 * @param {String} title The title bar text
5356 * @param {String} msg The message box body text
5357 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358 * @param {Object} scope (optional) The scope of the callback function
5359 * @return {Roo.MessageBox} This message box
5361 alert : function(title, msg, fn, scope)
5376 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5377 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378 * You are responsible for closing the message box when the process is complete.
5379 * @param {String} msg The message box body text
5380 * @param {String} title (optional) The title bar text
5381 * @return {Roo.MessageBox} This message box
5383 wait : function(msg, title){
5394 waitTimer = Roo.TaskMgr.start({
5396 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407 * @param {String} title The title bar text
5408 * @param {String} msg The message box body text
5409 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410 * @param {Object} scope (optional) The scope of the callback function
5411 * @return {Roo.MessageBox} This message box
5413 confirm : function(title, msg, fn, scope){
5417 buttons: this.YESNO,
5426 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5428 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429 * (could also be the top-right close button) and the text that was entered will be passed as the two
5430 * parameters to the callback.
5431 * @param {String} title The title bar text
5432 * @param {String} msg The message box body text
5433 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434 * @param {Object} scope (optional) The scope of the callback function
5435 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437 * @return {Roo.MessageBox} This message box
5439 prompt : function(title, msg, fn, scope, multiline){
5443 buttons: this.OKCANCEL,
5448 multiline: multiline,
5455 * Button config that displays a single OK button
5460 * Button config that displays Yes and No buttons
5463 YESNO : {yes:true, no:true},
5465 * Button config that displays OK and Cancel buttons
5468 OKCANCEL : {ok:true, cancel:true},
5470 * Button config that displays Yes, No and Cancel buttons
5473 YESNOCANCEL : {yes:true, no:true, cancel:true},
5476 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479 defaultTextHeight : 75,
5481 * The maximum width in pixels of the message box (defaults to 600)
5486 * The minimum width in pixels of the message box (defaults to 100)
5491 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5492 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495 minProgressWidth : 250,
5497 * An object containing the default button text strings that can be overriden for localized language support.
5498 * Supported properties are: ok, cancel, yes and no.
5499 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5512 * Shorthand for {@link Roo.MessageBox}
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5524 * @class Roo.bootstrap.nav.Bar
5525 * @extends Roo.bootstrap.Component
5527 * Bootstrap Navbar class
5530 * Create a new Navbar
5531 * @param {Object} config The config object
5535 Roo.bootstrap.nav.Bar = function(config){
5536 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5540 * @event beforetoggle
5541 * Fire before toggle the menu
5542 * @param {Roo.EventObject} e
5544 "beforetoggle" : true
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5557 getAutoCreate : function(){
5560 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5564 initEvents :function ()
5566 //Roo.log(this.el.select('.navbar-toggle',true));
5567 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5576 var size = this.el.getSize();
5577 this.maskEl.setSize(size.width, size.height);
5578 this.maskEl.enableDisplayMode("block");
5587 getChildContainer : function()
5589 if (this.el && this.el.select('.collapse').getCount()) {
5590 return this.el.select('.collapse',true).first();
5605 onToggle : function()
5608 if(this.fireEvent('beforetoggle', this) === false){
5611 var ce = this.el.select('.navbar-collapse',true).first();
5613 if (!ce.hasClass('show')) {
5623 * Expand the navbar pulldown
5625 expand : function ()
5628 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (ce.hasClass('collapsing')) {
5632 ce.dom.style.height = '';
5634 ce.addClass('in'); // old...
5635 ce.removeClass('collapse');
5636 ce.addClass('show');
5637 var h = ce.getHeight();
5639 ce.removeClass('show');
5640 // at this point we should be able to see it..
5641 ce.addClass('collapsing');
5643 ce.setHeight(0); // resize it ...
5644 ce.on('transitionend', function() {
5645 //Roo.log('done transition');
5646 ce.removeClass('collapsing');
5647 ce.addClass('show');
5648 ce.removeClass('collapse');
5650 ce.dom.style.height = '';
5651 }, this, { single: true} );
5653 ce.dom.scrollTop = 0;
5656 * Collapse the navbar pulldown
5658 collapse : function()
5660 var ce = this.el.select('.navbar-collapse',true).first();
5662 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663 // it's collapsed or collapsing..
5666 ce.removeClass('in'); // old...
5667 ce.setHeight(ce.getHeight());
5668 ce.removeClass('show');
5669 ce.addClass('collapsing');
5671 ce.on('transitionend', function() {
5672 ce.dom.style.height = '';
5673 ce.removeClass('collapsing');
5674 ce.addClass('collapse');
5675 }, this, { single: true} );
5695 * @class Roo.bootstrap.nav.Simplebar
5696 * @extends Roo.bootstrap.nav.Bar
5697 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698 * Bootstrap Sidebar class
5700 * @cfg {Boolean} inverse is inverted color
5702 * @cfg {String} type (nav | pills | tabs)
5703 * @cfg {Boolean} arrangement stacked | justified
5704 * @cfg {String} align (left | right) alignment
5706 * @cfg {Boolean} main (true|false) main nav bar? default false
5707 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5709 * @cfg {String} tag (header|footer|nav|div) default is nav
5711 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5715 * Create a new Sidebar
5716 * @param {Object} config The config object
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5740 getAutoCreate : function(){
5744 tag : this.tag || 'div',
5745 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5747 if (['light','white'].indexOf(this.weight) > -1) {
5748 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5750 cfg.cls += ' bg-' + this.weight;
5753 cfg.cls += ' navbar-inverse';
5757 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5759 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5768 cls: 'nav nav-' + this.xtype,
5774 this.type = this.type || 'nav';
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cn[0].cls += ' nav-' + this.type
5780 if (this.type!=='nav') {
5781 Roo.log('nav type must be nav/tabs/pills')
5783 cfg.cn[0].cls += ' navbar-nav'
5789 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790 cfg.cn[0].cls += ' nav-' + this.arrangement;
5794 if (this.align === 'right') {
5795 cfg.cn[0].cls += ' navbar-right';
5820 * navbar-expand-md fixed-top
5824 * @class Roo.bootstrap.nav.Headerbar
5825 * @extends Roo.bootstrap.nav.Simplebar
5826 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827 * Bootstrap Sidebar class
5829 * @cfg {String} brand what is brand
5830 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831 * @cfg {String} brand_href href of the brand
5832 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5833 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838 * Create a new Sidebar
5839 * @param {Object} config The config object
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5855 desktopCenter : false,
5858 getAutoCreate : function(){
5861 tag: this.nav || 'nav',
5862 cls: 'navbar navbar-expand-md',
5868 if (this.desktopCenter) {
5869 cn.push({cls : 'container', cn : []});
5877 cls: 'navbar-toggle navbar-toggler',
5878 'data-toggle': 'collapse',
5883 html: 'Toggle navigation'
5887 cls: 'icon-bar navbar-toggler-icon'
5900 cn.push( Roo.bootstrap.version == 4 ? btn : {
5902 cls: 'navbar-header',
5911 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5915 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5917 if (['light','white'].indexOf(this.weight) > -1) {
5918 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5920 cfg.cls += ' bg-' + this.weight;
5923 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5926 // tag can override this..
5928 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5931 if (this.brand !== '') {
5932 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5935 href: this.brand_href ? this.brand_href : '#',
5936 cls: 'navbar-brand',
5944 cfg.cls += ' main-nav';
5952 getHeaderChildContainer : function()
5954 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955 return this.el.select('.navbar-header',true).first();
5958 return this.getChildContainer();
5961 getChildContainer : function()
5964 return this.el.select('.roo-navbar-collapse',true).first();
5969 initEvents : function()
5971 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5973 if (this.autohide) {
5978 Roo.get(document).on('scroll',function(e) {
5979 var ns = Roo.get(document).getScroll().top;
5980 var os = prevScroll;
5984 ft.removeClass('slideDown');
5985 ft.addClass('slideUp');
5988 ft.removeClass('slideUp');
5989 ft.addClass('slideDown');
6010 * @class Roo.bootstrap.nav.Sidebar
6011 * @extends Roo.bootstrap.nav.Bar
6012 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013 * Bootstrap Sidebar class
6016 * Create a new Sidebar
6017 * @param {Object} config The config object
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6027 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6029 getAutoCreate : function(){
6034 cls: 'sidebar sidebar-nav'
6056 * @class Roo.bootstrap.nav.Group
6057 * @extends Roo.bootstrap.Component
6058 * @children Roo.bootstrap.nav.Item
6059 * Bootstrap NavGroup class
6060 * @cfg {String} align (left|right)
6061 * @cfg {Boolean} inverse
6062 * @cfg {String} type (nav|pills|tab) default nav
6063 * @cfg {String} navId - reference Id for navbar.
6064 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067 * Create a new nav group
6068 * @param {Object} config The config object
6071 Roo.bootstrap.nav.Group = function(config){
6072 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6075 Roo.bootstrap.nav.Group.register(this);
6079 * Fires when the active item changes
6080 * @param {Roo.bootstrap.nav.Group} this
6081 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6109 if (Roo.bootstrap.version == 4) {
6110 if (['tabs','pills'].indexOf(this.type) != -1) {
6111 cfg.cls += ' nav-' + this.type;
6113 // trying to remove so header bar can right align top?
6114 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115 // do not use on header bar...
6116 cfg.cls += ' navbar-nav';
6121 if (['tabs','pills'].indexOf(this.type) != -1) {
6122 cfg.cls += ' nav-' + this.type
6124 if (this.type !== 'nav') {
6125 Roo.log('nav type must be nav/tabs/pills')
6127 cfg.cls += ' navbar-nav'
6131 if (this.parent() && this.parent().sidebar) {
6134 cls: 'dashboard-menu sidebar-menu'
6140 if (this.form === true) {
6143 cls: 'navbar-form form-inline'
6145 //nav navbar-right ml-md-auto
6146 if (this.align === 'right') {
6147 cfg.cls += ' navbar-right ml-md-auto';
6149 cfg.cls += ' navbar-left';
6153 if (this.align === 'right') {
6154 cfg.cls += ' navbar-right ml-md-auto';
6156 cfg.cls += ' mr-auto';
6160 cfg.cls += ' navbar-inverse';
6168 * sets the active Navigation item
6169 * @param {Roo.bootstrap.nav.Item} the new current navitem
6171 setActiveItem : function(item)
6174 Roo.each(this.navItems, function(v){
6179 v.setActive(false, true);
6186 item.setActive(true, true);
6187 this.fireEvent('changed', this, item, prev);
6192 * gets the active Navigation item
6193 * @return {Roo.bootstrap.nav.Item} the current navitem
6195 getActive : function()
6199 Roo.each(this.navItems, function(v){
6210 indexOfNav : function()
6214 Roo.each(this.navItems, function(v,i){
6225 * adds a Navigation item
6226 * @param {Roo.bootstrap.nav.Item} the navitem to add
6228 addItem : function(cfg)
6230 if (this.form && Roo.bootstrap.version == 4) {
6233 var cn = new Roo.bootstrap.nav.Item(cfg);
6235 cn.parentId = this.id;
6236 cn.onRender(this.el, null);
6240 * register a Navigation item
6241 * @param {Roo.bootstrap.nav.Item} the navitem to add
6243 register : function(item)
6245 this.navItems.push( item);
6246 item.navId = this.navId;
6251 * clear all the Navigation item
6254 clearAll : function()
6257 this.el.dom.innerHTML = '';
6260 getNavItem: function(tabId)
6263 Roo.each(this.navItems, function(e) {
6264 if (e.tabId == tabId) {
6274 setActiveNext : function()
6276 var i = this.indexOfNav(this.getActive());
6277 if (i > this.navItems.length) {
6280 this.setActiveItem(this.navItems[i+1]);
6282 setActivePrev : function()
6284 var i = this.indexOfNav(this.getActive());
6288 this.setActiveItem(this.navItems[i-1]);
6290 clearWasActive : function(except) {
6291 Roo.each(this.navItems, function(e) {
6292 if (e.tabId != except.tabId && e.was_active) {
6293 e.was_active = false;
6300 getWasActive : function ()
6303 Roo.each(this.navItems, function(e) {
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6322 * register a Navigation Group
6323 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6325 register : function(navgrp)
6327 this.groups[navgrp.navId] = navgrp;
6331 * fetch a Navigation Group based on the navigation ID
6332 * @param {string} the navgroup to add
6333 * @returns {Roo.bootstrap.nav.Group} the navgroup
6335 get: function(navId) {
6336 if (typeof(this.groups[navId]) == 'undefined') {
6338 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6340 return this.groups[navId] ;
6348 * @class Roo.bootstrap.nav.Item
6349 * @extends Roo.bootstrap.Component
6350 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351 * @parent Roo.bootstrap.nav.Group
6353 * Bootstrap Navbar.NavItem class
6355 * @cfg {String} href link to
6356 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357 * @cfg {Boolean} button_outline show and outlined button
6358 * @cfg {String} html content of button
6359 * @cfg {String} badge text inside badge
6360 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361 * @cfg {String} glyphicon DEPRICATED - use fa
6362 * @cfg {String} icon DEPRICATED - use fa
6363 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364 * @cfg {Boolean} active Is item active
6365 * @cfg {Boolean} disabled Is item disabled
6366 * @cfg {String} linkcls Link Class
6367 * @cfg {Boolean} preventDefault (true | false) default false
6368 * @cfg {String} tabId the tab that this item activates.
6369 * @cfg {String} tagtype (a|span) render as a href or span?
6370 * @cfg {Boolean} animateRef (true|false) link to element default false
6371 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6374 * Create a new Navbar Item
6375 * @param {Object} config The config object
6377 Roo.bootstrap.nav.Item = function(config){
6378 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6383 * The raw click event for the entire grid.
6384 * @param {Roo.EventObject} e
6389 * Fires when the active item active state changes
6390 * @param {Roo.bootstrap.nav.Item} this
6391 * @param {boolean} state the new state
6397 * Fires when scroll to element
6398 * @param {Roo.bootstrap.nav.Item} this
6399 * @param {Object} options
6400 * @param {Roo.EventObject} e
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6417 preventDefault : false,
6425 button_outline : false,
6429 getAutoCreate : function(){
6436 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6439 cfg.cls += ' active' ;
6441 if (this.disabled) {
6442 cfg.cls += ' disabled';
6446 if (this.button_weight.length) {
6447 cfg.tag = this.href ? 'a' : 'button';
6448 cfg.html = this.html || '';
6449 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6451 cfg.href = this.href;
6454 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6456 cfg.cls += " nav-html";
6459 // menu .. should add dropdown-menu class - so no need for carat..
6461 if (this.badge !== '') {
6463 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6472 href : this.href || "#",
6473 html: this.html || '',
6477 if (this.tagtype == 'a') {
6478 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6482 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483 } else if (this.fa) {
6484 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485 } else if(this.glyphicon) {
6486 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6488 cfg.cn[0].cls += " nav-html";
6492 cfg.cn[0].html += " <span class='caret'></span>";
6496 if (this.badge !== '') {
6497 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6505 onRender : function(ct, position)
6507 // Roo.log("Call onRender: " + this.xtype);
6508 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6512 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513 this.navLink = this.el.select('.nav-link',true).first();
6514 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519 initEvents: function()
6521 if (typeof (this.menu) != 'undefined') {
6522 this.menu.parentType = this.xtype;
6523 this.menu.triggerEl = this.el;
6524 this.menu = this.addxtype(Roo.apply({}, this.menu));
6527 this.el.on('click', this.onClick, this);
6529 //if(this.tagtype == 'span'){
6530 // this.el.select('span',true).on('click', this.onClick, this);
6533 // at this point parent should be available..
6534 this.parent().register(this);
6537 onClick : function(e)
6539 if (e.getTarget('.dropdown-menu-item')) {
6540 // did you click on a menu itemm.... - then don't trigger onclick..
6545 this.preventDefault ||
6546 this.href === false ||
6549 //Roo.log("NavItem - prevent Default?");
6553 if (this.disabled) {
6557 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6558 if (tg && tg.transition) {
6559 Roo.log("waiting for the transitionend");
6565 //Roo.log("fire event clicked");
6566 if(this.fireEvent('click', this, e) === false){
6570 if(this.tagtype == 'span'){
6574 //Roo.log(this.href);
6575 var ael = this.el.select('a',true).first();
6578 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6579 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6580 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6581 return; // ignore... - it's a 'hash' to another page.
6583 Roo.log("NavItem - prevent Default?");
6585 this.scrollToElement(e);
6589 var p = this.parent();
6591 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6592 if (typeof(p.setActiveItem) !== 'undefined') {
6593 p.setActiveItem(this);
6597 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6598 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6599 // remove the collapsed menu expand...
6600 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6604 isActive: function () {
6607 setActive : function(state, fire, is_was_active)
6609 if (this.active && !state && this.navId) {
6610 this.was_active = true;
6611 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6613 nv.clearWasActive(this);
6617 this.active = state;
6620 this.el.removeClass('active');
6621 this.navLink ? this.navLink.removeClass('active') : false;
6622 } else if (!this.el.hasClass('active')) {
6624 this.el.addClass('active');
6625 if (Roo.bootstrap.version == 4 && this.navLink ) {
6626 this.navLink.addClass('active');
6631 this.fireEvent('changed', this, state);
6634 // show a panel if it's registered and related..
6636 if (!this.navId || !this.tabId || !state || is_was_active) {
6640 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6644 var pan = tg.getPanelByName(this.tabId);
6648 // if we can not flip to new panel - go back to old nav highlight..
6649 if (false == tg.showPanel(pan)) {
6650 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6652 var onav = nv.getWasActive();
6654 onav.setActive(true, false, true);
6663 // this should not be here...
6664 setDisabled : function(state)
6666 this.disabled = state;
6668 this.el.removeClass('disabled');
6669 } else if (!this.el.hasClass('disabled')) {
6670 this.el.addClass('disabled');
6676 * Fetch the element to display the tooltip on.
6677 * @return {Roo.Element} defaults to this.el
6679 tooltipEl : function()
6681 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6684 scrollToElement : function(e)
6686 var c = document.body;
6689 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6691 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6692 c = document.documentElement;
6695 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6701 var o = target.calcOffsetsTo(c);
6708 this.fireEvent('scrollto', this, options, e);
6710 Roo.get(c).scrollTo('top', options.value, true);
6715 * Set the HTML (text content) of the item
6716 * @param {string} html content for the nav item
6718 setHtml : function(html)
6721 this.htmlEl.dom.innerHTML = html;
6733 * <span> icon </span>
6734 * <span> text </span>
6735 * <span>badge </span>
6739 * @class Roo.bootstrap.nav.SidebarItem
6740 * @extends Roo.bootstrap.nav.Item
6741 * Bootstrap Navbar.NavSidebarItem class
6743 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744 * {Boolean} open is the menu open
6745 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747 * {String} buttonSize (sm|md|lg)the extra classes for the button
6748 * {Boolean} showArrow show arrow next to the text (default true)
6750 * Create a new Navbar Button
6751 * @param {Object} config The config object
6753 Roo.bootstrap.nav.SidebarItem = function(config){
6754 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6759 * The raw click event for the entire grid.
6760 * @param {Roo.EventObject} e
6765 * Fires when the active item active state changes
6766 * @param {Roo.bootstrap.nav.SidebarItem} this
6767 * @param {boolean} state the new state
6775 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6777 badgeWeight : 'default',
6783 buttonWeight : 'default',
6789 getAutoCreate : function(){
6794 href : this.href || '#',
6800 if(this.buttonView){
6803 href : this.href || '#',
6804 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6817 cfg.cls += ' active';
6820 if (this.disabled) {
6821 cfg.cls += ' disabled';
6824 cfg.cls += ' open x-open';
6827 if (this.glyphicon || this.icon) {
6828 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6829 a.cn.push({ tag : 'i', cls : c }) ;
6832 if(!this.buttonView){
6835 html : this.html || ''
6842 if (this.badge !== '') {
6843 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6849 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852 a.cls += ' dropdown-toggle treeview' ;
6858 initEvents : function()
6860 if (typeof (this.menu) != 'undefined') {
6861 this.menu.parentType = this.xtype;
6862 this.menu.triggerEl = this.el;
6863 this.menu = this.addxtype(Roo.apply({}, this.menu));
6866 this.el.on('click', this.onClick, this);
6868 if(this.badge !== ''){
6869 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6874 onClick : function(e)
6881 if(this.preventDefault){
6885 this.fireEvent('click', this, e);
6888 disable : function()
6890 this.setDisabled(true);
6895 this.setDisabled(false);
6898 setDisabled : function(state)
6900 if(this.disabled == state){
6904 this.disabled = state;
6907 this.el.addClass('disabled');
6911 this.el.removeClass('disabled');
6916 setActive : function(state)
6918 if(this.active == state){
6922 this.active = state;
6925 this.el.addClass('active');
6929 this.el.removeClass('active');
6934 isActive: function ()
6939 setBadge : function(str)
6945 this.badgeEl.dom.innerHTML = str;
6962 * @class Roo.bootstrap.nav.ProgressBar
6963 * @extends Roo.bootstrap.Component
6964 * @children Roo.bootstrap.nav.ProgressBarItem
6965 * Bootstrap NavProgressBar class
6968 * Create a new nav progress bar - a bar indicating step along a process
6969 * @param {Object} config The config object
6972 Roo.bootstrap.nav.ProgressBar = function(config){
6973 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6975 this.bullets = this.bullets || [];
6977 // Roo.bootstrap.nav.ProgressBar.register(this);
6981 * Fires when the active item changes
6982 * @param {Roo.bootstrap.nav.ProgressBar} this
6983 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6984 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6991 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6993 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6994 * Bullets for the Nav Progress bar for the toolbar
6999 getAutoCreate : function()
7001 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7005 cls : 'roo-navigation-bar-group',
7009 cls : 'roo-navigation-top-bar'
7013 cls : 'roo-navigation-bullets-bar',
7017 cls : 'roo-navigation-bar'
7024 cls : 'roo-navigation-bottom-bar'
7034 initEvents: function()
7039 onRender : function(ct, position)
7041 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7043 if(this.bullets.length){
7044 Roo.each(this.bullets, function(b){
7053 addItem : function(cfg)
7055 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7057 item.parentId = this.id;
7058 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7061 var top = new Roo.bootstrap.Element({
7063 cls : 'roo-navigation-bar-text'
7066 var bottom = new Roo.bootstrap.Element({
7068 cls : 'roo-navigation-bar-text'
7071 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7072 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7074 var topText = new Roo.bootstrap.Element({
7076 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7079 var bottomText = new Roo.bootstrap.Element({
7081 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7084 topText.onRender(top.el, null);
7085 bottomText.onRender(bottom.el, null);
7088 item.bottomEl = bottom;
7091 this.barItems.push(item);
7096 getActive : function()
7100 Roo.each(this.barItems, function(v){
7102 if (!v.isActive()) {
7114 setActiveItem : function(item)
7118 Roo.each(this.barItems, function(v){
7119 if (v.rid == item.rid) {
7129 item.setActive(true);
7131 this.fireEvent('changed', this, item, prev);
7134 getBarItem: function(rid)
7138 Roo.each(this.barItems, function(e) {
7150 indexOfItem : function(item)
7154 Roo.each(this.barItems, function(v, i){
7156 if (v.rid != item.rid) {
7167 setActiveNext : function()
7169 var i = this.indexOfItem(this.getActive());
7171 if (i > this.barItems.length) {
7175 this.setActiveItem(this.barItems[i+1]);
7178 setActivePrev : function()
7180 var i = this.indexOfItem(this.getActive());
7186 this.setActiveItem(this.barItems[i-1]);
7191 if(!this.barItems.length){
7195 var width = 100 / this.barItems.length;
7197 Roo.each(this.barItems, function(i){
7198 i.el.setStyle('width', width + '%');
7199 i.topEl.el.setStyle('width', width + '%');
7200 i.bottomEl.el.setStyle('width', width + '%');
7214 * @class Roo.bootstrap.nav.ProgressBarItem
7215 * @extends Roo.bootstrap.Component
7216 * Bootstrap NavProgressBarItem class
7217 * @cfg {String} rid the reference id
7218 * @cfg {Boolean} active (true|false) Is item active default false
7219 * @cfg {Boolean} disabled (true|false) Is item active default false
7220 * @cfg {String} html
7221 * @cfg {String} position (top|bottom) text position default bottom
7222 * @cfg {String} icon show icon instead of number
7225 * Create a new NavProgressBarItem
7226 * @param {Object} config The config object
7228 Roo.bootstrap.nav.ProgressBarItem = function(config){
7229 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7234 * The raw click event for the entire grid.
7235 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7236 * @param {Roo.EventObject} e
7243 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7249 position : 'bottom',
7252 getAutoCreate : function()
7254 var iconCls = 'roo-navigation-bar-item-icon';
7256 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7260 cls: 'roo-navigation-bar-item',
7270 cfg.cls += ' active';
7273 cfg.cls += ' disabled';
7279 disable : function()
7281 this.setDisabled(true);
7286 this.setDisabled(false);
7289 initEvents: function()
7291 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7293 this.iconEl.on('click', this.onClick, this);
7296 onClick : function(e)
7304 if(this.fireEvent('click', this, e) === false){
7308 this.parent().setActiveItem(this);
7311 isActive: function ()
7316 setActive : function(state)
7318 if(this.active == state){
7322 this.active = state;
7325 this.el.addClass('active');
7329 this.el.removeClass('active');
7334 setDisabled : function(state)
7336 if(this.disabled == state){
7340 this.disabled = state;
7343 this.el.addClass('disabled');
7347 this.el.removeClass('disabled');
7350 tooltipEl : function()
7352 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363 Roo.namespace('Roo.bootstrap.breadcrumb');
7367 * @class Roo.bootstrap.breadcrumb.Nav
7368 * @extends Roo.bootstrap.Component
7369 * Bootstrap Breadcrumb Nav Class
7371 * @children Roo.bootstrap.breadcrumb.Item
7374 * Create a new breadcrumb.Nav
7375 * @param {Object} config The config object
7379 Roo.bootstrap.breadcrumb.Nav = function(config){
7380 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7385 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7387 getAutoCreate : function()
7404 initEvents: function()
7406 this.olEl = this.el.select('ol',true).first();
7408 getChildContainer : function()
7424 * @class Roo.bootstrap.breadcrumb.Nav
7425 * @extends Roo.bootstrap.Component
7426 * @children Roo.bootstrap.Component
7427 * @parent Roo.bootstrap.breadcrumb.Nav
7428 * Bootstrap Breadcrumb Nav Class
7431 * @cfg {String} html the content of the link.
7432 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7433 * @cfg {Boolean} active is it active
7437 * Create a new breadcrumb.Nav
7438 * @param {Object} config The config object
7441 Roo.bootstrap.breadcrumb.Item = function(config){
7442 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7447 * The img click event for the img.
7448 * @param {Roo.EventObject} e
7455 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7460 getAutoCreate : function()
7465 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7467 if (this.href !== false) {
7474 cfg.html = this.html;
7480 initEvents: function()
7483 this.el.select('a', true).first().on('click',this.onClick, this)
7487 onClick : function(e)
7490 this.fireEvent('click',this, e);
7503 * @class Roo.bootstrap.Row
7504 * @extends Roo.bootstrap.Component
7505 * @children Roo.bootstrap.Component
7506 * Bootstrap Row class (contains columns...)
7510 * @param {Object} config The config object
7513 Roo.bootstrap.Row = function(config){
7514 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7517 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7519 getAutoCreate : function(){
7538 * @class Roo.bootstrap.Pagination
7539 * @extends Roo.bootstrap.Component
7540 * @children Roo.bootstrap.Pagination
7541 * Bootstrap Pagination class
7543 * @cfg {String} size (xs|sm|md|lg|xl)
7544 * @cfg {Boolean} inverse
7547 * Create a new Pagination
7548 * @param {Object} config The config object
7551 Roo.bootstrap.Pagination = function(config){
7552 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7555 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7561 getAutoCreate : function(){
7567 cfg.cls += ' inverse';
7573 cfg.cls += " " + this.cls;
7591 * @class Roo.bootstrap.PaginationItem
7592 * @extends Roo.bootstrap.Component
7593 * Bootstrap PaginationItem class
7594 * @cfg {String} html text
7595 * @cfg {String} href the link
7596 * @cfg {Boolean} preventDefault (true | false) default true
7597 * @cfg {Boolean} active (true | false) default false
7598 * @cfg {Boolean} disabled default false
7602 * Create a new PaginationItem
7603 * @param {Object} config The config object
7607 Roo.bootstrap.PaginationItem = function(config){
7608 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7613 * The raw click event for the entire grid.
7614 * @param {Roo.EventObject} e
7620 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7624 preventDefault: true,
7629 getAutoCreate : function(){
7635 href : this.href ? this.href : '#',
7636 html : this.html ? this.html : ''
7646 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7650 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7656 initEvents: function() {
7658 this.el.on('click', this.onClick, this);
7661 onClick : function(e)
7663 Roo.log('PaginationItem on click ');
7664 if(this.preventDefault){
7672 this.fireEvent('click', this, e);
7688 * @class Roo.bootstrap.Slider
7689 * @extends Roo.bootstrap.Component
7690 * Bootstrap Slider class
7693 * Create a new Slider
7694 * @param {Object} config The config object
7697 Roo.bootstrap.Slider = function(config){
7698 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7701 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7703 getAutoCreate : function(){
7707 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7711 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7723 * Ext JS Library 1.1.1
7724 * Copyright(c) 2006-2007, Ext JS, LLC.
7726 * Originally Released Under LGPL - original licence link has changed is not relivant.
7729 * <script type="text/javascript">
7732 * @extends Roo.dd.DDProxy
7733 * @class Roo.grid.SplitDragZone
7734 * Support for Column Header resizing
7736 * @param {Object} config
7739 // This is a support class used internally by the Grid components
7740 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7742 this.view = grid.getView();
7743 this.proxy = this.view.resizeProxy;
7744 Roo.grid.SplitDragZone.superclass.constructor.call(
7747 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7749 dragElId : Roo.id(this.proxy.dom),
7754 this.setHandleElId(Roo.id(hd));
7755 if (hd2 !== false) {
7756 this.setOuterHandleElId(Roo.id(hd2));
7759 this.scroll = false;
7761 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7762 fly: Roo.Element.fly,
7764 b4StartDrag : function(x, y){
7765 this.view.headersDisabled = true;
7766 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7767 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7769 this.proxy.setHeight(h);
7771 // for old system colWidth really stored the actual width?
7772 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7773 // which in reality did not work.. - it worked only for fixed sizes
7774 // for resizable we need to use actual sizes.
7775 var w = this.cm.getColumnWidth(this.cellIndex);
7776 if (!this.view.mainWrap) {
7778 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7783 // this was w-this.grid.minColumnWidth;
7784 // doesnt really make sense? - w = thie curren width or the rendered one?
7785 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7786 this.resetConstraints();
7787 this.setXConstraint(minw, 1000);
7788 this.setYConstraint(0, 0);
7789 this.minX = x - minw;
7790 this.maxX = x + 1000;
7792 if (!this.view.mainWrap) { // this is Bootstrap code..
7793 this.getDragEl().style.display='block';
7796 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7800 handleMouseDown : function(e){
7801 ev = Roo.EventObject.setEvent(e);
7802 var t = this.fly(ev.getTarget());
7803 if(t.hasClass("x-grid-split")){
7804 this.cellIndex = this.view.getCellIndex(t.dom);
7806 this.cm = this.grid.colModel;
7807 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7808 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7813 endDrag : function(e){
7814 this.view.headersDisabled = false;
7815 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7816 var diff = endX - this.startPos;
7818 var w = this.cm.getColumnWidth(this.cellIndex);
7819 if (!this.view.mainWrap) {
7822 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7825 autoOffset : function(){
7830 * Ext JS Library 1.1.1
7831 * Copyright(c) 2006-2007, Ext JS, LLC.
7833 * Originally Released Under LGPL - original licence link has changed is not relivant.
7836 * <script type="text/javascript">
7840 * @class Roo.grid.AbstractSelectionModel
7841 * @extends Roo.util.Observable
7843 * Abstract base class for grid SelectionModels. It provides the interface that should be
7844 * implemented by descendant classes. This class should not be directly instantiated.
7847 Roo.grid.AbstractSelectionModel = function(){
7848 this.locked = false;
7849 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7852 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7853 /** @ignore Called by the grid automatically. Do not call directly. */
7854 init : function(grid){
7860 * Locks the selections.
7867 * Unlocks the selections.
7869 unlock : function(){
7870 this.locked = false;
7874 * Returns true if the selections are locked.
7877 isLocked : function(){
7882 * Ext JS Library 1.1.1
7883 * Copyright(c) 2006-2007, Ext JS, LLC.
7885 * Originally Released Under LGPL - original licence link has changed is not relivant.
7888 * <script type="text/javascript">
7891 * @extends Roo.grid.AbstractSelectionModel
7892 * @class Roo.grid.RowSelectionModel
7893 * The default SelectionModel used by {@link Roo.grid.Grid}.
7894 * It supports multiple selections and keyboard selection/navigation.
7896 * @param {Object} config
7898 Roo.grid.RowSelectionModel = function(config){
7899 Roo.apply(this, config);
7900 this.selections = new Roo.util.MixedCollection(false, function(o){
7905 this.lastActive = false;
7909 * @event selectionchange
7910 * Fires when the selection changes
7911 * @param {SelectionModel} this
7913 "selectionchange" : true,
7915 * @event afterselectionchange
7916 * Fires after the selection changes (eg. by key press or clicking)
7917 * @param {SelectionModel} this
7919 "afterselectionchange" : true,
7921 * @event beforerowselect
7922 * Fires when a row is selected being selected, return false to cancel.
7923 * @param {SelectionModel} this
7924 * @param {Number} rowIndex The selected index
7925 * @param {Boolean} keepExisting False if other selections will be cleared
7927 "beforerowselect" : true,
7930 * Fires when a row is selected.
7931 * @param {SelectionModel} this
7932 * @param {Number} rowIndex The selected index
7933 * @param {Roo.data.Record} r The record
7937 * @event rowdeselect
7938 * Fires when a row is deselected.
7939 * @param {SelectionModel} this
7940 * @param {Number} rowIndex The selected index
7942 "rowdeselect" : true
7944 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7945 this.locked = false;
7948 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7950 * @cfg {Boolean} singleSelect
7951 * True to allow selection of only one row at a time (defaults to false)
7953 singleSelect : false,
7956 initEvents : function(){
7958 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7959 this.grid.on("mousedown", this.handleMouseDown, this);
7960 }else{ // allow click to work like normal
7961 this.grid.on("rowclick", this.handleDragableRowClick, this);
7963 // bootstrap does not have a view..
7964 var view = this.grid.view ? this.grid.view : this.grid;
7965 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7968 this.selectPrevious(e.shiftKey);
7969 }else if(this.last !== false && this.lastActive !== false){
7970 var last = this.last;
7971 this.selectRange(this.last, this.lastActive-1);
7972 view.focusRow(this.lastActive);
7977 this.selectFirstRow();
7979 this.fireEvent("afterselectionchange", this);
7981 "down" : function(e){
7983 this.selectNext(e.shiftKey);
7984 }else if(this.last !== false && this.lastActive !== false){
7985 var last = this.last;
7986 this.selectRange(this.last, this.lastActive+1);
7987 view.focusRow(this.lastActive);
7992 this.selectFirstRow();
7994 this.fireEvent("afterselectionchange", this);
8000 view.on("refresh", this.onRefresh, this);
8001 view.on("rowupdated", this.onRowUpdated, this);
8002 view.on("rowremoved", this.onRemove, this);
8006 onRefresh : function(){
8007 var ds = this.grid.ds, i, v = this.grid.view;
8008 var s = this.selections;
8010 if((i = ds.indexOfId(r.id)) != -1){
8012 s.add(ds.getAt(i)); // updating the selection relate data
8020 onRemove : function(v, index, r){
8021 this.selections.remove(r);
8025 onRowUpdated : function(v, index, r){
8026 if(this.isSelected(r)){
8027 v.onRowSelect(index);
8033 * @param {Array} records The records to select
8034 * @param {Boolean} keepExisting (optional) True to keep existing selections
8036 selectRecords : function(records, keepExisting){
8038 this.clearSelections();
8040 var ds = this.grid.ds;
8041 for(var i = 0, len = records.length; i < len; i++){
8042 this.selectRow(ds.indexOf(records[i]), true);
8047 * Gets the number of selected rows.
8050 getCount : function(){
8051 return this.selections.length;
8055 * Selects the first row in the grid.
8057 selectFirstRow : function(){
8062 * Select the last row.
8063 * @param {Boolean} keepExisting (optional) True to keep existing selections
8065 selectLastRow : function(keepExisting){
8066 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8070 * Selects the row immediately following the last selected row.
8071 * @param {Boolean} keepExisting (optional) True to keep existing selections
8073 selectNext : function(keepExisting){
8074 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8075 this.selectRow(this.last+1, keepExisting);
8076 var view = this.grid.view ? this.grid.view : this.grid;
8077 view.focusRow(this.last);
8082 * Selects the row that precedes the last selected row.
8083 * @param {Boolean} keepExisting (optional) True to keep existing selections
8085 selectPrevious : function(keepExisting){
8087 this.selectRow(this.last-1, keepExisting);
8088 var view = this.grid.view ? this.grid.view : this.grid;
8089 view.focusRow(this.last);
8094 * Returns the selected records
8095 * @return {Array} Array of selected records
8097 getSelections : function(){
8098 return [].concat(this.selections.items);
8102 * Returns the first selected record.
8105 getSelected : function(){
8106 return this.selections.itemAt(0);
8111 * Clears all selections.
8113 clearSelections : function(fast){
8118 var ds = this.grid.ds;
8119 var s = this.selections;
8121 this.deselectRow(ds.indexOfId(r.id));
8125 this.selections.clear();
8134 selectAll : function(){
8138 this.selections.clear();
8139 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8140 this.selectRow(i, true);
8145 * Returns True if there is a selection.
8148 hasSelection : function(){
8149 return this.selections.length > 0;
8153 * Returns True if the specified row is selected.
8154 * @param {Number/Record} record The record or index of the record to check
8157 isSelected : function(index){
8158 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8159 return (r && this.selections.key(r.id) ? true : false);
8163 * Returns True if the specified record id is selected.
8164 * @param {String} id The id of record to check
8167 isIdSelected : function(id){
8168 return (this.selections.key(id) ? true : false);
8172 handleMouseDown : function(e, t)
8174 var view = this.grid.view ? this.grid.view : this.grid;
8176 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8179 if(e.shiftKey && this.last !== false){
8180 var last = this.last;
8181 this.selectRange(last, rowIndex, e.ctrlKey);
8182 this.last = last; // reset the last
8183 view.focusRow(rowIndex);
8185 var isSelected = this.isSelected(rowIndex);
8186 if(e.button !== 0 && isSelected){
8187 view.focusRow(rowIndex);
8188 }else if(e.ctrlKey && isSelected){
8189 this.deselectRow(rowIndex);
8190 }else if(!isSelected){
8191 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8192 view.focusRow(rowIndex);
8195 this.fireEvent("afterselectionchange", this);
8198 handleDragableRowClick : function(grid, rowIndex, e)
8200 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8201 this.selectRow(rowIndex, false);
8202 var view = this.grid.view ? this.grid.view : this.grid;
8203 view.focusRow(rowIndex);
8204 this.fireEvent("afterselectionchange", this);
8209 * Selects multiple rows.
8210 * @param {Array} rows Array of the indexes of the row to select
8211 * @param {Boolean} keepExisting (optional) True to keep existing selections
8213 selectRows : function(rows, keepExisting){
8215 this.clearSelections();
8217 for(var i = 0, len = rows.length; i < len; i++){
8218 this.selectRow(rows[i], true);
8223 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8224 * @param {Number} startRow The index of the first row in the range
8225 * @param {Number} endRow The index of the last row in the range
8226 * @param {Boolean} keepExisting (optional) True to retain existing selections
8228 selectRange : function(startRow, endRow, keepExisting){
8233 this.clearSelections();
8235 if(startRow <= endRow){
8236 for(var i = startRow; i <= endRow; i++){
8237 this.selectRow(i, true);
8240 for(var i = startRow; i >= endRow; i--){
8241 this.selectRow(i, true);
8247 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8248 * @param {Number} startRow The index of the first row in the range
8249 * @param {Number} endRow The index of the last row in the range
8251 deselectRange : function(startRow, endRow, preventViewNotify){
8255 for(var i = startRow; i <= endRow; i++){
8256 this.deselectRow(i, preventViewNotify);
8262 * @param {Number} row The index of the row to select
8263 * @param {Boolean} keepExisting (optional) True to keep existing selections
8265 selectRow : function(index, keepExisting, preventViewNotify){
8266 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8269 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8270 if(!keepExisting || this.singleSelect){
8271 this.clearSelections();
8273 var r = this.grid.ds.getAt(index);
8274 this.selections.add(r);
8275 this.last = this.lastActive = index;
8276 if(!preventViewNotify){
8277 var view = this.grid.view ? this.grid.view : this.grid;
8278 view.onRowSelect(index);
8280 this.fireEvent("rowselect", this, index, r);
8281 this.fireEvent("selectionchange", this);
8287 * @param {Number} row The index of the row to deselect
8289 deselectRow : function(index, preventViewNotify){
8293 if(this.last == index){
8296 if(this.lastActive == index){
8297 this.lastActive = false;
8299 var r = this.grid.ds.getAt(index);
8300 this.selections.remove(r);
8301 if(!preventViewNotify){
8302 var view = this.grid.view ? this.grid.view : this.grid;
8303 view.onRowDeselect(index);
8305 this.fireEvent("rowdeselect", this, index);
8306 this.fireEvent("selectionchange", this);
8310 restoreLast : function(){
8312 this.last = this._last;
8317 acceptsNav : function(row, col, cm){
8318 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8322 onEditorKey : function(field, e){
8323 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8328 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8330 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8332 }else if(k == e.ENTER && !e.ctrlKey){
8336 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8338 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8340 }else if(k == e.ESC){
8344 g.startEditing(newCell[0], newCell[1]);
8349 * Ext JS Library 1.1.1
8350 * Copyright(c) 2006-2007, Ext JS, LLC.
8352 * Originally Released Under LGPL - original licence link has changed is not relivant.
8355 * <script type="text/javascript">
8360 * @class Roo.grid.ColumnModel
8361 * @extends Roo.util.Observable
8362 * This is the default implementation of a ColumnModel used by the Grid. It defines
8363 * the columns in the grid.
8366 var colModel = new Roo.grid.ColumnModel([
8367 {header: "Ticker", width: 60, sortable: true, locked: true},
8368 {header: "Company Name", width: 150, sortable: true},
8369 {header: "Market Cap.", width: 100, sortable: true},
8370 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8371 {header: "Employees", width: 100, sortable: true, resizable: false}
8376 * The config options listed for this class are options which may appear in each
8377 * individual column definition.
8378 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8380 * @param {Object} config An Array of column config objects. See this class's
8381 * config objects for details.
8383 Roo.grid.ColumnModel = function(config){
8385 * The config passed into the constructor
8387 this.config = []; //config;
8390 // if no id, create one
8391 // if the column does not have a dataIndex mapping,
8392 // map it to the order it is in the config
8393 for(var i = 0, len = config.length; i < len; i++){
8394 this.addColumn(config[i]);
8399 * The width of columns which have no width specified (defaults to 100)
8402 this.defaultWidth = 100;
8405 * Default sortable of columns which have no sortable specified (defaults to false)
8408 this.defaultSortable = false;
8412 * @event widthchange
8413 * Fires when the width of a column changes.
8414 * @param {ColumnModel} this
8415 * @param {Number} columnIndex The column index
8416 * @param {Number} newWidth The new width
8418 "widthchange": true,
8420 * @event headerchange
8421 * Fires when the text of a header changes.
8422 * @param {ColumnModel} this
8423 * @param {Number} columnIndex The column index
8424 * @param {Number} newText The new header text
8426 "headerchange": true,
8428 * @event hiddenchange
8429 * Fires when a column is hidden or "unhidden".
8430 * @param {ColumnModel} this
8431 * @param {Number} columnIndex The column index
8432 * @param {Boolean} hidden true if hidden, false otherwise
8434 "hiddenchange": true,
8436 * @event columnmoved
8437 * Fires when a column is moved.
8438 * @param {ColumnModel} this
8439 * @param {Number} oldIndex
8440 * @param {Number} newIndex
8442 "columnmoved" : true,
8444 * @event columlockchange
8445 * Fires when a column's locked state is changed
8446 * @param {ColumnModel} this
8447 * @param {Number} colIndex
8448 * @param {Boolean} locked true if locked
8450 "columnlockchange" : true
8452 Roo.grid.ColumnModel.superclass.constructor.call(this);
8454 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8456 * @cfg {String} header The header text to display in the Grid view.
8459 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8462 * @cfg {String} smHeader Header at Bootsrap Small width
8465 * @cfg {String} mdHeader Header at Bootsrap Medium width
8468 * @cfg {String} lgHeader Header at Bootsrap Large width
8471 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8474 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8475 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8476 * specified, the column's index is used as an index into the Record's data Array.
8479 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8480 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8483 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8484 * Defaults to the value of the {@link #defaultSortable} property.
8485 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8488 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8491 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8494 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8497 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8500 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8501 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8502 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8503 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8506 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8509 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8512 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8515 * @cfg {String} cursor (Optional)
8518 * @cfg {String} tooltip (Optional)
8521 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8524 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8527 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8530 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8533 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8536 * Returns the id of the column at the specified index.
8537 * @param {Number} index The column index
8538 * @return {String} the id
8540 getColumnId : function(index){
8541 return this.config[index].id;
8545 * Returns the column for a specified id.
8546 * @param {String} id The column id
8547 * @return {Object} the column
8549 getColumnById : function(id){
8550 return this.lookup[id];
8555 * Returns the column Object for a specified dataIndex.
8556 * @param {String} dataIndex The column dataIndex
8557 * @return {Object|Boolean} the column or false if not found
8559 getColumnByDataIndex: function(dataIndex){
8560 var index = this.findColumnIndex(dataIndex);
8561 return index > -1 ? this.config[index] : false;
8565 * Returns the index for a specified column id.
8566 * @param {String} id The column id
8567 * @return {Number} the index, or -1 if not found
8569 getIndexById : function(id){
8570 for(var i = 0, len = this.config.length; i < len; i++){
8571 if(this.config[i].id == id){
8579 * Returns the index for a specified column dataIndex.
8580 * @param {String} dataIndex The column dataIndex
8581 * @return {Number} the index, or -1 if not found
8584 findColumnIndex : function(dataIndex){
8585 for(var i = 0, len = this.config.length; i < len; i++){
8586 if(this.config[i].dataIndex == dataIndex){
8594 moveColumn : function(oldIndex, newIndex){
8595 var c = this.config[oldIndex];
8596 this.config.splice(oldIndex, 1);
8597 this.config.splice(newIndex, 0, c);
8598 this.dataMap = null;
8599 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8602 isLocked : function(colIndex){
8603 return this.config[colIndex].locked === true;
8606 setLocked : function(colIndex, value, suppressEvent){
8607 if(this.isLocked(colIndex) == value){
8610 this.config[colIndex].locked = value;
8612 this.fireEvent("columnlockchange", this, colIndex, value);
8616 getTotalLockedWidth : function(){
8618 for(var i = 0; i < this.config.length; i++){
8619 if(this.isLocked(i) && !this.isHidden(i)){
8620 this.totalWidth += this.getColumnWidth(i);
8626 getLockedCount : function(){
8627 for(var i = 0, len = this.config.length; i < len; i++){
8628 if(!this.isLocked(i)){
8633 return this.config.length;
8637 * Returns the number of columns.
8640 getColumnCount : function(visibleOnly){
8641 if(visibleOnly === true){
8643 for(var i = 0, len = this.config.length; i < len; i++){
8644 if(!this.isHidden(i)){
8650 return this.config.length;
8654 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8655 * @param {Function} fn
8656 * @param {Object} scope (optional)
8657 * @return {Array} result
8659 getColumnsBy : function(fn, scope){
8661 for(var i = 0, len = this.config.length; i < len; i++){
8662 var c = this.config[i];
8663 if(fn.call(scope||this, c, i) === true){
8671 * Returns true if the specified column is sortable.
8672 * @param {Number} col The column index
8675 isSortable : function(col){
8676 if(typeof this.config[col].sortable == "undefined"){
8677 return this.defaultSortable;
8679 return this.config[col].sortable;
8683 * Returns the rendering (formatting) function defined for the column.
8684 * @param {Number} col The column index.
8685 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8687 getRenderer : function(col){
8688 if(!this.config[col].renderer){
8689 return Roo.grid.ColumnModel.defaultRenderer;
8691 return this.config[col].renderer;
8695 * Sets the rendering (formatting) function for a column.
8696 * @param {Number} col The column index
8697 * @param {Function} fn The function to use to process the cell's raw data
8698 * to return HTML markup for the grid view. The render function is called with
8699 * the following parameters:<ul>
8700 * <li>Data value.</li>
8701 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8702 * <li>css A CSS style string to apply to the table cell.</li>
8703 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8704 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8705 * <li>Row index</li>
8706 * <li>Column index</li>
8707 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8709 setRenderer : function(col, fn){
8710 this.config[col].renderer = fn;
8714 * Returns the width for the specified column.
8715 * @param {Number} col The column index
8716 * @param (optional) {String} gridSize bootstrap width size.
8719 getColumnWidth : function(col, gridSize)
8721 var cfg = this.config[col];
8723 if (typeof(gridSize) == 'undefined') {
8724 return cfg.width * 1 || this.defaultWidth;
8726 if (gridSize === false) { // if we set it..
8727 return cfg.width || false;
8729 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8731 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8732 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8735 return cfg[ sizes[i] ];
8742 * Sets the width for a column.
8743 * @param {Number} col The column index
8744 * @param {Number} width The new width
8746 setColumnWidth : function(col, width, suppressEvent){
8747 this.config[col].width = width;
8748 this.totalWidth = null;
8750 this.fireEvent("widthchange", this, col, width);
8755 * Returns the total width of all columns.
8756 * @param {Boolean} includeHidden True to include hidden column widths
8759 getTotalWidth : function(includeHidden){
8760 if(!this.totalWidth){
8761 this.totalWidth = 0;
8762 for(var i = 0, len = this.config.length; i < len; i++){
8763 if(includeHidden || !this.isHidden(i)){
8764 this.totalWidth += this.getColumnWidth(i);
8768 return this.totalWidth;
8772 * Returns the header for the specified column.
8773 * @param {Number} col The column index
8776 getColumnHeader : function(col){
8777 return this.config[col].header;
8781 * Sets the header for a column.
8782 * @param {Number} col The column index
8783 * @param {String} header The new header
8785 setColumnHeader : function(col, header){
8786 this.config[col].header = header;
8787 this.fireEvent("headerchange", this, col, header);
8791 * Returns the tooltip for the specified column.
8792 * @param {Number} col The column index
8795 getColumnTooltip : function(col){
8796 return this.config[col].tooltip;
8799 * Sets the tooltip for a column.
8800 * @param {Number} col The column index
8801 * @param {String} tooltip The new tooltip
8803 setColumnTooltip : function(col, tooltip){
8804 this.config[col].tooltip = tooltip;
8808 * Returns the dataIndex for the specified column.
8809 * @param {Number} col The column index
8812 getDataIndex : function(col){
8813 return this.config[col].dataIndex;
8817 * Sets the dataIndex for a column.
8818 * @param {Number} col The column index
8819 * @param {Number} dataIndex The new dataIndex
8821 setDataIndex : function(col, dataIndex){
8822 this.config[col].dataIndex = dataIndex;
8828 * Returns true if the cell is editable.
8829 * @param {Number} colIndex The column index
8830 * @param {Number} rowIndex The row index - this is nto actually used..?
8833 isCellEditable : function(colIndex, rowIndex){
8834 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8838 * Returns the editor defined for the cell/column.
8839 * return false or null to disable editing.
8840 * @param {Number} colIndex The column index
8841 * @param {Number} rowIndex The row index
8844 getCellEditor : function(colIndex, rowIndex){
8845 return this.config[colIndex].editor;
8849 * Sets if a column is editable.
8850 * @param {Number} col The column index
8851 * @param {Boolean} editable True if the column is editable
8853 setEditable : function(col, editable){
8854 this.config[col].editable = editable;
8859 * Returns true if the column is hidden.
8860 * @param {Number} colIndex The column index
8863 isHidden : function(colIndex){
8864 return this.config[colIndex].hidden;
8869 * Returns true if the column width cannot be changed
8871 isFixed : function(colIndex){
8872 return this.config[colIndex].fixed;
8876 * Returns true if the column can be resized
8879 isResizable : function(colIndex){
8880 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8883 * Sets if a column is hidden.
8884 * @param {Number} colIndex The column index
8885 * @param {Boolean} hidden True if the column is hidden
8887 setHidden : function(colIndex, hidden){
8888 this.config[colIndex].hidden = hidden;
8889 this.totalWidth = null;
8890 this.fireEvent("hiddenchange", this, colIndex, hidden);
8894 * Sets the editor for a column.
8895 * @param {Number} col The column index
8896 * @param {Object} editor The editor object
8898 setEditor : function(col, editor){
8899 this.config[col].editor = editor;
8902 * Add a column (experimental...) - defaults to adding to the end..
8903 * @param {Object} config
8905 addColumn : function(c)
8908 var i = this.config.length;
8911 if(typeof c.dataIndex == "undefined"){
8914 if(typeof c.renderer == "string"){
8915 c.renderer = Roo.util.Format[c.renderer];
8917 if(typeof c.id == "undefined"){
8920 if(c.editor && c.editor.xtype){
8921 c.editor = Roo.factory(c.editor, Roo.grid);
8923 if(c.editor && c.editor.isFormField){
8924 c.editor = new Roo.grid.GridEditor(c.editor);
8926 this.lookup[c.id] = c;
8931 Roo.grid.ColumnModel.defaultRenderer = function(value)
8933 if(typeof value == "object") {
8936 if(typeof value == "string" && value.length < 1){
8940 return String.format("{0}", value);
8943 // Alias for backwards compatibility
8944 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8947 * Ext JS Library 1.1.1
8948 * Copyright(c) 2006-2007, Ext JS, LLC.
8950 * Originally Released Under LGPL - original licence link has changed is not relivant.
8953 * <script type="text/javascript">
8957 * @class Roo.LoadMask
8958 * A simple utility class for generically masking elements while loading data. If the element being masked has
8959 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8960 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8961 * element's UpdateManager load indicator and will be destroyed after the initial load.
8963 * Create a new LoadMask
8964 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8965 * @param {Object} config The config object
8967 Roo.LoadMask = function(el, config){
8968 this.el = Roo.get(el);
8969 Roo.apply(this, config);
8971 this.store.on('beforeload', this.onBeforeLoad, this);
8972 this.store.on('load', this.onLoad, this);
8973 this.store.on('loadexception', this.onLoadException, this);
8974 this.removeMask = false;
8976 var um = this.el.getUpdateManager();
8977 um.showLoadIndicator = false; // disable the default indicator
8978 um.on('beforeupdate', this.onBeforeLoad, this);
8979 um.on('update', this.onLoad, this);
8980 um.on('failure', this.onLoad, this);
8981 this.removeMask = true;
8985 Roo.LoadMask.prototype = {
8987 * @cfg {Boolean} removeMask
8988 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8989 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8994 * The text to display in a centered loading message box (defaults to 'Loading...')
8998 * @cfg {String} msgCls
8999 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9001 msgCls : 'x-mask-loading',
9004 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9010 * Disables the mask to prevent it from being displayed
9012 disable : function(){
9013 this.disabled = true;
9017 * Enables the mask so that it can be displayed
9019 enable : function(){
9020 this.disabled = false;
9023 onLoadException : function()
9027 if (typeof(arguments[3]) != 'undefined') {
9028 Roo.MessageBox.alert("Error loading",arguments[3]);
9032 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9033 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9040 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9049 onBeforeLoad : function(){
9051 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9056 destroy : function(){
9058 this.store.un('beforeload', this.onBeforeLoad, this);
9059 this.store.un('load', this.onLoad, this);
9060 this.store.un('loadexception', this.onLoadException, this);
9062 var um = this.el.getUpdateManager();
9063 um.un('beforeupdate', this.onBeforeLoad, this);
9064 um.un('update', this.onLoad, this);
9065 um.un('failure', this.onLoad, this);
9069 * @class Roo.bootstrap.Table
9071 * @extends Roo.bootstrap.Component
9072 * @children Roo.bootstrap.TableBody
9073 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9074 * Similar to Roo.grid.Grid
9076 var table = Roo.factory({
9078 xns : Roo.bootstrap,
9079 autoSizeColumns: true,
9086 sortInfo : { direction : 'ASC', field: 'name' },
9088 xtype : 'HttpProxy',
9091 url : 'https://example.com/some.data.url.json'
9094 xtype : 'JsonReader',
9096 fields : [ 'id', 'name', whatever' ],
9103 xtype : 'ColumnModel',
9107 dataIndex : 'is_in_group',
9110 renderer : function(v, x , r) {
9112 return String.format("{0}", v)
9118 xtype : 'RowSelectionModel',
9119 xns : Roo.bootstrap.Table
9120 // you can add listeners to catch selection change here....
9126 grid.render(Roo.get("some-div"));
9129 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9134 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9135 * @cfg {Roo.data.Store} store The data store to use
9136 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9138 * @cfg {String} cls table class
9141 * @cfg {string} empty_results Text to display for no results
9142 * @cfg {boolean} striped Should the rows be alternative striped
9143 * @cfg {boolean} bordered Add borders to the table
9144 * @cfg {boolean} hover Add hover highlighting
9145 * @cfg {boolean} condensed Format condensed
9146 * @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,
9147 * also adds table-responsive (see bootstrap docs for details)
9148 * @cfg {Boolean} loadMask (true|false) default false
9149 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9150 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9151 * @cfg {Boolean} rowSelection (true|false) default false
9152 * @cfg {Boolean} cellSelection (true|false) default false
9153 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9154 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9155 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9156 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9157 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9160 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9163 * Create a new Table
9164 * @param {Object} config The config object
9167 Roo.bootstrap.Table = function(config)
9169 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9172 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9173 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9174 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9175 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9177 this.view = this; // compat with grid.
9179 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9181 this.sm.grid = this;
9182 this.selModel = Roo.factory(this.sm, Roo.grid);
9183 this.sm = this.selModel;
9184 this.sm.xmodule = this.xmodule || false;
9187 if (this.cm && typeof(this.cm.config) == 'undefined') {
9188 this.colModel = new Roo.grid.ColumnModel(this.cm);
9189 this.cm = this.colModel;
9190 this.cm.xmodule = this.xmodule || false;
9193 this.store= Roo.factory(this.store, Roo.data);
9194 this.ds = this.store;
9195 this.ds.xmodule = this.xmodule || false;
9198 if (this.footer && this.store) {
9199 this.footer.dataSource = this.ds;
9200 this.footer = Roo.factory(this.footer);
9207 * Fires when a cell is clicked
9208 * @param {Roo.bootstrap.Table} this
9209 * @param {Roo.Element} el
9210 * @param {Number} rowIndex
9211 * @param {Number} columnIndex
9212 * @param {Roo.EventObject} e
9216 * @event celldblclick
9217 * Fires when a cell is double clicked
9218 * @param {Roo.bootstrap.Table} this
9219 * @param {Roo.Element} el
9220 * @param {Number} rowIndex
9221 * @param {Number} columnIndex
9222 * @param {Roo.EventObject} e
9224 "celldblclick" : true,
9227 * Fires when a row is clicked
9228 * @param {Roo.bootstrap.Table} this
9229 * @param {Roo.Element} el
9230 * @param {Number} rowIndex
9231 * @param {Roo.EventObject} e
9235 * @event rowdblclick
9236 * Fires when a row is double clicked
9237 * @param {Roo.bootstrap.Table} this
9238 * @param {Roo.Element} el
9239 * @param {Number} rowIndex
9240 * @param {Roo.EventObject} e
9242 "rowdblclick" : true,
9245 * Fires when a mouseover occur
9246 * @param {Roo.bootstrap.Table} this
9247 * @param {Roo.Element} el
9248 * @param {Number} rowIndex
9249 * @param {Number} columnIndex
9250 * @param {Roo.EventObject} e
9255 * Fires when a mouseout occur
9256 * @param {Roo.bootstrap.Table} this
9257 * @param {Roo.Element} el
9258 * @param {Number} rowIndex
9259 * @param {Number} columnIndex
9260 * @param {Roo.EventObject} e
9265 * Fires when a row is rendered, so you can change add a style to it.
9266 * @param {Roo.bootstrap.Table} this
9267 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9271 * @event rowsrendered
9272 * Fires when all the rows have been rendered
9273 * @param {Roo.bootstrap.Table} this
9275 'rowsrendered' : true,
9277 * @event contextmenu
9278 * The raw contextmenu event for the entire grid.
9279 * @param {Roo.EventObject} e
9281 "contextmenu" : true,
9283 * @event rowcontextmenu
9284 * Fires when a row is right clicked
9285 * @param {Roo.bootstrap.Table} this
9286 * @param {Number} rowIndex
9287 * @param {Roo.EventObject} e
9289 "rowcontextmenu" : true,
9291 * @event cellcontextmenu
9292 * Fires when a cell is right clicked
9293 * @param {Roo.bootstrap.Table} this
9294 * @param {Number} rowIndex
9295 * @param {Number} cellIndex
9296 * @param {Roo.EventObject} e
9298 "cellcontextmenu" : true,
9300 * @event headercontextmenu
9301 * Fires when a header is right clicked
9302 * @param {Roo.bootstrap.Table} this
9303 * @param {Number} columnIndex
9304 * @param {Roo.EventObject} e
9306 "headercontextmenu" : true,
9309 * The raw mousedown event for the entire grid.
9310 * @param {Roo.EventObject} e
9317 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9334 enableColumnResize: true,
9336 rowSelection : false,
9337 cellSelection : false,
9340 minColumnWidth : 50,
9342 // Roo.Element - the tbody
9343 bodyEl: false, // <tbody> Roo.Element - thead element
9344 headEl: false, // <thead> Roo.Element - thead element
9345 resizeProxy : false, // proxy element for dragging?
9349 container: false, // used by gridpanel...
9355 auto_hide_footer : false,
9357 view: false, // actually points to this..
9359 getAutoCreate : function()
9361 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9368 // this get's auto added by panel.Grid
9369 if (this.scrollBody) {
9370 cfg.cls += ' table-body-fixed';
9373 cfg.cls += ' table-striped';
9377 cfg.cls += ' table-hover';
9379 if (this.bordered) {
9380 cfg.cls += ' table-bordered';
9382 if (this.condensed) {
9383 cfg.cls += ' table-condensed';
9386 if (this.responsive) {
9387 cfg.cls += ' table-responsive';
9391 cfg.cls+= ' ' +this.cls;
9397 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9400 if(this.store || this.cm){
9401 if(this.headerShow){
9402 cfg.cn.push(this.renderHeader());
9405 cfg.cn.push(this.renderBody());
9407 if(this.footerShow){
9408 cfg.cn.push(this.renderFooter());
9410 // where does this come from?
9411 //cfg.cls+= ' TableGrid';
9414 return { cn : [ cfg ] };
9417 initEvents : function()
9419 if(!this.store || !this.cm){
9422 if (this.selModel) {
9423 this.selModel.initEvents();
9427 //Roo.log('initEvents with ds!!!!');
9429 this.bodyEl = this.el.select('tbody', true).first();
9430 this.headEl = this.el.select('thead', true).first();
9431 this.mainFoot = this.el.select('tfoot', true).first();
9436 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9437 e.on('click', this.sort, this);
9441 // why is this done????? = it breaks dialogs??
9442 //this.parent().el.setStyle('position', 'relative');
9446 this.footer.parentId = this.id;
9447 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9450 this.el.select('tfoot tr td').first().addClass('hide');
9455 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9458 this.store.on('load', this.onLoad, this);
9459 this.store.on('beforeload', this.onBeforeLoad, this);
9460 this.store.on('update', this.onUpdate, this);
9461 this.store.on('add', this.onAdd, this);
9462 this.store.on("clear", this.clear, this);
9464 this.el.on("contextmenu", this.onContextMenu, this);
9467 this.cm.on("headerchange", this.onHeaderChange, this);
9468 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9470 //?? does bodyEl get replaced on render?
9471 this.bodyEl.on("click", this.onClick, this);
9472 this.bodyEl.on("dblclick", this.onDblClick, this);
9473 this.bodyEl.on('scroll', this.onBodyScroll, this);
9475 // guessing mainbody will work - this relays usually caught by selmodel at present.
9476 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9479 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9482 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9483 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9488 // Compatibility with grid - we implement all the view features at present.
9489 getView : function()
9494 initCSS : function()
9498 var cm = this.cm, styles = [];
9499 this.CSS.removeStyleSheet(this.id + '-cssrules');
9500 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9501 // we can honour xs/sm/md/xl as widths...
9502 // we first have to decide what widht we are currently at...
9503 var sz = Roo.getGridSize();
9507 var cols = []; // visable cols.
9509 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9510 var w = cm.getColumnWidth(i, false);
9512 cols.push( { rel : false, abs : 0 });
9516 cols.push( { rel : false, abs : w });
9518 last = i; // not really..
9521 var w = cm.getColumnWidth(i, sz);
9526 cols.push( { rel : w, abs : false });
9529 var avail = this.bodyEl.dom.clientWidth - total_abs;
9531 var unitWidth = Math.floor(avail / total);
9532 var rem = avail - (unitWidth * total);
9534 var hidden, width, pos = 0 , splithide , left;
9535 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9537 hidden = 'display:none;';
9539 width = 'width:0px;';
9541 if(!cm.isHidden(i)){
9545 // we can honour xs/sm/md/xl ?
9546 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9548 hidden = 'display:none;';
9550 // width should return a small number...
9552 w+=rem; // add the remaining with..
9555 left = "left:" + (pos -4) + "px;";
9556 width = "width:" + w+ "px;";
9559 if (this.responsive) {
9562 hidden = cm.isHidden(i) ? 'display:none;' : '';
9563 splithide = 'display: none;';
9566 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9569 splithide = 'display:none;';
9572 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9573 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9574 // this is the popover version..
9575 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9580 //Roo.log(styles.join(''));
9581 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9587 onContextMenu : function(e, t)
9589 this.processEvent("contextmenu", e);
9592 processEvent : function(name, e)
9594 if (name != 'touchstart' ) {
9595 this.fireEvent(name, e);
9598 var t = e.getTarget();
9600 var cell = Roo.get(t);
9606 if(cell.findParent('tfoot', false, true)){
9610 if(cell.findParent('thead', false, true)){
9612 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9613 cell = Roo.get(t).findParent('th', false, true);
9615 Roo.log("failed to find th in thead?");
9616 Roo.log(e.getTarget());
9621 var cellIndex = cell.dom.cellIndex;
9623 var ename = name == 'touchstart' ? 'click' : name;
9624 this.fireEvent("header" + ename, this, cellIndex, e);
9629 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9630 cell = Roo.get(t).findParent('td', false, true);
9632 Roo.log("failed to find th in tbody?");
9633 Roo.log(e.getTarget());
9638 var row = cell.findParent('tr', false, true);
9639 var cellIndex = cell.dom.cellIndex;
9640 var rowIndex = row.dom.rowIndex - 1;
9644 this.fireEvent("row" + name, this, rowIndex, e);
9648 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9654 onMouseover : function(e, el)
9656 var cell = Roo.get(el);
9662 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9663 cell = cell.findParent('td', false, true);
9666 var row = cell.findParent('tr', false, true);
9667 var cellIndex = cell.dom.cellIndex;
9668 var rowIndex = row.dom.rowIndex - 1; // start from 0
9670 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9674 onMouseout : function(e, el)
9676 var cell = Roo.get(el);
9682 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9683 cell = cell.findParent('td', false, true);
9686 var row = cell.findParent('tr', false, true);
9687 var cellIndex = cell.dom.cellIndex;
9688 var rowIndex = row.dom.rowIndex - 1; // start from 0
9690 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9694 onClick : function(e, el)
9696 var cell = Roo.get(el);
9698 if(!cell || (!this.cellSelection && !this.rowSelection)){
9702 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9703 cell = cell.findParent('td', false, true);
9706 if(!cell || typeof(cell) == 'undefined'){
9710 var row = cell.findParent('tr', false, true);
9712 if(!row || typeof(row) == 'undefined'){
9716 var cellIndex = cell.dom.cellIndex;
9717 var rowIndex = this.getRowIndex(row);
9719 // why??? - should these not be based on SelectionModel?
9720 //if(this.cellSelection){
9721 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9724 //if(this.rowSelection){
9725 this.fireEvent('rowclick', this, row, rowIndex, e);
9730 onDblClick : function(e,el)
9732 var cell = Roo.get(el);
9734 if(!cell || (!this.cellSelection && !this.rowSelection)){
9738 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9739 cell = cell.findParent('td', false, true);
9742 if(!cell || typeof(cell) == 'undefined'){
9746 var row = cell.findParent('tr', false, true);
9748 if(!row || typeof(row) == 'undefined'){
9752 var cellIndex = cell.dom.cellIndex;
9753 var rowIndex = this.getRowIndex(row);
9755 if(this.cellSelection){
9756 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9759 if(this.rowSelection){
9760 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9763 findRowIndex : function(el)
9765 var cell = Roo.get(el);
9769 var row = cell.findParent('tr', false, true);
9771 if(!row || typeof(row) == 'undefined'){
9774 return this.getRowIndex(row);
9776 sort : function(e,el)
9778 var col = Roo.get(el);
9780 if(!col.hasClass('sortable')){
9784 var sort = col.attr('sort');
9787 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9791 this.store.sortInfo = {field : sort, direction : dir};
9794 Roo.log("calling footer first");
9795 this.footer.onClick('first');
9798 this.store.load({ params : { start : 0 } });
9802 renderHeader : function()
9810 this.totalWidth = 0;
9812 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9814 var config = cm.config[i];
9818 cls : 'x-hcol-' + i,
9821 html: cm.getColumnHeader(i)
9824 var tooltip = cm.getColumnTooltip(i);
9826 c.tooltip = tooltip;
9832 if(typeof(config.sortable) != 'undefined' && config.sortable){
9833 c.cls += ' sortable';
9834 c.html = '<i class="fa"></i>' + c.html;
9837 // could use BS4 hidden-..-down
9839 if(typeof(config.lgHeader) != 'undefined'){
9840 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9843 if(typeof(config.mdHeader) != 'undefined'){
9844 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9847 if(typeof(config.smHeader) != 'undefined'){
9848 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9851 if(typeof(config.xsHeader) != 'undefined'){
9852 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9859 if(typeof(config.tooltip) != 'undefined'){
9860 c.tooltip = config.tooltip;
9863 if(typeof(config.colspan) != 'undefined'){
9864 c.colspan = config.colspan;
9867 // hidden is handled by CSS now
9869 if(typeof(config.dataIndex) != 'undefined'){
9870 c.sort = config.dataIndex;
9875 if(typeof(config.align) != 'undefined' && config.align.length){
9876 c.style += ' text-align:' + config.align + ';';
9879 /* width is done in CSS
9880 *if(typeof(config.width) != 'undefined'){
9881 c.style += ' width:' + config.width + 'px;';
9882 this.totalWidth += config.width;
9884 this.totalWidth += 100; // assume minimum of 100 per column?
9888 if(typeof(config.cls) != 'undefined'){
9889 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9891 // this is the bit that doesnt reall work at all...
9893 if (this.responsive) {
9896 ['xs','sm','md','lg'].map(function(size){
9898 if(typeof(config[size]) == 'undefined'){
9902 if (!config[size]) { // 0 = hidden
9903 // BS 4 '0' is treated as hide that column and below.
9904 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9908 c.cls += ' col-' + size + '-' + config[size] + (
9909 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9917 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9928 renderBody : function()
9938 colspan : this.cm.getColumnCount()
9948 renderFooter : function()
9958 colspan : this.cm.getColumnCount()
9972 // Roo.log('ds onload');
9977 var ds = this.store;
9979 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9980 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9981 if (_this.store.sortInfo) {
9983 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9984 e.select('i', true).addClass(['fa-arrow-up']);
9987 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9988 e.select('i', true).addClass(['fa-arrow-down']);
9993 var tbody = this.bodyEl;
9995 if(ds.getCount() > 0){
9996 ds.data.each(function(d,rowIndex){
9997 var row = this.renderRow(cm, ds, rowIndex);
9999 tbody.createChild(row);
10003 if(row.cellObjects.length){
10004 Roo.each(row.cellObjects, function(r){
10005 _this.renderCellObject(r);
10010 } else if (this.empty_results.length) {
10011 this.el.mask(this.empty_results, 'no-spinner');
10014 var tfoot = this.el.select('tfoot', true).first();
10016 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10018 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10020 var total = this.ds.getTotalCount();
10022 if(this.footer.pageSize < total){
10023 this.mainFoot.show();
10027 Roo.each(this.el.select('tbody td', true).elements, function(e){
10028 e.on('mouseover', _this.onMouseover, _this);
10031 Roo.each(this.el.select('tbody td', true).elements, function(e){
10032 e.on('mouseout', _this.onMouseout, _this);
10034 this.fireEvent('rowsrendered', this);
10038 this.initCSS(); /// resize cols
10044 onUpdate : function(ds,record)
10046 this.refreshRow(record);
10050 onRemove : function(ds, record, index, isUpdate){
10051 if(isUpdate !== true){
10052 this.fireEvent("beforerowremoved", this, index, record);
10054 var bt = this.bodyEl.dom;
10056 var rows = this.el.select('tbody > tr', true).elements;
10058 if(typeof(rows[index]) != 'undefined'){
10059 bt.removeChild(rows[index].dom);
10062 // if(bt.rows[index]){
10063 // bt.removeChild(bt.rows[index]);
10066 if(isUpdate !== true){
10067 //this.stripeRows(index);
10068 //this.syncRowHeights(index, index);
10070 this.fireEvent("rowremoved", this, index, record);
10074 onAdd : function(ds, records, rowIndex)
10076 //Roo.log('on Add called');
10077 // - note this does not handle multiple adding very well..
10078 var bt = this.bodyEl.dom;
10079 for (var i =0 ; i < records.length;i++) {
10080 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10081 //Roo.log(records[i]);
10082 //Roo.log(this.store.getAt(rowIndex+i));
10083 this.insertRow(this.store, rowIndex + i, false);
10090 refreshRow : function(record){
10091 var ds = this.store, index;
10092 if(typeof record == 'number'){
10094 record = ds.getAt(index);
10096 index = ds.indexOf(record);
10098 return; // should not happen - but seems to
10101 this.insertRow(ds, index, true);
10103 this.onRemove(ds, record, index+1, true);
10105 //this.syncRowHeights(index, index);
10107 this.fireEvent("rowupdated", this, index, record);
10109 // private - called by RowSelection
10110 onRowSelect : function(rowIndex){
10111 var row = this.getRowDom(rowIndex);
10112 row.addClass(['bg-info','info']);
10114 // private - called by RowSelection
10115 onRowDeselect : function(rowIndex)
10117 if (rowIndex < 0) {
10120 var row = this.getRowDom(rowIndex);
10121 row.removeClass(['bg-info','info']);
10124 * Focuses the specified row.
10125 * @param {Number} row The row index
10127 focusRow : function(row)
10129 //Roo.log('GridView.focusRow');
10130 var x = this.bodyEl.dom.scrollLeft;
10131 this.focusCell(row, 0, false);
10132 this.bodyEl.dom.scrollLeft = x;
10136 * Focuses the specified cell.
10137 * @param {Number} row The row index
10138 * @param {Number} col The column index
10139 * @param {Boolean} hscroll false to disable horizontal scrolling
10141 focusCell : function(row, col, hscroll)
10143 //Roo.log('GridView.focusCell');
10144 var el = this.ensureVisible(row, col, hscroll);
10145 // not sure what focusEL achives = it's a <a> pos relative
10146 //this.focusEl.alignTo(el, "tl-tl");
10148 // this.focusEl.focus();
10150 // this.focusEl.focus.defer(1, this.focusEl);
10155 * Scrolls the specified cell into view
10156 * @param {Number} row The row index
10157 * @param {Number} col The column index
10158 * @param {Boolean} hscroll false to disable horizontal scrolling
10160 ensureVisible : function(row, col, hscroll)
10162 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10163 //return null; //disable for testing.
10164 if(typeof row != "number"){
10165 row = row.rowIndex;
10167 if(row < 0 && row >= this.ds.getCount()){
10170 col = (col !== undefined ? col : 0);
10172 while(cm.isHidden(col)){
10176 var el = this.getCellDom(row, col);
10180 var c = this.bodyEl.dom;
10182 var ctop = parseInt(el.offsetTop, 10);
10183 var cleft = parseInt(el.offsetLeft, 10);
10184 var cbot = ctop + el.offsetHeight;
10185 var cright = cleft + el.offsetWidth;
10187 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10188 var ch = 0; //?? header is not withing the area?
10189 var stop = parseInt(c.scrollTop, 10);
10190 var sleft = parseInt(c.scrollLeft, 10);
10191 var sbot = stop + ch;
10192 var sright = sleft + c.clientWidth;
10194 Roo.log('GridView.ensureVisible:' +
10196 ' c.clientHeight:' + c.clientHeight +
10197 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10205 c.scrollTop = ctop;
10206 //Roo.log("set scrolltop to ctop DISABLE?");
10207 }else if(cbot > sbot){
10208 //Roo.log("set scrolltop to cbot-ch");
10209 c.scrollTop = cbot-ch;
10212 if(hscroll !== false){
10214 c.scrollLeft = cleft;
10215 }else if(cright > sright){
10216 c.scrollLeft = cright-c.clientWidth;
10224 insertRow : function(dm, rowIndex, isUpdate){
10227 this.fireEvent("beforerowsinserted", this, rowIndex);
10229 //var s = this.getScrollState();
10230 var row = this.renderRow(this.cm, this.store, rowIndex);
10231 // insert before rowIndex..
10232 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10236 if(row.cellObjects.length){
10237 Roo.each(row.cellObjects, function(r){
10238 _this.renderCellObject(r);
10243 this.fireEvent("rowsinserted", this, rowIndex);
10244 //this.syncRowHeights(firstRow, lastRow);
10245 //this.stripeRows(firstRow);
10252 getRowDom : function(rowIndex)
10254 var rows = this.el.select('tbody > tr', true).elements;
10256 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10259 getCellDom : function(rowIndex, colIndex)
10261 var row = this.getRowDom(rowIndex);
10262 if (row === false) {
10265 var cols = row.select('td', true).elements;
10266 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10270 // returns the object tree for a tr..
10273 renderRow : function(cm, ds, rowIndex)
10275 var d = ds.getAt(rowIndex);
10279 cls : 'x-row-' + rowIndex,
10283 var cellObjects = [];
10285 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10286 var config = cm.config[i];
10288 var renderer = cm.getRenderer(i);
10292 if(typeof(renderer) !== 'undefined'){
10293 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10295 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10296 // and are rendered into the cells after the row is rendered - using the id for the element.
10298 if(typeof(value) === 'object'){
10308 rowIndex : rowIndex,
10313 this.fireEvent('rowclass', this, rowcfg);
10317 // this might end up displaying HTML?
10318 // this is too messy... - better to only do it on columsn you know are going to be too long
10319 //tooltip : (typeof(value) === 'object') ? '' : value,
10320 cls : rowcfg.rowClass + ' x-col-' + i,
10322 html: (typeof(value) === 'object') ? '' : value
10329 if(typeof(config.colspan) != 'undefined'){
10330 td.colspan = config.colspan;
10335 if(typeof(config.align) != 'undefined' && config.align.length){
10336 td.style += ' text-align:' + config.align + ';';
10338 if(typeof(config.valign) != 'undefined' && config.valign.length){
10339 td.style += ' vertical-align:' + config.valign + ';';
10342 if(typeof(config.width) != 'undefined'){
10343 td.style += ' width:' + config.width + 'px;';
10347 if(typeof(config.cursor) != 'undefined'){
10348 td.style += ' cursor:' + config.cursor + ';';
10351 if(typeof(config.cls) != 'undefined'){
10352 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10354 if (this.responsive) {
10355 ['xs','sm','md','lg'].map(function(size){
10357 if(typeof(config[size]) == 'undefined'){
10363 if (!config[size]) { // 0 = hidden
10364 // BS 4 '0' is treated as hide that column and below.
10365 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10369 td.cls += ' col-' + size + '-' + config[size] + (
10370 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10380 row.cellObjects = cellObjects;
10388 onBeforeLoad : function()
10390 this.el.unmask(); // if needed.
10397 this.el.select('tbody', true).first().dom.innerHTML = '';
10400 * Show or hide a row.
10401 * @param {Number} rowIndex to show or hide
10402 * @param {Boolean} state hide
10404 setRowVisibility : function(rowIndex, state)
10406 var bt = this.bodyEl.dom;
10408 var rows = this.el.select('tbody > tr', true).elements;
10410 if(typeof(rows[rowIndex]) == 'undefined'){
10413 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10418 getSelectionModel : function(){
10419 if(!this.selModel){
10420 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10422 return this.selModel;
10425 * Render the Roo.bootstrap object from renderder
10427 renderCellObject : function(r)
10431 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10433 var t = r.cfg.render(r.container);
10436 Roo.each(r.cfg.cn, function(c){
10438 container: t.getChildContainer(),
10441 _this.renderCellObject(child);
10446 * get the Row Index from a dom element.
10447 * @param {Roo.Element} row The row to look for
10448 * @returns {Number} the row
10450 getRowIndex : function(row)
10454 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10465 * get the header TH element for columnIndex
10466 * @param {Number} columnIndex
10467 * @returns {Roo.Element}
10469 getHeaderIndex: function(colIndex)
10471 var cols = this.headEl.select('th', true).elements;
10472 return cols[colIndex];
10475 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10476 * @param {domElement} cell to look for
10477 * @returns {Number} the column
10479 getCellIndex : function(cell)
10481 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10483 return parseInt(id[1], 10);
10488 * Returns the grid's underlying element = used by panel.Grid
10489 * @return {Element} The element
10491 getGridEl : function(){
10495 * Forces a resize - used by panel.Grid
10496 * @return {Element} The element
10498 autoSize : function()
10500 //var ctr = Roo.get(this.container.dom.parentElement);
10501 var ctr = Roo.get(this.el.dom);
10503 var thd = this.getGridEl().select('thead',true).first();
10504 var tbd = this.getGridEl().select('tbody', true).first();
10505 var tfd = this.getGridEl().select('tfoot', true).first();
10507 var cw = ctr.getWidth();
10508 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10512 tbd.setWidth(ctr.getWidth());
10513 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10514 // this needs fixing for various usage - currently only hydra job advers I think..
10516 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10518 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10521 cw = Math.max(cw, this.totalWidth);
10522 this.getGridEl().select('tbody tr',true).setWidth(cw);
10525 // resize 'expandable coloumn?
10527 return; // we doe not have a view in this design..
10530 onBodyScroll: function()
10532 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10534 this.headEl.setStyle({
10535 'position' : 'relative',
10536 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10542 var scrollHeight = this.bodyEl.dom.scrollHeight;
10544 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10546 var height = this.bodyEl.getHeight();
10548 if(scrollHeight - height == scrollTop) {
10550 var total = this.ds.getTotalCount();
10552 if(this.footer.cursor + this.footer.pageSize < total){
10554 this.footer.ds.load({
10556 start : this.footer.cursor + this.footer.pageSize,
10557 limit : this.footer.pageSize
10566 onColumnSplitterMoved : function(i, diff)
10568 this.userResized = true;
10570 var cm = this.colModel;
10572 var w = this.getHeaderIndex(i).getWidth() + diff;
10575 cm.setColumnWidth(i, w, true);
10577 //var cid = cm.getColumnId(i); << not used in this version?
10578 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10580 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10581 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10582 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10584 //this.updateSplitters();
10585 //this.layout(); << ??
10586 this.fireEvent("columnresize", i, w);
10588 onHeaderChange : function()
10590 var header = this.renderHeader();
10591 var table = this.el.select('table', true).first();
10593 this.headEl.remove();
10594 this.headEl = table.createChild(header, this.bodyEl, false);
10596 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10597 e.on('click', this.sort, this);
10600 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10601 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10606 onHiddenChange : function(colModel, colIndex, hidden)
10609 this.cm.setHidden()
10610 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10611 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10613 this.CSS.updateRule(thSelector, "display", "");
10614 this.CSS.updateRule(tdSelector, "display", "");
10617 this.CSS.updateRule(thSelector, "display", "none");
10618 this.CSS.updateRule(tdSelector, "display", "none");
10621 // onload calls initCSS()
10622 this.onHeaderChange();
10626 setColumnWidth: function(col_index, width)
10628 // width = "md-2 xs-2..."
10629 if(!this.colModel.config[col_index]) {
10633 var w = width.split(" ");
10635 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10637 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10640 for(var j = 0; j < w.length; j++) {
10646 var size_cls = w[j].split("-");
10648 if(!Number.isInteger(size_cls[1] * 1)) {
10652 if(!this.colModel.config[col_index][size_cls[0]]) {
10656 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10660 h_row[0].classList.replace(
10661 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10662 "col-"+size_cls[0]+"-"+size_cls[1]
10665 for(var i = 0; i < rows.length; i++) {
10667 var size_cls = w[j].split("-");
10669 if(!Number.isInteger(size_cls[1] * 1)) {
10673 if(!this.colModel.config[col_index][size_cls[0]]) {
10677 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10681 rows[i].classList.replace(
10682 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10683 "col-"+size_cls[0]+"-"+size_cls[1]
10687 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10692 // currently only used to find the split on drag..
10693 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10698 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10699 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10708 * @class Roo.bootstrap.TableCell
10709 * @extends Roo.bootstrap.Component
10710 * @children Roo.bootstrap.Component
10711 * @parent Roo.bootstrap.TableRow
10712 * Bootstrap TableCell class
10714 * @cfg {String} html cell contain text
10715 * @cfg {String} cls cell class
10716 * @cfg {String} tag cell tag (td|th) default td
10717 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10718 * @cfg {String} align Aligns the content in a cell
10719 * @cfg {String} axis Categorizes cells
10720 * @cfg {String} bgcolor Specifies the background color of a cell
10721 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10722 * @cfg {Number} colspan Specifies the number of columns a cell should span
10723 * @cfg {String} headers Specifies one or more header cells a cell is related to
10724 * @cfg {Number} height Sets the height of a cell
10725 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10726 * @cfg {Number} rowspan Sets the number of rows a cell should span
10727 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10728 * @cfg {String} valign Vertical aligns the content in a cell
10729 * @cfg {Number} width Specifies the width of a cell
10732 * Create a new TableCell
10733 * @param {Object} config The config object
10736 Roo.bootstrap.TableCell = function(config){
10737 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10740 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10760 getAutoCreate : function(){
10761 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10768 cfg.tag = this.tag;
10781 cfg.align=this.align
10786 if (this.bgcolor) {
10787 cfg.bgcolor=this.bgcolor
10789 if (this.charoff) {
10790 cfg.charoff=this.charoff
10792 if (this.colspan) {
10793 cfg.colspan=this.colspan
10795 if (this.headers) {
10796 cfg.headers=this.headers
10799 cfg.height=this.height
10802 cfg.nowrap=this.nowrap
10804 if (this.rowspan) {
10805 cfg.rowspan=this.rowspan
10808 cfg.scope=this.scope
10811 cfg.valign=this.valign
10814 cfg.width=this.width
10833 * @class Roo.bootstrap.TableRow
10834 * @extends Roo.bootstrap.Component
10835 * @children Roo.bootstrap.TableCell
10836 * @parent Roo.bootstrap.TableBody
10837 * Bootstrap TableRow class
10838 * @cfg {String} cls row class
10839 * @cfg {String} align Aligns the content in a table row
10840 * @cfg {String} bgcolor Specifies a background color for a table row
10841 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10842 * @cfg {String} valign Vertical aligns the content in a table row
10845 * Create a new TableRow
10846 * @param {Object} config The config object
10849 Roo.bootstrap.TableRow = function(config){
10850 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10853 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10861 getAutoCreate : function(){
10862 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10869 cfg.cls = this.cls;
10872 cfg.align = this.align;
10875 cfg.bgcolor = this.bgcolor;
10878 cfg.charoff = this.charoff;
10881 cfg.valign = this.valign;
10899 * @class Roo.bootstrap.TableBody
10900 * @extends Roo.bootstrap.Component
10901 * @children Roo.bootstrap.TableRow
10902 * @parent Roo.bootstrap.Table
10903 * Bootstrap TableBody class
10904 * @cfg {String} cls element class
10905 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10906 * @cfg {String} align Aligns the content inside the element
10907 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10908 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10911 * Create a new TableBody
10912 * @param {Object} config The config object
10915 Roo.bootstrap.TableBody = function(config){
10916 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10919 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10927 getAutoCreate : function(){
10928 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10938 cfg.tag = this.tag;
10942 cfg.align = this.align;
10945 cfg.charoff = this.charoff;
10948 cfg.valign = this.valign;
10955 // initEvents : function()
10958 // if(!this.store){
10962 // this.store = Roo.factory(this.store, Roo.data);
10963 // this.store.on('load', this.onLoad, this);
10965 // this.store.load();
10969 // onLoad: function ()
10971 // this.fireEvent('load', this);
10981 * Ext JS Library 1.1.1
10982 * Copyright(c) 2006-2007, Ext JS, LLC.
10984 * Originally Released Under LGPL - original licence link has changed is not relivant.
10987 * <script type="text/javascript">
10990 // as we use this in bootstrap.
10991 Roo.namespace('Roo.form');
10993 * @class Roo.form.Action
10994 * Internal Class used to handle form actions
10996 * @param {Roo.form.BasicForm} el The form element or its id
10997 * @param {Object} config Configuration options
11002 // define the action interface
11003 Roo.form.Action = function(form, options){
11005 this.options = options || {};
11008 * Client Validation Failed
11011 Roo.form.Action.CLIENT_INVALID = 'client';
11013 * Server Validation Failed
11016 Roo.form.Action.SERVER_INVALID = 'server';
11018 * Connect to Server Failed
11021 Roo.form.Action.CONNECT_FAILURE = 'connect';
11023 * Reading Data from Server Failed
11026 Roo.form.Action.LOAD_FAILURE = 'load';
11028 Roo.form.Action.prototype = {
11030 failureType : undefined,
11031 response : undefined,
11032 result : undefined,
11034 // interface method
11035 run : function(options){
11039 // interface method
11040 success : function(response){
11044 // interface method
11045 handleResponse : function(response){
11049 // default connection failure
11050 failure : function(response){
11052 this.response = response;
11053 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11054 this.form.afterAction(this, false);
11057 processResponse : function(response){
11058 this.response = response;
11059 if(!response.responseText){
11062 this.result = this.handleResponse(response);
11063 return this.result;
11066 // utility functions used internally
11067 getUrl : function(appendParams){
11068 var url = this.options.url || this.form.url || this.form.el.dom.action;
11070 var p = this.getParams();
11072 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11078 getMethod : function(){
11079 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11082 getParams : function(){
11083 var bp = this.form.baseParams;
11084 var p = this.options.params;
11086 if(typeof p == "object"){
11087 p = Roo.urlEncode(Roo.applyIf(p, bp));
11088 }else if(typeof p == 'string' && bp){
11089 p += '&' + Roo.urlEncode(bp);
11092 p = Roo.urlEncode(bp);
11097 createCallback : function(){
11099 success: this.success,
11100 failure: this.failure,
11102 timeout: (this.form.timeout*1000),
11103 upload: this.form.fileUpload ? this.success : undefined
11108 Roo.form.Action.Submit = function(form, options){
11109 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11112 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11115 haveProgress : false,
11116 uploadComplete : false,
11118 // uploadProgress indicator.
11119 uploadProgress : function()
11121 if (!this.form.progressUrl) {
11125 if (!this.haveProgress) {
11126 Roo.MessageBox.progress("Uploading", "Uploading");
11128 if (this.uploadComplete) {
11129 Roo.MessageBox.hide();
11133 this.haveProgress = true;
11135 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11137 var c = new Roo.data.Connection();
11139 url : this.form.progressUrl,
11144 success : function(req){
11145 //console.log(data);
11149 rdata = Roo.decode(req.responseText)
11151 Roo.log("Invalid data from server..");
11155 if (!rdata || !rdata.success) {
11157 Roo.MessageBox.alert(Roo.encode(rdata));
11160 var data = rdata.data;
11162 if (this.uploadComplete) {
11163 Roo.MessageBox.hide();
11168 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11169 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11172 this.uploadProgress.defer(2000,this);
11175 failure: function(data) {
11176 Roo.log('progress url failed ');
11187 // run get Values on the form, so it syncs any secondary forms.
11188 this.form.getValues();
11190 var o = this.options;
11191 var method = this.getMethod();
11192 var isPost = method == 'POST';
11193 if(o.clientValidation === false || this.form.isValid()){
11195 if (this.form.progressUrl) {
11196 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11197 (new Date() * 1) + '' + Math.random());
11202 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11203 form:this.form.el.dom,
11204 url:this.getUrl(!isPost),
11206 params:isPost ? this.getParams() : null,
11207 isUpload: this.form.fileUpload,
11208 formData : this.form.formData
11211 this.uploadProgress();
11213 }else if (o.clientValidation !== false){ // client validation failed
11214 this.failureType = Roo.form.Action.CLIENT_INVALID;
11215 this.form.afterAction(this, false);
11219 success : function(response)
11221 this.uploadComplete= true;
11222 if (this.haveProgress) {
11223 Roo.MessageBox.hide();
11227 var result = this.processResponse(response);
11228 if(result === true || result.success){
11229 this.form.afterAction(this, true);
11233 this.form.markInvalid(result.errors);
11234 this.failureType = Roo.form.Action.SERVER_INVALID;
11236 this.form.afterAction(this, false);
11238 failure : function(response)
11240 this.uploadComplete= true;
11241 if (this.haveProgress) {
11242 Roo.MessageBox.hide();
11245 this.response = response;
11246 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11247 this.form.afterAction(this, false);
11250 handleResponse : function(response){
11251 if(this.form.errorReader){
11252 var rs = this.form.errorReader.read(response);
11255 for(var i = 0, len = rs.records.length; i < len; i++) {
11256 var r = rs.records[i];
11257 errors[i] = r.data;
11260 if(errors.length < 1){
11264 success : rs.success,
11270 ret = Roo.decode(response.responseText);
11274 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11284 Roo.form.Action.Load = function(form, options){
11285 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11286 this.reader = this.form.reader;
11289 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11294 Roo.Ajax.request(Roo.apply(
11295 this.createCallback(), {
11296 method:this.getMethod(),
11297 url:this.getUrl(false),
11298 params:this.getParams()
11302 success : function(response){
11304 var result = this.processResponse(response);
11305 if(result === true || !result.success || !result.data){
11306 this.failureType = Roo.form.Action.LOAD_FAILURE;
11307 this.form.afterAction(this, false);
11310 this.form.clearInvalid();
11311 this.form.setValues(result.data);
11312 this.form.afterAction(this, true);
11315 handleResponse : function(response){
11316 if(this.form.reader){
11317 var rs = this.form.reader.read(response);
11318 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11320 success : rs.success,
11324 return Roo.decode(response.responseText);
11328 Roo.form.Action.ACTION_TYPES = {
11329 'load' : Roo.form.Action.Load,
11330 'submit' : Roo.form.Action.Submit
11339 * @class Roo.bootstrap.form.Form
11340 * @extends Roo.bootstrap.Component
11341 * @children Roo.bootstrap.Component
11342 * Bootstrap Form class
11343 * @cfg {String} method GET | POST (default POST)
11344 * @cfg {String} labelAlign top | left (default top)
11345 * @cfg {String} align left | right - for navbars
11346 * @cfg {Boolean} loadMask load mask when submit (default true)
11350 * Create a new Form
11351 * @param {Object} config The config object
11355 Roo.bootstrap.form.Form = function(config){
11357 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11359 Roo.bootstrap.form.Form.popover.apply();
11363 * @event clientvalidation
11364 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11365 * @param {Form} this
11366 * @param {Boolean} valid true if the form has passed client-side validation
11368 clientvalidation: true,
11370 * @event beforeaction
11371 * Fires before any action is performed. Return false to cancel the action.
11372 * @param {Form} this
11373 * @param {Action} action The action to be performed
11375 beforeaction: true,
11377 * @event actionfailed
11378 * Fires when an action fails.
11379 * @param {Form} this
11380 * @param {Action} action The action that failed
11382 actionfailed : true,
11384 * @event actioncomplete
11385 * Fires when an action is completed.
11386 * @param {Form} this
11387 * @param {Action} action The action that completed
11389 actioncomplete : true
11393 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11396 * @cfg {String} method
11397 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11401 * @cfg {String} url
11402 * The URL to use for form actions if one isn't supplied in the action options.
11405 * @cfg {Boolean} fileUpload
11406 * Set to true if this form is a file upload.
11410 * @cfg {Object} baseParams
11411 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11415 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11419 * @cfg {Sting} align (left|right) for navbar forms
11424 activeAction : null,
11427 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11428 * element by passing it or its id or mask the form itself by passing in true.
11431 waitMsgTarget : false,
11436 * @cfg {Boolean} errorMask (true|false) default false
11441 * @cfg {Number} maskOffset Default 100
11446 * @cfg {Boolean} maskBody
11450 getAutoCreate : function(){
11454 method : this.method || 'POST',
11455 id : this.id || Roo.id(),
11458 if (this.parent().xtype.match(/^Nav/)) {
11459 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11463 if (this.labelAlign == 'left' ) {
11464 cfg.cls += ' form-horizontal';
11470 initEvents : function()
11472 this.el.on('submit', this.onSubmit, this);
11473 // this was added as random key presses on the form where triggering form submit.
11474 this.el.on('keypress', function(e) {
11475 if (e.getCharCode() != 13) {
11478 // we might need to allow it for textareas.. and some other items.
11479 // check e.getTarget().
11481 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11485 Roo.log("keypress blocked");
11487 e.preventDefault();
11493 onSubmit : function(e){
11498 * Returns true if client-side validation on the form is successful.
11501 isValid : function(){
11502 var items = this.getItems();
11504 var target = false;
11506 items.each(function(f){
11512 Roo.log('invalid field: ' + f.name);
11516 if(!target && f.el.isVisible(true)){
11522 if(this.errorMask && !valid){
11523 Roo.bootstrap.form.Form.popover.mask(this, target);
11530 * Returns true if any fields in this form have changed since their original load.
11533 isDirty : function(){
11535 var items = this.getItems();
11536 items.each(function(f){
11546 * Performs a predefined action (submit or load) or custom actions you define on this form.
11547 * @param {String} actionName The name of the action type
11548 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11549 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11550 * accept other config options):
11552 Property Type Description
11553 ---------------- --------------- ----------------------------------------------------------------------------------
11554 url String The url for the action (defaults to the form's url)
11555 method String The form method to use (defaults to the form's method, or POST if not defined)
11556 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11557 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11558 validate the form on the client (defaults to false)
11560 * @return {BasicForm} this
11562 doAction : function(action, options){
11563 if(typeof action == 'string'){
11564 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11566 if(this.fireEvent('beforeaction', this, action) !== false){
11567 this.beforeAction(action);
11568 action.run.defer(100, action);
11574 beforeAction : function(action){
11575 var o = action.options;
11580 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11582 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11585 // not really supported yet.. ??
11587 //if(this.waitMsgTarget === true){
11588 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11589 //}else if(this.waitMsgTarget){
11590 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11591 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11593 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11599 afterAction : function(action, success){
11600 this.activeAction = null;
11601 var o = action.options;
11606 Roo.get(document.body).unmask();
11612 //if(this.waitMsgTarget === true){
11613 // this.el.unmask();
11614 //}else if(this.waitMsgTarget){
11615 // this.waitMsgTarget.unmask();
11617 // Roo.MessageBox.updateProgress(1);
11618 // Roo.MessageBox.hide();
11625 Roo.callback(o.success, o.scope, [this, action]);
11626 this.fireEvent('actioncomplete', this, action);
11630 // failure condition..
11631 // we have a scenario where updates need confirming.
11632 // eg. if a locking scenario exists..
11633 // we look for { errors : { needs_confirm : true }} in the response.
11635 (typeof(action.result) != 'undefined') &&
11636 (typeof(action.result.errors) != 'undefined') &&
11637 (typeof(action.result.errors.needs_confirm) != 'undefined')
11640 Roo.log("not supported yet");
11643 Roo.MessageBox.confirm(
11644 "Change requires confirmation",
11645 action.result.errorMsg,
11650 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11660 Roo.callback(o.failure, o.scope, [this, action]);
11661 // show an error message if no failed handler is set..
11662 if (!this.hasListener('actionfailed')) {
11663 Roo.log("need to add dialog support");
11665 Roo.MessageBox.alert("Error",
11666 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11667 action.result.errorMsg :
11668 "Saving Failed, please check your entries or try again"
11673 this.fireEvent('actionfailed', this, action);
11678 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11679 * @param {String} id The value to search for
11682 findField : function(id){
11683 var items = this.getItems();
11684 var field = items.get(id);
11686 items.each(function(f){
11687 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11694 return field || null;
11697 * Mark fields in this form invalid in bulk.
11698 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11699 * @return {BasicForm} this
11701 markInvalid : function(errors){
11702 if(errors instanceof Array){
11703 for(var i = 0, len = errors.length; i < len; i++){
11704 var fieldError = errors[i];
11705 var f = this.findField(fieldError.id);
11707 f.markInvalid(fieldError.msg);
11713 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11714 field.markInvalid(errors[id]);
11718 //Roo.each(this.childForms || [], function (f) {
11719 // f.markInvalid(errors);
11726 * Set values for fields in this form in bulk.
11727 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11728 * @return {BasicForm} this
11730 setValues : function(values){
11731 if(values instanceof Array){ // array of objects
11732 for(var i = 0, len = values.length; i < len; i++){
11734 var f = this.findField(v.id);
11736 f.setValue(v.value);
11737 if(this.trackResetOnLoad){
11738 f.originalValue = f.getValue();
11742 }else{ // object hash
11745 if(typeof values[id] != 'function' && (field = this.findField(id))){
11747 if (field.setFromData &&
11748 field.valueField &&
11749 field.displayField &&
11750 // combos' with local stores can
11751 // be queried via setValue()
11752 // to set their value..
11753 (field.store && !field.store.isLocal)
11757 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11758 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11759 field.setFromData(sd);
11761 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11763 field.setFromData(values);
11766 field.setValue(values[id]);
11770 if(this.trackResetOnLoad){
11771 field.originalValue = field.getValue();
11777 //Roo.each(this.childForms || [], function (f) {
11778 // f.setValues(values);
11785 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11786 * they are returned as an array.
11787 * @param {Boolean} asString
11790 getValues : function(asString){
11791 //if (this.childForms) {
11792 // copy values from the child forms
11793 // Roo.each(this.childForms, function (f) {
11794 // this.setValues(f.getValues());
11800 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11801 if(asString === true){
11804 return Roo.urlDecode(fs);
11808 * Returns the fields in this form as an object with key/value pairs.
11809 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11812 getFieldValues : function(with_hidden)
11814 var items = this.getItems();
11816 items.each(function(f){
11818 if (!f.getName()) {
11822 var v = f.getValue();
11824 if (f.inputType =='radio') {
11825 if (typeof(ret[f.getName()]) == 'undefined') {
11826 ret[f.getName()] = ''; // empty..
11829 if (!f.el.dom.checked) {
11833 v = f.el.dom.value;
11837 if(f.xtype == 'MoneyField'){
11838 ret[f.currencyName] = f.getCurrency();
11841 // not sure if this supported any more..
11842 if ((typeof(v) == 'object') && f.getRawValue) {
11843 v = f.getRawValue() ; // dates..
11845 // combo boxes where name != hiddenName...
11846 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11847 ret[f.name] = f.getRawValue();
11849 ret[f.getName()] = v;
11856 * Clears all invalid messages in this form.
11857 * @return {BasicForm} this
11859 clearInvalid : function(){
11860 var items = this.getItems();
11862 items.each(function(f){
11870 * Resets this form.
11871 * @return {BasicForm} this
11873 reset : function(){
11874 var items = this.getItems();
11875 items.each(function(f){
11879 Roo.each(this.childForms || [], function (f) {
11887 getItems : function()
11889 var r=new Roo.util.MixedCollection(false, function(o){
11890 return o.id || (o.id = Roo.id());
11892 var iter = function(el) {
11899 Roo.each(el.items,function(e) {
11908 hideFields : function(items)
11910 Roo.each(items, function(i){
11912 var f = this.findField(i);
11923 showFields : function(items)
11925 Roo.each(items, function(i){
11927 var f = this.findField(i);
11940 Roo.apply(Roo.bootstrap.form.Form, {
11956 intervalID : false,
11962 if(this.isApplied){
11967 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11968 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11969 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11970 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11973 this.maskEl.top.enableDisplayMode("block");
11974 this.maskEl.left.enableDisplayMode("block");
11975 this.maskEl.bottom.enableDisplayMode("block");
11976 this.maskEl.right.enableDisplayMode("block");
11978 this.toolTip = new Roo.bootstrap.Tooltip({
11979 cls : 'roo-form-error-popover',
11981 'left' : ['r-l', [-2,0], 'right'],
11982 'right' : ['l-r', [2,0], 'left'],
11983 'bottom' : ['tl-bl', [0,2], 'top'],
11984 'top' : [ 'bl-tl', [0,-2], 'bottom']
11988 this.toolTip.render(Roo.get(document.body));
11990 this.toolTip.el.enableDisplayMode("block");
11992 Roo.get(document.body).on('click', function(){
11996 Roo.get(document.body).on('touchstart', function(){
12000 this.isApplied = true
12003 mask : function(form, target)
12007 this.target = target;
12009 if(!this.form.errorMask || !target.el){
12013 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12015 Roo.log(scrollable);
12017 var ot = this.target.el.calcOffsetsTo(scrollable);
12019 var scrollTo = ot[1] - this.form.maskOffset;
12021 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12023 scrollable.scrollTo('top', scrollTo);
12025 var box = this.target.el.getBox();
12027 var zIndex = Roo.bootstrap.Modal.zIndex++;
12030 this.maskEl.top.setStyle('position', 'absolute');
12031 this.maskEl.top.setStyle('z-index', zIndex);
12032 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12033 this.maskEl.top.setLeft(0);
12034 this.maskEl.top.setTop(0);
12035 this.maskEl.top.show();
12037 this.maskEl.left.setStyle('position', 'absolute');
12038 this.maskEl.left.setStyle('z-index', zIndex);
12039 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12040 this.maskEl.left.setLeft(0);
12041 this.maskEl.left.setTop(box.y - this.padding);
12042 this.maskEl.left.show();
12044 this.maskEl.bottom.setStyle('position', 'absolute');
12045 this.maskEl.bottom.setStyle('z-index', zIndex);
12046 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12047 this.maskEl.bottom.setLeft(0);
12048 this.maskEl.bottom.setTop(box.bottom + this.padding);
12049 this.maskEl.bottom.show();
12051 this.maskEl.right.setStyle('position', 'absolute');
12052 this.maskEl.right.setStyle('z-index', zIndex);
12053 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12054 this.maskEl.right.setLeft(box.right + this.padding);
12055 this.maskEl.right.setTop(box.y - this.padding);
12056 this.maskEl.right.show();
12058 this.toolTip.bindEl = this.target.el;
12060 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12062 var tip = this.target.blankText;
12064 if(this.target.getValue() !== '' ) {
12066 if (this.target.invalidText.length) {
12067 tip = this.target.invalidText;
12068 } else if (this.target.regexText.length){
12069 tip = this.target.regexText;
12073 this.toolTip.show(tip);
12075 this.intervalID = window.setInterval(function() {
12076 Roo.bootstrap.form.Form.popover.unmask();
12079 window.onwheel = function(){ return false;};
12081 (function(){ this.isMasked = true; }).defer(500, this);
12085 unmask : function()
12087 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12091 this.maskEl.top.setStyle('position', 'absolute');
12092 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12093 this.maskEl.top.hide();
12095 this.maskEl.left.setStyle('position', 'absolute');
12096 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12097 this.maskEl.left.hide();
12099 this.maskEl.bottom.setStyle('position', 'absolute');
12100 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12101 this.maskEl.bottom.hide();
12103 this.maskEl.right.setStyle('position', 'absolute');
12104 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12105 this.maskEl.right.hide();
12107 this.toolTip.hide();
12109 this.toolTip.el.hide();
12111 window.onwheel = function(){ return true;};
12113 if(this.intervalID){
12114 window.clearInterval(this.intervalID);
12115 this.intervalID = false;
12118 this.isMasked = false;
12128 * Ext JS Library 1.1.1
12129 * Copyright(c) 2006-2007, Ext JS, LLC.
12131 * Originally Released Under LGPL - original licence link has changed is not relivant.
12134 * <script type="text/javascript">
12137 * @class Roo.form.VTypes
12138 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12141 Roo.form.VTypes = function(){
12142 // closure these in so they are only created once.
12143 var alpha = /^[a-zA-Z_]+$/;
12144 var alphanum = /^[a-zA-Z0-9_]+$/;
12145 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12146 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12148 // All these messages and functions are configurable
12151 * The function used to validate email addresses
12152 * @param {String} value The email address
12154 'email' : function(v){
12155 return email.test(v);
12158 * The error text to display when the email validation function returns false
12161 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12163 * The keystroke filter mask to be applied on email input
12166 'emailMask' : /[a-z0-9_\.\-@]/i,
12169 * The function used to validate URLs
12170 * @param {String} value The URL
12172 'url' : function(v){
12173 return url.test(v);
12176 * The error text to display when the url validation function returns false
12179 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12182 * The function used to validate alpha values
12183 * @param {String} value The value
12185 'alpha' : function(v){
12186 return alpha.test(v);
12189 * The error text to display when the alpha validation function returns false
12192 'alphaText' : 'This field should only contain letters and _',
12194 * The keystroke filter mask to be applied on alpha input
12197 'alphaMask' : /[a-z_]/i,
12200 * The function used to validate alphanumeric values
12201 * @param {String} value The value
12203 'alphanum' : function(v){
12204 return alphanum.test(v);
12207 * The error text to display when the alphanumeric validation function returns false
12210 'alphanumText' : 'This field should only contain letters, numbers and _',
12212 * The keystroke filter mask to be applied on alphanumeric input
12215 'alphanumMask' : /[a-z0-9_]/i
12225 * @class Roo.bootstrap.form.Input
12226 * @extends Roo.bootstrap.Component
12227 * Bootstrap Input class
12228 * @cfg {Boolean} disabled is it disabled
12229 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12230 * @cfg {String} name name of the input
12231 * @cfg {string} fieldLabel - the label associated
12232 * @cfg {string} placeholder - placeholder to put in text.
12233 * @cfg {string} before - input group add on before
12234 * @cfg {string} after - input group add on after
12235 * @cfg {string} size - (lg|sm) or leave empty..
12236 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12237 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12238 * @cfg {Number} md colspan out of 12 for computer-sized screens
12239 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12240 * @cfg {string} value default value of the input
12241 * @cfg {Number} labelWidth set the width of label
12242 * @cfg {Number} labellg set the width of label (1-12)
12243 * @cfg {Number} labelmd set the width of label (1-12)
12244 * @cfg {Number} labelsm set the width of label (1-12)
12245 * @cfg {Number} labelxs set the width of label (1-12)
12246 * @cfg {String} labelAlign (top|left)
12247 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12248 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12249 * @cfg {String} indicatorpos (left|right) default left
12250 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12251 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12252 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12253 * @cfg {Roo.bootstrap.Button} before Button to show before
12254 * @cfg {Roo.bootstrap.Button} afterButton to show before
12255 * @cfg {String} align (left|center|right) Default left
12256 * @cfg {Boolean} forceFeedback (true|false) Default false
12259 * Create a new Input
12260 * @param {Object} config The config object
12263 Roo.bootstrap.form.Input = function(config){
12265 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12270 * Fires when this field receives input focus.
12271 * @param {Roo.form.Field} this
12276 * Fires when this field loses input focus.
12277 * @param {Roo.form.Field} this
12281 * @event specialkey
12282 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12283 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12284 * @param {Roo.form.Field} this
12285 * @param {Roo.EventObject} e The event object
12290 * Fires just before the field blurs if the field value has changed.
12291 * @param {Roo.form.Field} this
12292 * @param {Mixed} newValue The new value
12293 * @param {Mixed} oldValue The original value
12298 * Fires after the field has been marked as invalid.
12299 * @param {Roo.form.Field} this
12300 * @param {String} msg The validation message
12305 * Fires after the field has been validated with no errors.
12306 * @param {Roo.form.Field} this
12311 * Fires after the key up
12312 * @param {Roo.form.Field} this
12313 * @param {Roo.EventObject} e The event Object
12318 * Fires after the user pastes into input
12319 * @param {Roo.form.Field} this
12320 * @param {Roo.EventObject} e The event Object
12326 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12328 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12329 automatic validation (defaults to "keyup").
12331 validationEvent : "keyup",
12333 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12335 validateOnBlur : true,
12337 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12339 validationDelay : 250,
12341 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12343 focusClass : "x-form-focus", // not needed???
12347 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12349 invalidClass : "has-warning",
12352 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12354 validClass : "has-success",
12357 * @cfg {Boolean} hasFeedback (true|false) default true
12359 hasFeedback : true,
12362 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12364 invalidFeedbackClass : "glyphicon-warning-sign",
12367 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12369 validFeedbackClass : "glyphicon-ok",
12372 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12374 selectOnFocus : false,
12377 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12381 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12386 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12388 disableKeyFilter : false,
12391 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12395 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12399 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12401 blankText : "Please complete this mandatory field",
12404 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12408 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12410 maxLength : Number.MAX_VALUE,
12412 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12414 minLengthText : "The minimum length for this field is {0}",
12416 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12418 maxLengthText : "The maximum length for this field is {0}",
12422 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12423 * If available, this function will be called only after the basic validators all return true, and will be passed the
12424 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12428 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12429 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12430 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12434 * @cfg {String} regexText -- Depricated - use Invalid Text
12439 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12445 autocomplete: false,
12449 inputType : 'text',
12452 placeholder: false,
12457 preventMark: false,
12458 isFormField : true,
12461 labelAlign : false,
12464 formatedValue : false,
12465 forceFeedback : false,
12467 indicatorpos : 'left',
12477 parentLabelAlign : function()
12480 while (parent.parent()) {
12481 parent = parent.parent();
12482 if (typeof(parent.labelAlign) !='undefined') {
12483 return parent.labelAlign;
12490 getAutoCreate : function()
12492 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12498 if(this.inputType != 'hidden'){
12499 cfg.cls = 'form-group' //input-group
12505 type : this.inputType,
12506 value : this.value,
12507 cls : 'form-control',
12508 placeholder : this.placeholder || '',
12509 autocomplete : this.autocomplete || 'new-password'
12511 if (this.inputType == 'file') {
12512 input.style = 'overflow:hidden'; // why not in CSS?
12515 if(this.capture.length){
12516 input.capture = this.capture;
12519 if(this.accept.length){
12520 input.accept = this.accept + "/*";
12524 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12527 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12528 input.maxLength = this.maxLength;
12531 if (this.disabled) {
12532 input.disabled=true;
12535 if (this.readOnly) {
12536 input.readonly=true;
12540 input.name = this.name;
12544 input.cls += ' input-' + this.size;
12548 ['xs','sm','md','lg'].map(function(size){
12549 if (settings[size]) {
12550 cfg.cls += ' col-' + size + '-' + settings[size];
12554 var inputblock = input;
12558 cls: 'glyphicon form-control-feedback'
12561 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12564 cls : 'has-feedback',
12572 if (this.before || this.after) {
12575 cls : 'input-group',
12579 if (this.before && typeof(this.before) == 'string') {
12581 inputblock.cn.push({
12583 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12587 if (this.before && typeof(this.before) == 'object') {
12588 this.before = Roo.factory(this.before);
12590 inputblock.cn.push({
12592 cls : 'roo-input-before input-group-prepend input-group-' +
12593 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12597 inputblock.cn.push(input);
12599 if (this.after && typeof(this.after) == 'string') {
12600 inputblock.cn.push({
12602 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12606 if (this.after && typeof(this.after) == 'object') {
12607 this.after = Roo.factory(this.after);
12609 inputblock.cn.push({
12611 cls : 'roo-input-after input-group-append input-group-' +
12612 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12617 inputblock.cls += ' has-feedback';
12618 inputblock.cn.push(feedback);
12623 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12624 tooltip : 'This field is required'
12626 if (this.allowBlank ) {
12627 indicator.style = this.allowBlank ? ' display:none' : '';
12629 if (align ==='left' && this.fieldLabel.length) {
12631 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12638 cls : 'control-label col-form-label',
12639 html : this.fieldLabel
12650 var labelCfg = cfg.cn[1];
12651 var contentCfg = cfg.cn[2];
12653 if(this.indicatorpos == 'right'){
12658 cls : 'control-label col-form-label',
12662 html : this.fieldLabel
12676 labelCfg = cfg.cn[0];
12677 contentCfg = cfg.cn[1];
12681 if(this.labelWidth > 12){
12682 labelCfg.style = "width: " + this.labelWidth + 'px';
12685 if(this.labelWidth < 13 && this.labelmd == 0){
12686 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12689 if(this.labellg > 0){
12690 labelCfg.cls += ' col-lg-' + this.labellg;
12691 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12694 if(this.labelmd > 0){
12695 labelCfg.cls += ' col-md-' + this.labelmd;
12696 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12699 if(this.labelsm > 0){
12700 labelCfg.cls += ' col-sm-' + this.labelsm;
12701 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12704 if(this.labelxs > 0){
12705 labelCfg.cls += ' col-xs-' + this.labelxs;
12706 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12710 } else if ( this.fieldLabel.length) {
12717 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12718 tooltip : 'This field is required',
12719 style : this.allowBlank ? ' display:none' : ''
12723 //cls : 'input-group-addon',
12724 html : this.fieldLabel
12732 if(this.indicatorpos == 'right'){
12737 //cls : 'input-group-addon',
12738 html : this.fieldLabel
12743 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12744 tooltip : 'This field is required',
12745 style : this.allowBlank ? ' display:none' : ''
12765 if (this.parentType === 'Navbar' && this.parent().bar) {
12766 cfg.cls += ' navbar-form';
12769 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12770 // on BS4 we do this only if not form
12771 cfg.cls += ' navbar-form';
12779 * return the real input element.
12781 inputEl: function ()
12783 return this.el.select('input.form-control',true).first();
12786 tooltipEl : function()
12788 return this.inputEl();
12791 indicatorEl : function()
12793 if (Roo.bootstrap.version == 4) {
12794 return false; // not enabled in v4 yet.
12797 var indicator = this.el.select('i.roo-required-indicator',true).first();
12807 setDisabled : function(v)
12809 var i = this.inputEl().dom;
12811 i.removeAttribute('disabled');
12815 i.setAttribute('disabled','true');
12817 initEvents : function()
12820 this.inputEl().on("keydown" , this.fireKey, this);
12821 this.inputEl().on("focus", this.onFocus, this);
12822 this.inputEl().on("blur", this.onBlur, this);
12824 this.inputEl().relayEvent('keyup', this);
12825 this.inputEl().relayEvent('paste', this);
12827 this.indicator = this.indicatorEl();
12829 if(this.indicator){
12830 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12833 // reference to original value for reset
12834 this.originalValue = this.getValue();
12835 //Roo.form.TextField.superclass.initEvents.call(this);
12836 if(this.validationEvent == 'keyup'){
12837 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12838 this.inputEl().on('keyup', this.filterValidation, this);
12840 else if(this.validationEvent !== false){
12841 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12844 if(this.selectOnFocus){
12845 this.on("focus", this.preFocus, this);
12848 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12849 this.inputEl().on("keypress", this.filterKeys, this);
12851 this.inputEl().relayEvent('keypress', this);
12854 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12855 this.el.on("click", this.autoSize, this);
12858 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12859 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12862 if (typeof(this.before) == 'object') {
12863 this.before.render(this.el.select('.roo-input-before',true).first());
12865 if (typeof(this.after) == 'object') {
12866 this.after.render(this.el.select('.roo-input-after',true).first());
12869 this.inputEl().on('change', this.onChange, this);
12872 filterValidation : function(e){
12873 if(!e.isNavKeyPress()){
12874 this.validationTask.delay(this.validationDelay);
12878 * Validates the field value
12879 * @return {Boolean} True if the value is valid, else false
12881 validate : function(){
12882 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12883 if(this.disabled || this.validateValue(this.getRawValue())){
12888 this.markInvalid();
12894 * Validates a value according to the field's validation rules and marks the field as invalid
12895 * if the validation fails
12896 * @param {Mixed} value The value to validate
12897 * @return {Boolean} True if the value is valid, else false
12899 validateValue : function(value)
12901 if(this.getVisibilityEl().hasClass('hidden')){
12905 if(value.length < 1) { // if it's blank
12906 if(this.allowBlank){
12912 if(value.length < this.minLength){
12915 if(value.length > this.maxLength){
12919 var vt = Roo.form.VTypes;
12920 if(!vt[this.vtype](value, this)){
12924 if(typeof this.validator == "function"){
12925 var msg = this.validator(value);
12929 if (typeof(msg) == 'string') {
12930 this.invalidText = msg;
12934 if(this.regex && !this.regex.test(value)){
12942 fireKey : function(e){
12943 //Roo.log('field ' + e.getKey());
12944 if(e.isNavKeyPress()){
12945 this.fireEvent("specialkey", this, e);
12948 focus : function (selectText){
12950 this.inputEl().focus();
12951 if(selectText === true){
12952 this.inputEl().dom.select();
12958 onFocus : function(){
12959 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12960 // this.el.addClass(this.focusClass);
12962 if(!this.hasFocus){
12963 this.hasFocus = true;
12964 this.startValue = this.getValue();
12965 this.fireEvent("focus", this);
12969 beforeBlur : Roo.emptyFn,
12973 onBlur : function(){
12975 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12976 //this.el.removeClass(this.focusClass);
12978 this.hasFocus = false;
12979 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12982 var v = this.getValue();
12983 if(String(v) !== String(this.startValue)){
12984 this.fireEvent('change', this, v, this.startValue);
12986 this.fireEvent("blur", this);
12989 onChange : function(e)
12991 var v = this.getValue();
12992 if(String(v) !== String(this.startValue)){
12993 this.fireEvent('change', this, v, this.startValue);
12999 * Resets the current field value to the originally loaded value and clears any validation messages
13001 reset : function(){
13002 this.setValue(this.originalValue);
13006 * Returns the name of the field
13007 * @return {Mixed} name The name field
13009 getName: function(){
13013 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13014 * @return {Mixed} value The field value
13016 getValue : function(){
13018 var v = this.inputEl().getValue();
13023 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13024 * @return {Mixed} value The field value
13026 getRawValue : function(){
13027 var v = this.inputEl().getValue();
13033 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13034 * @param {Mixed} value The value to set
13036 setRawValue : function(v){
13037 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13040 selectText : function(start, end){
13041 var v = this.getRawValue();
13043 start = start === undefined ? 0 : start;
13044 end = end === undefined ? v.length : end;
13045 var d = this.inputEl().dom;
13046 if(d.setSelectionRange){
13047 d.setSelectionRange(start, end);
13048 }else if(d.createTextRange){
13049 var range = d.createTextRange();
13050 range.moveStart("character", start);
13051 range.moveEnd("character", v.length-end);
13058 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13059 * @param {Mixed} value The value to set
13061 setValue : function(v){
13064 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13070 processValue : function(value){
13071 if(this.stripCharsRe){
13072 var newValue = value.replace(this.stripCharsRe, '');
13073 if(newValue !== value){
13074 this.setRawValue(newValue);
13081 preFocus : function(){
13083 if(this.selectOnFocus){
13084 this.inputEl().dom.select();
13087 filterKeys : function(e){
13088 var k = e.getKey();
13089 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13092 var c = e.getCharCode(), cc = String.fromCharCode(c);
13093 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13096 if(!this.maskRe.test(cc)){
13101 * Clear any invalid styles/messages for this field
13103 clearInvalid : function(){
13105 if(!this.el || this.preventMark){ // not rendered
13110 this.el.removeClass([this.invalidClass, 'is-invalid']);
13112 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13114 var feedback = this.el.select('.form-control-feedback', true).first();
13117 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13122 if(this.indicator){
13123 this.indicator.removeClass('visible');
13124 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13127 this.fireEvent('valid', this);
13131 * Mark this field as valid
13133 markValid : function()
13135 if(!this.el || this.preventMark){ // not rendered...
13139 this.el.removeClass([this.invalidClass, this.validClass]);
13140 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13142 var feedback = this.el.select('.form-control-feedback', true).first();
13145 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13148 if(this.indicator){
13149 this.indicator.removeClass('visible');
13150 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13158 if(this.allowBlank && !this.getRawValue().length){
13161 if (Roo.bootstrap.version == 3) {
13162 this.el.addClass(this.validClass);
13164 this.inputEl().addClass('is-valid');
13167 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13169 var feedback = this.el.select('.form-control-feedback', true).first();
13172 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13173 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13178 this.fireEvent('valid', this);
13182 * Mark this field as invalid
13183 * @param {String} msg The validation message
13185 markInvalid : function(msg)
13187 if(!this.el || this.preventMark){ // not rendered
13191 this.el.removeClass([this.invalidClass, this.validClass]);
13192 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13194 var feedback = this.el.select('.form-control-feedback', true).first();
13197 this.el.select('.form-control-feedback', true).first().removeClass(
13198 [this.invalidFeedbackClass, this.validFeedbackClass]);
13205 if(this.allowBlank && !this.getRawValue().length){
13209 if(this.indicator){
13210 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13211 this.indicator.addClass('visible');
13213 if (Roo.bootstrap.version == 3) {
13214 this.el.addClass(this.invalidClass);
13216 this.inputEl().addClass('is-invalid');
13221 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13223 var feedback = this.el.select('.form-control-feedback', true).first();
13226 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13228 if(this.getValue().length || this.forceFeedback){
13229 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13236 this.fireEvent('invalid', this, msg);
13239 SafariOnKeyDown : function(event)
13241 // this is a workaround for a password hang bug on chrome/ webkit.
13242 if (this.inputEl().dom.type != 'password') {
13246 var isSelectAll = false;
13248 if(this.inputEl().dom.selectionEnd > 0){
13249 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13251 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13252 event.preventDefault();
13257 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13259 event.preventDefault();
13260 // this is very hacky as keydown always get's upper case.
13262 var cc = String.fromCharCode(event.getCharCode());
13263 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13267 adjustWidth : function(tag, w){
13268 tag = tag.toLowerCase();
13269 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13270 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13271 if(tag == 'input'){
13274 if(tag == 'textarea'){
13277 }else if(Roo.isOpera){
13278 if(tag == 'input'){
13281 if(tag == 'textarea'){
13289 setFieldLabel : function(v)
13291 if(!this.rendered){
13295 if(this.indicatorEl()){
13296 var ar = this.el.select('label > span',true);
13298 if (ar.elements.length) {
13299 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13300 this.fieldLabel = v;
13304 var br = this.el.select('label',true);
13306 if(br.elements.length) {
13307 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308 this.fieldLabel = v;
13312 Roo.log('Cannot Found any of label > span || label in input');
13316 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317 this.fieldLabel = v;
13332 * @class Roo.bootstrap.form.TextArea
13333 * @extends Roo.bootstrap.form.Input
13334 * Bootstrap TextArea class
13335 * @cfg {Number} cols Specifies the visible width of a text area
13336 * @cfg {Number} rows Specifies the visible number of lines in a text area
13337 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13338 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13339 * @cfg {string} html text
13342 * Create a new TextArea
13343 * @param {Object} config The config object
13346 Roo.bootstrap.form.TextArea = function(config){
13347 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13351 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13361 getAutoCreate : function(){
13363 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13369 if(this.inputType != 'hidden'){
13370 cfg.cls = 'form-group' //input-group
13378 value : this.value || '',
13379 html: this.html || '',
13380 cls : 'form-control',
13381 placeholder : this.placeholder || ''
13385 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13386 input.maxLength = this.maxLength;
13390 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13394 input.cols = this.cols;
13397 if (this.readOnly) {
13398 input.readonly = true;
13402 input.name = this.name;
13406 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13410 ['xs','sm','md','lg'].map(function(size){
13411 if (settings[size]) {
13412 cfg.cls += ' col-' + size + '-' + settings[size];
13416 var inputblock = input;
13418 if(this.hasFeedback && !this.allowBlank){
13422 cls: 'glyphicon form-control-feedback'
13426 cls : 'has-feedback',
13435 if (this.before || this.after) {
13438 cls : 'input-group',
13442 inputblock.cn.push({
13444 cls : 'input-group-addon',
13449 inputblock.cn.push(input);
13451 if(this.hasFeedback && !this.allowBlank){
13452 inputblock.cls += ' has-feedback';
13453 inputblock.cn.push(feedback);
13457 inputblock.cn.push({
13459 cls : 'input-group-addon',
13466 if (align ==='left' && this.fieldLabel.length) {
13471 cls : 'control-label',
13472 html : this.fieldLabel
13483 if(this.labelWidth > 12){
13484 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13487 if(this.labelWidth < 13 && this.labelmd == 0){
13488 this.labelmd = this.labelWidth;
13491 if(this.labellg > 0){
13492 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13493 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13496 if(this.labelmd > 0){
13497 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13498 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13501 if(this.labelsm > 0){
13502 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13503 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13506 if(this.labelxs > 0){
13507 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13508 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13511 } else if ( this.fieldLabel.length) {
13516 //cls : 'input-group-addon',
13517 html : this.fieldLabel
13535 if (this.disabled) {
13536 input.disabled=true;
13543 * return the real textarea element.
13545 inputEl: function ()
13547 return this.el.select('textarea.form-control',true).first();
13551 * Clear any invalid styles/messages for this field
13553 clearInvalid : function()
13556 if(!this.el || this.preventMark){ // not rendered
13560 var label = this.el.select('label', true).first();
13561 var icon = this.el.select('i.fa-star', true).first();
13566 this.el.removeClass( this.validClass);
13567 this.inputEl().removeClass('is-invalid');
13569 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13571 var feedback = this.el.select('.form-control-feedback', true).first();
13574 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13579 this.fireEvent('valid', this);
13583 * Mark this field as valid
13585 markValid : function()
13587 if(!this.el || this.preventMark){ // not rendered
13591 this.el.removeClass([this.invalidClass, this.validClass]);
13592 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13594 var feedback = this.el.select('.form-control-feedback', true).first();
13597 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13600 if(this.disabled || this.allowBlank){
13604 var label = this.el.select('label', true).first();
13605 var icon = this.el.select('i.fa-star', true).first();
13610 if (Roo.bootstrap.version == 3) {
13611 this.el.addClass(this.validClass);
13613 this.inputEl().addClass('is-valid');
13617 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13619 var feedback = this.el.select('.form-control-feedback', true).first();
13622 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13623 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13628 this.fireEvent('valid', this);
13632 * Mark this field as invalid
13633 * @param {String} msg The validation message
13635 markInvalid : function(msg)
13637 if(!this.el || this.preventMark){ // not rendered
13641 this.el.removeClass([this.invalidClass, this.validClass]);
13642 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13644 var feedback = this.el.select('.form-control-feedback', true).first();
13647 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13650 if(this.disabled || this.allowBlank){
13654 var label = this.el.select('label', true).first();
13655 var icon = this.el.select('i.fa-star', true).first();
13657 if(!this.getValue().length && label && !icon){
13658 this.el.createChild({
13660 cls : 'text-danger fa fa-lg fa-star',
13661 tooltip : 'This field is required',
13662 style : 'margin-right:5px;'
13666 if (Roo.bootstrap.version == 3) {
13667 this.el.addClass(this.invalidClass);
13669 this.inputEl().addClass('is-invalid');
13672 // fixme ... this may be depricated need to test..
13673 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13675 var feedback = this.el.select('.form-control-feedback', true).first();
13678 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13680 if(this.getValue().length || this.forceFeedback){
13681 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13688 this.fireEvent('invalid', this, msg);
13696 * trigger field - base class for combo..
13701 * @class Roo.bootstrap.form.TriggerField
13702 * @extends Roo.bootstrap.form.Input
13703 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13704 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13705 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13706 * for which you can provide a custom implementation. For example:
13708 var trigger = new Roo.bootstrap.form.TriggerField();
13709 trigger.onTriggerClick = myTriggerFn;
13710 trigger.applyTo('my-field');
13713 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13714 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13715 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13716 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13717 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13720 * Create a new TriggerField.
13721 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13722 * to the base TextField)
13724 Roo.bootstrap.form.TriggerField = function(config){
13725 this.mimicing = false;
13726 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13729 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13731 * @cfg {String} triggerClass A CSS class to apply to the trigger
13734 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13739 * @cfg {Boolean} removable (true|false) special filter default false
13743 /** @cfg {Boolean} grow @hide */
13744 /** @cfg {Number} growMin @hide */
13745 /** @cfg {Number} growMax @hide */
13751 autoSize: Roo.emptyFn,
13755 deferHeight : true,
13758 actionMode : 'wrap',
13763 getAutoCreate : function(){
13765 var align = this.labelAlign || this.parentLabelAlign();
13770 cls: 'form-group' //input-group
13777 type : this.inputType,
13778 cls : 'form-control',
13779 autocomplete: 'new-password',
13780 placeholder : this.placeholder || ''
13784 input.name = this.name;
13787 input.cls += ' input-' + this.size;
13790 if (this.disabled) {
13791 input.disabled=true;
13794 var inputblock = input;
13796 if(this.hasFeedback && !this.allowBlank){
13800 cls: 'glyphicon form-control-feedback'
13803 if(this.removable && !this.editable ){
13805 cls : 'has-feedback',
13811 cls : 'roo-combo-removable-btn close'
13818 cls : 'has-feedback',
13827 if(this.removable && !this.editable ){
13829 cls : 'roo-removable',
13835 cls : 'roo-combo-removable-btn close'
13842 if (this.before || this.after) {
13845 cls : 'input-group',
13849 inputblock.cn.push({
13851 cls : 'input-group-addon input-group-prepend input-group-text',
13856 inputblock.cn.push(input);
13858 if(this.hasFeedback && !this.allowBlank){
13859 inputblock.cls += ' has-feedback';
13860 inputblock.cn.push(feedback);
13864 inputblock.cn.push({
13866 cls : 'input-group-addon input-group-append input-group-text',
13875 var ibwrap = inputblock;
13880 cls: 'roo-select2-choices',
13884 cls: 'roo-select2-search-field',
13896 cls: 'roo-select2-container input-group',
13901 cls: 'form-hidden-field'
13907 if(!this.multiple && this.showToggleBtn){
13913 if (this.caret != false) {
13916 cls: 'fa fa-' + this.caret
13923 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13925 Roo.bootstrap.version == 3 ? caret : '',
13928 cls: 'combobox-clear',
13942 combobox.cls += ' roo-select2-container-multi';
13946 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13947 tooltip : 'This field is required'
13949 if (Roo.bootstrap.version == 4) {
13952 style : 'display:none'
13957 if (align ==='left' && this.fieldLabel.length) {
13959 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13966 cls : 'control-label',
13967 html : this.fieldLabel
13979 var labelCfg = cfg.cn[1];
13980 var contentCfg = cfg.cn[2];
13982 if(this.indicatorpos == 'right'){
13987 cls : 'control-label',
13991 html : this.fieldLabel
14005 labelCfg = cfg.cn[0];
14006 contentCfg = cfg.cn[1];
14009 if(this.labelWidth > 12){
14010 labelCfg.style = "width: " + this.labelWidth + 'px';
14013 if(this.labelWidth < 13 && this.labelmd == 0){
14014 this.labelmd = this.labelWidth;
14017 if(this.labellg > 0){
14018 labelCfg.cls += ' col-lg-' + this.labellg;
14019 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14022 if(this.labelmd > 0){
14023 labelCfg.cls += ' col-md-' + this.labelmd;
14024 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14027 if(this.labelsm > 0){
14028 labelCfg.cls += ' col-sm-' + this.labelsm;
14029 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14032 if(this.labelxs > 0){
14033 labelCfg.cls += ' col-xs-' + this.labelxs;
14034 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14037 } else if ( this.fieldLabel.length) {
14038 // Roo.log(" label");
14043 //cls : 'input-group-addon',
14044 html : this.fieldLabel
14052 if(this.indicatorpos == 'right'){
14060 html : this.fieldLabel
14074 // Roo.log(" no label && no align");
14081 ['xs','sm','md','lg'].map(function(size){
14082 if (settings[size]) {
14083 cfg.cls += ' col-' + size + '-' + settings[size];
14094 onResize : function(w, h){
14095 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14096 // if(typeof w == 'number'){
14097 // var x = w - this.trigger.getWidth();
14098 // this.inputEl().setWidth(this.adjustWidth('input', x));
14099 // this.trigger.setStyle('left', x+'px');
14104 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14107 getResizeEl : function(){
14108 return this.inputEl();
14112 getPositionEl : function(){
14113 return this.inputEl();
14117 alignErrorIcon : function(){
14118 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14122 initEvents : function(){
14126 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14127 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14128 if(!this.multiple && this.showToggleBtn){
14129 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14130 if(this.hideTrigger){
14131 this.trigger.setDisplayed(false);
14133 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14137 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14140 if(this.removable && !this.editable && !this.tickable){
14141 var close = this.closeTriggerEl();
14144 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14145 close.on('click', this.removeBtnClick, this, close);
14149 //this.trigger.addClassOnOver('x-form-trigger-over');
14150 //this.trigger.addClassOnClick('x-form-trigger-click');
14153 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14157 closeTriggerEl : function()
14159 var close = this.el.select('.roo-combo-removable-btn', true).first();
14160 return close ? close : false;
14163 removeBtnClick : function(e, h, el)
14165 e.preventDefault();
14167 if(this.fireEvent("remove", this) !== false){
14169 this.fireEvent("afterremove", this)
14173 createList : function()
14175 this.list = Roo.get(document.body).createChild({
14176 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14177 cls: 'typeahead typeahead-long dropdown-menu shadow',
14178 style: 'display:none'
14181 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14186 initTrigger : function(){
14191 onDestroy : function(){
14193 this.trigger.removeAllListeners();
14194 // this.trigger.remove();
14197 // this.wrap.remove();
14199 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14203 onFocus : function(){
14204 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14206 if(!this.mimicing){
14207 this.wrap.addClass('x-trigger-wrap-focus');
14208 this.mimicing = true;
14209 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14210 if(this.monitorTab){
14211 this.el.on("keydown", this.checkTab, this);
14218 checkTab : function(e){
14219 if(e.getKey() == e.TAB){
14220 this.triggerBlur();
14225 onBlur : function(){
14230 mimicBlur : function(e, t){
14232 if(!this.wrap.contains(t) && this.validateBlur()){
14233 this.triggerBlur();
14239 triggerBlur : function(){
14240 this.mimicing = false;
14241 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14242 if(this.monitorTab){
14243 this.el.un("keydown", this.checkTab, this);
14245 //this.wrap.removeClass('x-trigger-wrap-focus');
14246 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14250 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14251 validateBlur : function(e, t){
14256 onDisable : function(){
14257 this.inputEl().dom.disabled = true;
14258 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14260 // this.wrap.addClass('x-item-disabled');
14265 onEnable : function(){
14266 this.inputEl().dom.disabled = false;
14267 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14269 // this.el.removeClass('x-item-disabled');
14274 onShow : function(){
14275 var ae = this.getActionEl();
14278 ae.dom.style.display = '';
14279 ae.dom.style.visibility = 'visible';
14285 onHide : function(){
14286 var ae = this.getActionEl();
14287 ae.dom.style.display = 'none';
14291 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14292 * by an implementing function.
14294 * @param {EventObject} e
14296 onTriggerClick : Roo.emptyFn
14304 * @class Roo.bootstrap.form.CardUploader
14305 * @extends Roo.bootstrap.Button
14306 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14307 * @cfg {Number} errorTimeout default 3000
14308 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14309 * @cfg {Array} html The button text.
14313 * Create a new CardUploader
14314 * @param {Object} config The config object
14317 Roo.bootstrap.form.CardUploader = function(config){
14321 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14324 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14332 * When a image is clicked on - and needs to display a slideshow or similar..
14333 * @param {Roo.bootstrap.Card} this
14334 * @param {Object} The image information data
14340 * When a the download link is clicked
14341 * @param {Roo.bootstrap.Card} this
14342 * @param {Object} The image information data contains
14349 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14352 errorTimeout : 3000,
14356 fileCollection : false,
14359 getAutoCreate : function()
14363 cls :'form-group' ,
14368 //cls : 'input-group-addon',
14369 html : this.fieldLabel
14377 value : this.value,
14378 cls : 'd-none form-control'
14383 multiple : 'multiple',
14385 cls : 'd-none roo-card-upload-selector'
14389 cls : 'roo-card-uploader-button-container w-100 mb-2'
14392 cls : 'card-columns roo-card-uploader-container'
14402 getChildContainer : function() /// what children are added to.
14404 return this.containerEl;
14407 getButtonContainer : function() /// what children are added to.
14409 return this.el.select(".roo-card-uploader-button-container").first();
14412 initEvents : function()
14415 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14419 xns: Roo.bootstrap,
14422 container_method : 'getButtonContainer' ,
14423 html : this.html, // fix changable?
14426 'click' : function(btn, e) {
14435 this.urlAPI = (window.createObjectURL && window) ||
14436 (window.URL && URL.revokeObjectURL && URL) ||
14437 (window.webkitURL && webkitURL);
14442 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14444 this.selectorEl.on('change', this.onFileSelected, this);
14447 this.images.forEach(function(img) {
14450 this.images = false;
14452 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14458 onClick : function(e)
14460 e.preventDefault();
14462 this.selectorEl.dom.click();
14466 onFileSelected : function(e)
14468 e.preventDefault();
14470 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14474 Roo.each(this.selectorEl.dom.files, function(file){
14475 this.addFile(file);
14484 addFile : function(file)
14487 if(typeof(file) === 'string'){
14488 throw "Add file by name?"; // should not happen
14492 if(!file || !this.urlAPI){
14502 var url = _this.urlAPI.createObjectURL( file);
14505 id : Roo.bootstrap.form.CardUploader.ID--,
14506 is_uploaded : false,
14510 mimetype : file.type,
14518 * addCard - add an Attachment to the uploader
14519 * @param data - the data about the image to upload
14523 title : "Title of file",
14524 is_uploaded : false,
14525 src : "http://.....",
14526 srcfile : { the File upload object },
14527 mimetype : file.type,
14530 .. any other data...
14536 addCard : function (data)
14538 // hidden input element?
14539 // if the file is not an image...
14540 //then we need to use something other that and header_image
14545 xns : Roo.bootstrap,
14546 xtype : 'CardFooter',
14549 xns : Roo.bootstrap,
14555 xns : Roo.bootstrap,
14557 html : String.format("<small>{0}</small>", data.title),
14558 cls : 'col-10 text-left',
14563 click : function() {
14565 t.fireEvent( "download", t, data );
14571 xns : Roo.bootstrap,
14573 style: 'max-height: 28px; ',
14579 click : function() {
14580 t.removeCard(data.id)
14592 var cn = this.addxtype(
14595 xns : Roo.bootstrap,
14598 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14599 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14600 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14605 initEvents : function() {
14606 Roo.bootstrap.Card.prototype.initEvents.call(this);
14608 this.imgEl = this.el.select('.card-img-top').first();
14610 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14611 this.imgEl.set({ 'pointer' : 'cursor' });
14614 this.getCardFooter().addClass('p-1');
14621 // dont' really need ot update items.
14622 // this.items.push(cn);
14623 this.fileCollection.add(cn);
14625 if (!data.srcfile) {
14626 this.updateInput();
14631 var reader = new FileReader();
14632 reader.addEventListener("load", function() {
14633 data.srcdata = reader.result;
14636 reader.readAsDataURL(data.srcfile);
14641 removeCard : function(id)
14644 var card = this.fileCollection.get(id);
14645 card.data.is_deleted = 1;
14646 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14647 //this.fileCollection.remove(card);
14648 //this.items = this.items.filter(function(e) { return e != card });
14649 // dont' really need ot update items.
14650 card.el.dom.parentNode.removeChild(card.el.dom);
14651 this.updateInput();
14657 this.fileCollection.each(function(card) {
14658 if (card.el.dom && card.el.dom.parentNode) {
14659 card.el.dom.parentNode.removeChild(card.el.dom);
14662 this.fileCollection.clear();
14663 this.updateInput();
14666 updateInput : function()
14669 this.fileCollection.each(function(e) {
14673 this.inputEl().dom.value = JSON.stringify(data);
14683 Roo.bootstrap.form.CardUploader.ID = -1;/*
14685 * Ext JS Library 1.1.1
14686 * Copyright(c) 2006-2007, Ext JS, LLC.
14688 * Originally Released Under LGPL - original licence link has changed is not relivant.
14691 * <script type="text/javascript">
14696 * @class Roo.data.SortTypes
14698 * Defines the default sorting (casting?) comparison functions used when sorting data.
14700 Roo.data.SortTypes = {
14702 * Default sort that does nothing
14703 * @param {Mixed} s The value being converted
14704 * @return {Mixed} The comparison value
14706 none : function(s){
14711 * The regular expression used to strip tags
14715 stripTagsRE : /<\/?[^>]+>/gi,
14718 * Strips all HTML tags to sort on text only
14719 * @param {Mixed} s The value being converted
14720 * @return {String} The comparison value
14722 asText : function(s){
14723 return String(s).replace(this.stripTagsRE, "");
14727 * Strips all HTML tags to sort on text only - Case insensitive
14728 * @param {Mixed} s The value being converted
14729 * @return {String} The comparison value
14731 asUCText : function(s){
14732 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14736 * Case insensitive string
14737 * @param {Mixed} s The value being converted
14738 * @return {String} The comparison value
14740 asUCString : function(s) {
14741 return String(s).toUpperCase();
14746 * @param {Mixed} s The value being converted
14747 * @return {Number} The comparison value
14749 asDate : function(s) {
14753 if(s instanceof Date){
14754 return s.getTime();
14756 return Date.parse(String(s));
14761 * @param {Mixed} s The value being converted
14762 * @return {Float} The comparison value
14764 asFloat : function(s) {
14765 var val = parseFloat(String(s).replace(/,/g, ""));
14774 * @param {Mixed} s The value being converted
14775 * @return {Number} The comparison value
14777 asInt : function(s) {
14778 var val = parseInt(String(s).replace(/,/g, ""));
14786 * Ext JS Library 1.1.1
14787 * Copyright(c) 2006-2007, Ext JS, LLC.
14789 * Originally Released Under LGPL - original licence link has changed is not relivant.
14792 * <script type="text/javascript">
14796 * @class Roo.data.Record
14797 * Instances of this class encapsulate both record <em>definition</em> information, and record
14798 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14799 * to access Records cached in an {@link Roo.data.Store} object.<br>
14801 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14802 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14805 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14807 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14808 * {@link #create}. The parameters are the same.
14809 * @param {Array} data An associative Array of data values keyed by the field name.
14810 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14811 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14812 * not specified an integer id is generated.
14814 Roo.data.Record = function(data, id){
14815 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14820 * Generate a constructor for a specific record layout.
14821 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14822 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14823 * Each field definition object may contain the following properties: <ul>
14824 * <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,
14825 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14826 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14827 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14828 * is being used, then this is a string containing the javascript expression to reference the data relative to
14829 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14830 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14831 * this may be omitted.</p></li>
14832 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14833 * <ul><li>auto (Default, implies no conversion)</li>
14838 * <li>date</li></ul></p></li>
14839 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14840 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14841 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14842 * by the Reader into an object that will be stored in the Record. It is passed the
14843 * following parameters:<ul>
14844 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14846 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14848 * <br>usage:<br><pre><code>
14849 var TopicRecord = Roo.data.Record.create(
14850 {name: 'title', mapping: 'topic_title'},
14851 {name: 'author', mapping: 'username'},
14852 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14853 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14854 {name: 'lastPoster', mapping: 'user2'},
14855 {name: 'excerpt', mapping: 'post_text'}
14858 var myNewRecord = new TopicRecord({
14859 title: 'Do my job please',
14862 lastPost: new Date(),
14863 lastPoster: 'Animal',
14864 excerpt: 'No way dude!'
14866 myStore.add(myNewRecord);
14871 Roo.data.Record.create = function(o){
14872 var f = function(){
14873 f.superclass.constructor.apply(this, arguments);
14875 Roo.extend(f, Roo.data.Record);
14876 var p = f.prototype;
14877 p.fields = new Roo.util.MixedCollection(false, function(field){
14880 for(var i = 0, len = o.length; i < len; i++){
14881 p.fields.add(new Roo.data.Field(o[i]));
14883 f.getField = function(name){
14884 return p.fields.get(name);
14889 Roo.data.Record.AUTO_ID = 1000;
14890 Roo.data.Record.EDIT = 'edit';
14891 Roo.data.Record.REJECT = 'reject';
14892 Roo.data.Record.COMMIT = 'commit';
14894 Roo.data.Record.prototype = {
14896 * Readonly flag - true if this record has been modified.
14905 join : function(store){
14906 this.store = store;
14910 * Set the named field to the specified value.
14911 * @param {String} name The name of the field to set.
14912 * @param {Object} value The value to set the field to.
14914 set : function(name, value){
14915 if(this.data[name] == value){
14919 if(!this.modified){
14920 this.modified = {};
14922 if(typeof this.modified[name] == 'undefined'){
14923 this.modified[name] = this.data[name];
14925 this.data[name] = value;
14926 if(!this.editing && this.store){
14927 this.store.afterEdit(this);
14932 * Get the value of the named field.
14933 * @param {String} name The name of the field to get the value of.
14934 * @return {Object} The value of the field.
14936 get : function(name){
14937 return this.data[name];
14941 beginEdit : function(){
14942 this.editing = true;
14943 this.modified = {};
14947 cancelEdit : function(){
14948 this.editing = false;
14949 delete this.modified;
14953 endEdit : function(){
14954 this.editing = false;
14955 if(this.dirty && this.store){
14956 this.store.afterEdit(this);
14961 * Usually called by the {@link Roo.data.Store} which owns the Record.
14962 * Rejects all changes made to the Record since either creation, or the last commit operation.
14963 * Modified fields are reverted to their original values.
14965 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14966 * of reject operations.
14968 reject : function(){
14969 var m = this.modified;
14971 if(typeof m[n] != "function"){
14972 this.data[n] = m[n];
14975 this.dirty = false;
14976 delete this.modified;
14977 this.editing = false;
14979 this.store.afterReject(this);
14984 * Usually called by the {@link Roo.data.Store} which owns the Record.
14985 * Commits all changes made to the Record since either creation, or the last commit operation.
14987 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14988 * of commit operations.
14990 commit : function(){
14991 this.dirty = false;
14992 delete this.modified;
14993 this.editing = false;
14995 this.store.afterCommit(this);
15000 hasError : function(){
15001 return this.error != null;
15005 clearError : function(){
15010 * Creates a copy of this record.
15011 * @param {String} id (optional) A new record id if you don't want to use this record's id
15014 copy : function(newId) {
15015 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15019 * Ext JS Library 1.1.1
15020 * Copyright(c) 2006-2007, Ext JS, LLC.
15022 * Originally Released Under LGPL - original licence link has changed is not relivant.
15025 * <script type="text/javascript">
15031 * @class Roo.data.Store
15032 * @extends Roo.util.Observable
15033 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15034 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15036 * 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
15037 * has no knowledge of the format of the data returned by the Proxy.<br>
15039 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15040 * instances from the data object. These records are cached and made available through accessor functions.
15042 * Creates a new Store.
15043 * @param {Object} config A config object containing the objects needed for the Store to access data,
15044 * and read the data into Records.
15046 Roo.data.Store = function(config){
15047 this.data = new Roo.util.MixedCollection(false);
15048 this.data.getKey = function(o){
15051 this.baseParams = {};
15053 this.paramNames = {
15058 "multisort" : "_multisort"
15061 if(config && config.data){
15062 this.inlineData = config.data;
15063 delete config.data;
15066 Roo.apply(this, config);
15068 if(this.reader){ // reader passed
15069 this.reader = Roo.factory(this.reader, Roo.data);
15070 this.reader.xmodule = this.xmodule || false;
15071 if(!this.recordType){
15072 this.recordType = this.reader.recordType;
15074 if(this.reader.onMetaChange){
15075 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15079 if(this.recordType){
15080 this.fields = this.recordType.prototype.fields;
15082 this.modified = [];
15086 * @event datachanged
15087 * Fires when the data cache has changed, and a widget which is using this Store
15088 * as a Record cache should refresh its view.
15089 * @param {Store} this
15091 datachanged : true,
15093 * @event metachange
15094 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15095 * @param {Store} this
15096 * @param {Object} meta The JSON metadata
15101 * Fires when Records have been added to the Store
15102 * @param {Store} this
15103 * @param {Roo.data.Record[]} records The array of Records added
15104 * @param {Number} index The index at which the record(s) were added
15109 * Fires when a Record has been removed from the Store
15110 * @param {Store} this
15111 * @param {Roo.data.Record} record The Record that was removed
15112 * @param {Number} index The index at which the record was removed
15117 * Fires when a Record has been updated
15118 * @param {Store} this
15119 * @param {Roo.data.Record} record The Record that was updated
15120 * @param {String} operation The update operation being performed. Value may be one of:
15122 Roo.data.Record.EDIT
15123 Roo.data.Record.REJECT
15124 Roo.data.Record.COMMIT
15130 * Fires when the data cache has been cleared.
15131 * @param {Store} this
15135 * @event beforeload
15136 * Fires before a request is made for a new data object. If the beforeload handler returns false
15137 * the load action will be canceled.
15138 * @param {Store} this
15139 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15143 * @event beforeloadadd
15144 * Fires after a new set of Records has been loaded.
15145 * @param {Store} this
15146 * @param {Roo.data.Record[]} records The Records that were loaded
15147 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15149 beforeloadadd : true,
15152 * Fires after a new set of Records has been loaded, before they are added to the store.
15153 * @param {Store} this
15154 * @param {Roo.data.Record[]} records The Records that were loaded
15155 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15156 * @params {Object} return from reader
15160 * @event loadexception
15161 * Fires if an exception occurs in the Proxy during loading.
15162 * Called with the signature of the Proxy's "loadexception" event.
15163 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15166 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15167 * @param {Object} load options
15168 * @param {Object} jsonData from your request (normally this contains the Exception)
15170 loadexception : true
15174 this.proxy = Roo.factory(this.proxy, Roo.data);
15175 this.proxy.xmodule = this.xmodule || false;
15176 this.relayEvents(this.proxy, ["loadexception"]);
15178 this.sortToggle = {};
15179 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15181 Roo.data.Store.superclass.constructor.call(this);
15183 if(this.inlineData){
15184 this.loadData(this.inlineData);
15185 delete this.inlineData;
15189 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15191 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15192 * without a remote query - used by combo/forms at present.
15196 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15199 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15202 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15203 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15206 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15207 * on any HTTP request
15210 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15213 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15217 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15218 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15220 remoteSort : false,
15223 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15224 * loaded or when a record is removed. (defaults to false).
15226 pruneModifiedRecords : false,
15229 lastOptions : null,
15232 * Add Records to the Store and fires the add event.
15233 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15235 add : function(records){
15236 records = [].concat(records);
15237 for(var i = 0, len = records.length; i < len; i++){
15238 records[i].join(this);
15240 var index = this.data.length;
15241 this.data.addAll(records);
15242 this.fireEvent("add", this, records, index);
15246 * Remove a Record from the Store and fires the remove event.
15247 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15249 remove : function(record){
15250 var index = this.data.indexOf(record);
15251 this.data.removeAt(index);
15253 if(this.pruneModifiedRecords){
15254 this.modified.remove(record);
15256 this.fireEvent("remove", this, record, index);
15260 * Remove all Records from the Store and fires the clear event.
15262 removeAll : function(){
15264 if(this.pruneModifiedRecords){
15265 this.modified = [];
15267 this.fireEvent("clear", this);
15271 * Inserts Records to the Store at the given index and fires the add event.
15272 * @param {Number} index The start index at which to insert the passed Records.
15273 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15275 insert : function(index, records){
15276 records = [].concat(records);
15277 for(var i = 0, len = records.length; i < len; i++){
15278 this.data.insert(index, records[i]);
15279 records[i].join(this);
15281 this.fireEvent("add", this, records, index);
15285 * Get the index within the cache of the passed Record.
15286 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15287 * @return {Number} The index of the passed Record. Returns -1 if not found.
15289 indexOf : function(record){
15290 return this.data.indexOf(record);
15294 * Get the index within the cache of the Record with the passed id.
15295 * @param {String} id The id of the Record to find.
15296 * @return {Number} The index of the Record. Returns -1 if not found.
15298 indexOfId : function(id){
15299 return this.data.indexOfKey(id);
15303 * Get the Record with the specified id.
15304 * @param {String} id The id of the Record to find.
15305 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15307 getById : function(id){
15308 return this.data.key(id);
15312 * Get the Record at the specified index.
15313 * @param {Number} index The index of the Record to find.
15314 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15316 getAt : function(index){
15317 return this.data.itemAt(index);
15321 * Returns a range of Records between specified indices.
15322 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15323 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15324 * @return {Roo.data.Record[]} An array of Records
15326 getRange : function(start, end){
15327 return this.data.getRange(start, end);
15331 storeOptions : function(o){
15332 o = Roo.apply({}, o);
15335 this.lastOptions = o;
15339 * Loads the Record cache from the configured Proxy using the configured Reader.
15341 * If using remote paging, then the first load call must specify the <em>start</em>
15342 * and <em>limit</em> properties in the options.params property to establish the initial
15343 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15345 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15346 * and this call will return before the new data has been loaded. Perform any post-processing
15347 * in a callback function, or in a "load" event handler.</strong>
15349 * @param {Object} options An object containing properties which control loading options:<ul>
15350 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15351 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15352 * passed the following arguments:<ul>
15353 * <li>r : Roo.data.Record[]</li>
15354 * <li>options: Options object from the load call</li>
15355 * <li>success: Boolean success indicator</li></ul></li>
15356 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15357 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15360 load : function(options){
15361 options = options || {};
15362 if(this.fireEvent("beforeload", this, options) !== false){
15363 this.storeOptions(options);
15364 var p = Roo.apply(options.params || {}, this.baseParams);
15365 // if meta was not loaded from remote source.. try requesting it.
15366 if (!this.reader.metaFromRemote) {
15367 p._requestMeta = 1;
15369 if(this.sortInfo && this.remoteSort){
15370 var pn = this.paramNames;
15371 p[pn["sort"]] = this.sortInfo.field;
15372 p[pn["dir"]] = this.sortInfo.direction;
15374 if (this.multiSort) {
15375 var pn = this.paramNames;
15376 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15379 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15384 * Reloads the Record cache from the configured Proxy using the configured Reader and
15385 * the options from the last load operation performed.
15386 * @param {Object} options (optional) An object containing properties which may override the options
15387 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15388 * the most recently used options are reused).
15390 reload : function(options){
15391 this.load(Roo.applyIf(options||{}, this.lastOptions));
15395 // Called as a callback by the Reader during a load operation.
15396 loadRecords : function(o, options, success){
15399 if(success !== false){
15400 this.fireEvent("load", this, [], options, o);
15402 if(options.callback){
15403 options.callback.call(options.scope || this, [], options, false);
15407 // if data returned failure - throw an exception.
15408 if (o.success === false) {
15409 // show a message if no listener is registered.
15410 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15411 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15413 // loadmask wil be hooked into this..
15414 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15417 var r = o.records, t = o.totalRecords || r.length;
15419 this.fireEvent("beforeloadadd", this, r, options, o);
15421 if(!options || options.add !== true){
15422 if(this.pruneModifiedRecords){
15423 this.modified = [];
15425 for(var i = 0, len = r.length; i < len; i++){
15429 this.data = this.snapshot;
15430 delete this.snapshot;
15433 this.data.addAll(r);
15434 this.totalLength = t;
15436 this.fireEvent("datachanged", this);
15438 this.totalLength = Math.max(t, this.data.length+r.length);
15442 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15444 var e = new Roo.data.Record({});
15446 e.set(this.parent.displayField, this.parent.emptyTitle);
15447 e.set(this.parent.valueField, '');
15452 this.fireEvent("load", this, r, options, o);
15453 if(options.callback){
15454 options.callback.call(options.scope || this, r, options, true);
15460 * Loads data from a passed data block. A Reader which understands the format of the data
15461 * must have been configured in the constructor.
15462 * @param {Object} data The data block from which to read the Records. The format of the data expected
15463 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15464 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15466 loadData : function(o, append){
15467 var r = this.reader.readRecords(o);
15468 this.loadRecords(r, {add: append}, true);
15472 * using 'cn' the nested child reader read the child array into it's child stores.
15473 * @param {Object} rec The record with a 'children array
15475 loadDataFromChildren : function(rec)
15477 this.loadData(this.reader.toLoadData(rec));
15482 * Gets the number of cached records.
15484 * <em>If using paging, this may not be the total size of the dataset. If the data object
15485 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15486 * the data set size</em>
15488 getCount : function(){
15489 return this.data.length || 0;
15493 * Gets the total number of records in the dataset as returned by the server.
15495 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15496 * the dataset size</em>
15498 getTotalCount : function(){
15499 return this.totalLength || 0;
15503 * Returns the sort state of the Store as an object with two properties:
15505 field {String} The name of the field by which the Records are sorted
15506 direction {String} The sort order, "ASC" or "DESC"
15509 getSortState : function(){
15510 return this.sortInfo;
15514 applySort : function(){
15515 if(this.sortInfo && !this.remoteSort){
15516 var s = this.sortInfo, f = s.field;
15517 var st = this.fields.get(f).sortType;
15518 var fn = function(r1, r2){
15519 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15520 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15522 this.data.sort(s.direction, fn);
15523 if(this.snapshot && this.snapshot != this.data){
15524 this.snapshot.sort(s.direction, fn);
15530 * Sets the default sort column and order to be used by the next load operation.
15531 * @param {String} fieldName The name of the field to sort by.
15532 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15534 setDefaultSort : function(field, dir){
15535 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15539 * Sort the Records.
15540 * If remote sorting is used, the sort is performed on the server, and the cache is
15541 * reloaded. If local sorting is used, the cache is sorted internally.
15542 * @param {String} fieldName The name of the field to sort by.
15543 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15545 sort : function(fieldName, dir){
15546 var f = this.fields.get(fieldName);
15548 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15550 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15551 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15556 this.sortToggle[f.name] = dir;
15557 this.sortInfo = {field: f.name, direction: dir};
15558 if(!this.remoteSort){
15560 this.fireEvent("datachanged", this);
15562 this.load(this.lastOptions);
15567 * Calls the specified function for each of the Records in the cache.
15568 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15569 * Returning <em>false</em> aborts and exits the iteration.
15570 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15572 each : function(fn, scope){
15573 this.data.each(fn, scope);
15577 * Gets all records modified since the last commit. Modified records are persisted across load operations
15578 * (e.g., during paging).
15579 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15581 getModifiedRecords : function(){
15582 return this.modified;
15586 createFilterFn : function(property, value, anyMatch){
15587 if(!value.exec){ // not a regex
15588 value = String(value);
15589 if(value.length == 0){
15592 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15594 return function(r){
15595 return value.test(r.data[property]);
15600 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15601 * @param {String} property A field on your records
15602 * @param {Number} start The record index to start at (defaults to 0)
15603 * @param {Number} end The last record index to include (defaults to length - 1)
15604 * @return {Number} The sum
15606 sum : function(property, start, end){
15607 var rs = this.data.items, v = 0;
15608 start = start || 0;
15609 end = (end || end === 0) ? end : rs.length-1;
15611 for(var i = start; i <= end; i++){
15612 v += (rs[i].data[property] || 0);
15618 * Filter the records by a specified property.
15619 * @param {String} field A field on your records
15620 * @param {String/RegExp} value Either a string that the field
15621 * should start with or a RegExp to test against the field
15622 * @param {Boolean} anyMatch True to match any part not just the beginning
15624 filter : function(property, value, anyMatch){
15625 var fn = this.createFilterFn(property, value, anyMatch);
15626 return fn ? this.filterBy(fn) : this.clearFilter();
15630 * Filter by a function. The specified function will be called with each
15631 * record in this data source. If the function returns true the record is included,
15632 * otherwise it is filtered.
15633 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15634 * @param {Object} scope (optional) The scope of the function (defaults to this)
15636 filterBy : function(fn, scope){
15637 this.snapshot = this.snapshot || this.data;
15638 this.data = this.queryBy(fn, scope||this);
15639 this.fireEvent("datachanged", this);
15643 * Query the records by a specified property.
15644 * @param {String} field A field on your records
15645 * @param {String/RegExp} value Either a string that the field
15646 * should start with or a RegExp to test against the field
15647 * @param {Boolean} anyMatch True to match any part not just the beginning
15648 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15650 query : function(property, value, anyMatch){
15651 var fn = this.createFilterFn(property, value, anyMatch);
15652 return fn ? this.queryBy(fn) : this.data.clone();
15656 * Query by a function. The specified function will be called with each
15657 * record in this data source. If the function returns true the record is included
15659 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15660 * @param {Object} scope (optional) The scope of the function (defaults to this)
15661 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15663 queryBy : function(fn, scope){
15664 var data = this.snapshot || this.data;
15665 return data.filterBy(fn, scope||this);
15669 * Collects unique values for a particular dataIndex from this store.
15670 * @param {String} dataIndex The property to collect
15671 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15672 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15673 * @return {Array} An array of the unique values
15675 collect : function(dataIndex, allowNull, bypassFilter){
15676 var d = (bypassFilter === true && this.snapshot) ?
15677 this.snapshot.items : this.data.items;
15678 var v, sv, r = [], l = {};
15679 for(var i = 0, len = d.length; i < len; i++){
15680 v = d[i].data[dataIndex];
15682 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15691 * Revert to a view of the Record cache with no filtering applied.
15692 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15694 clearFilter : function(suppressEvent){
15695 if(this.snapshot && this.snapshot != this.data){
15696 this.data = this.snapshot;
15697 delete this.snapshot;
15698 if(suppressEvent !== true){
15699 this.fireEvent("datachanged", this);
15705 afterEdit : function(record){
15706 if(this.modified.indexOf(record) == -1){
15707 this.modified.push(record);
15709 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15713 afterReject : function(record){
15714 this.modified.remove(record);
15715 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15719 afterCommit : function(record){
15720 this.modified.remove(record);
15721 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15725 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15726 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15728 commitChanges : function(){
15729 var m = this.modified.slice(0);
15730 this.modified = [];
15731 for(var i = 0, len = m.length; i < len; i++){
15737 * Cancel outstanding changes on all changed records.
15739 rejectChanges : function(){
15740 var m = this.modified.slice(0);
15741 this.modified = [];
15742 for(var i = 0, len = m.length; i < len; i++){
15747 onMetaChange : function(meta, rtype, o){
15748 this.recordType = rtype;
15749 this.fields = rtype.prototype.fields;
15750 delete this.snapshot;
15751 this.sortInfo = meta.sortInfo || this.sortInfo;
15752 this.modified = [];
15753 this.fireEvent('metachange', this, this.reader.meta);
15756 moveIndex : function(data, type)
15758 var index = this.indexOf(data);
15760 var newIndex = index + type;
15764 this.insert(newIndex, data);
15769 * Ext JS Library 1.1.1
15770 * Copyright(c) 2006-2007, Ext JS, LLC.
15772 * Originally Released Under LGPL - original licence link has changed is not relivant.
15775 * <script type="text/javascript">
15779 * @class Roo.data.SimpleStore
15780 * @extends Roo.data.Store
15781 * Small helper class to make creating Stores from Array data easier.
15782 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15783 * @cfg {Array} fields An array of field definition objects, or field name strings.
15784 * @cfg {Object} an existing reader (eg. copied from another store)
15785 * @cfg {Array} data The multi-dimensional array of data
15786 * @cfg {Roo.data.DataProxy} proxy [not-required]
15787 * @cfg {Roo.data.Reader} reader [not-required]
15789 * @param {Object} config
15791 Roo.data.SimpleStore = function(config)
15793 Roo.data.SimpleStore.superclass.constructor.call(this, {
15795 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15798 Roo.data.Record.create(config.fields)
15800 proxy : new Roo.data.MemoryProxy(config.data)
15804 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15806 * Ext JS Library 1.1.1
15807 * Copyright(c) 2006-2007, Ext JS, LLC.
15809 * Originally Released Under LGPL - original licence link has changed is not relivant.
15812 * <script type="text/javascript">
15817 * @extends Roo.data.Store
15818 * @class Roo.data.JsonStore
15819 * Small helper class to make creating Stores for JSON data easier. <br/>
15821 var store = new Roo.data.JsonStore({
15822 url: 'get-images.php',
15824 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15827 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15828 * JsonReader and HttpProxy (unless inline data is provided).</b>
15829 * @cfg {Array} fields An array of field definition objects, or field name strings.
15831 * @param {Object} config
15833 Roo.data.JsonStore = function(c){
15834 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15835 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15836 reader: new Roo.data.JsonReader(c, c.fields)
15839 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15841 * Ext JS Library 1.1.1
15842 * Copyright(c) 2006-2007, Ext JS, LLC.
15844 * Originally Released Under LGPL - original licence link has changed is not relivant.
15847 * <script type="text/javascript">
15851 Roo.data.Field = function(config){
15852 if(typeof config == "string"){
15853 config = {name: config};
15855 Roo.apply(this, config);
15858 this.type = "auto";
15861 var st = Roo.data.SortTypes;
15862 // named sortTypes are supported, here we look them up
15863 if(typeof this.sortType == "string"){
15864 this.sortType = st[this.sortType];
15867 // set default sortType for strings and dates
15868 if(!this.sortType){
15871 this.sortType = st.asUCString;
15874 this.sortType = st.asDate;
15877 this.sortType = st.none;
15882 var stripRe = /[\$,%]/g;
15884 // prebuilt conversion function for this field, instead of
15885 // switching every time we're reading a value
15887 var cv, dateFormat = this.dateFormat;
15892 cv = function(v){ return v; };
15895 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15899 return v !== undefined && v !== null && v !== '' ?
15900 parseInt(String(v).replace(stripRe, ""), 10) : '';
15905 return v !== undefined && v !== null && v !== '' ?
15906 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15911 cv = function(v){ return v === true || v === "true" || v == 1; };
15918 if(v instanceof Date){
15922 if(dateFormat == "timestamp"){
15923 return new Date(v*1000);
15925 return Date.parseDate(v, dateFormat);
15927 var parsed = Date.parse(v);
15928 return parsed ? new Date(parsed) : null;
15937 Roo.data.Field.prototype = {
15945 * Ext JS Library 1.1.1
15946 * Copyright(c) 2006-2007, Ext JS, LLC.
15948 * Originally Released Under LGPL - original licence link has changed is not relivant.
15951 * <script type="text/javascript">
15954 // Base class for reading structured data from a data source. This class is intended to be
15955 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15958 * @class Roo.data.DataReader
15960 * Base class for reading structured data from a data source. This class is intended to be
15961 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15964 Roo.data.DataReader = function(meta, recordType){
15968 this.recordType = recordType instanceof Array ?
15969 Roo.data.Record.create(recordType) : recordType;
15972 Roo.data.DataReader.prototype = {
15975 readerType : 'Data',
15977 * Create an empty record
15978 * @param {Object} data (optional) - overlay some values
15979 * @return {Roo.data.Record} record created.
15981 newRow : function(d) {
15983 this.recordType.prototype.fields.each(function(c) {
15985 case 'int' : da[c.name] = 0; break;
15986 case 'date' : da[c.name] = new Date(); break;
15987 case 'float' : da[c.name] = 0.0; break;
15988 case 'boolean' : da[c.name] = false; break;
15989 default : da[c.name] = ""; break;
15993 return new this.recordType(Roo.apply(da, d));
15999 * Ext JS Library 1.1.1
16000 * Copyright(c) 2006-2007, Ext JS, LLC.
16002 * Originally Released Under LGPL - original licence link has changed is not relivant.
16005 * <script type="text/javascript">
16009 * @class Roo.data.DataProxy
16010 * @extends Roo.util.Observable
16012 * This class is an abstract base class for implementations which provide retrieval of
16013 * unformatted data objects.<br>
16015 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16016 * (of the appropriate type which knows how to parse the data object) to provide a block of
16017 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16019 * Custom implementations must implement the load method as described in
16020 * {@link Roo.data.HttpProxy#load}.
16022 Roo.data.DataProxy = function(){
16025 * @event beforeload
16026 * Fires before a network request is made to retrieve a data object.
16027 * @param {Object} This DataProxy object.
16028 * @param {Object} params The params parameter to the load function.
16033 * Fires before the load method's callback is called.
16034 * @param {Object} This DataProxy object.
16035 * @param {Object} o The data object.
16036 * @param {Object} arg The callback argument object passed to the load function.
16040 * @event loadexception
16041 * Fires if an Exception occurs during data retrieval.
16042 * @param {Object} This DataProxy object.
16043 * @param {Object} o The data object.
16044 * @param {Object} arg The callback argument object passed to the load function.
16045 * @param {Object} e The Exception.
16047 loadexception : true
16049 Roo.data.DataProxy.superclass.constructor.call(this);
16052 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16055 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16059 * Ext JS Library 1.1.1
16060 * Copyright(c) 2006-2007, Ext JS, LLC.
16062 * Originally Released Under LGPL - original licence link has changed is not relivant.
16065 * <script type="text/javascript">
16068 * @class Roo.data.MemoryProxy
16069 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16070 * to the Reader when its load method is called.
16072 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16074 Roo.data.MemoryProxy = function(data){
16078 Roo.data.MemoryProxy.superclass.constructor.call(this);
16082 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16085 * Load data from the requested source (in this case an in-memory
16086 * data object passed to the constructor), read the data object into
16087 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16088 * process that block using the passed callback.
16089 * @param {Object} params This parameter is not used by the MemoryProxy class.
16090 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16091 * object into a block of Roo.data.Records.
16092 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16093 * The function must be passed <ul>
16094 * <li>The Record block object</li>
16095 * <li>The "arg" argument from the load function</li>
16096 * <li>A boolean success indicator</li>
16098 * @param {Object} scope The scope in which to call the callback
16099 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16101 load : function(params, reader, callback, scope, arg){
16102 params = params || {};
16105 result = reader.readRecords(params.data ? params.data :this.data);
16107 this.fireEvent("loadexception", this, arg, null, e);
16108 callback.call(scope, null, arg, false);
16111 callback.call(scope, result, arg, true);
16115 update : function(params, records){
16120 * Ext JS Library 1.1.1
16121 * Copyright(c) 2006-2007, Ext JS, LLC.
16123 * Originally Released Under LGPL - original licence link has changed is not relivant.
16126 * <script type="text/javascript">
16129 * @class Roo.data.HttpProxy
16130 * @extends Roo.data.DataProxy
16131 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16132 * configured to reference a certain URL.<br><br>
16134 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16135 * from which the running page was served.<br><br>
16137 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16139 * Be aware that to enable the browser to parse an XML document, the server must set
16140 * the Content-Type header in the HTTP response to "text/xml".
16142 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16143 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16144 * will be used to make the request.
16146 Roo.data.HttpProxy = function(conn){
16147 Roo.data.HttpProxy.superclass.constructor.call(this);
16148 // is conn a conn config or a real conn?
16150 this.useAjax = !conn || !conn.events;
16154 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16155 // thse are take from connection...
16158 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16161 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16162 * extra parameters to each request made by this object. (defaults to undefined)
16165 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16166 * to each request made by this object. (defaults to undefined)
16169 * @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)
16172 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16175 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16181 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16185 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16186 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16187 * a finer-grained basis than the DataProxy events.
16189 getConnection : function(){
16190 return this.useAjax ? Roo.Ajax : this.conn;
16194 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16195 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16196 * process that block using the passed callback.
16197 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16198 * for the request to the remote server.
16199 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16200 * object into a block of Roo.data.Records.
16201 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16202 * The function must be passed <ul>
16203 * <li>The Record block object</li>
16204 * <li>The "arg" argument from the load function</li>
16205 * <li>A boolean success indicator</li>
16207 * @param {Object} scope The scope in which to call the callback
16208 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16210 load : function(params, reader, callback, scope, arg){
16211 if(this.fireEvent("beforeload", this, params) !== false){
16213 params : params || {},
16215 callback : callback,
16220 callback : this.loadResponse,
16224 Roo.applyIf(o, this.conn);
16225 if(this.activeRequest){
16226 Roo.Ajax.abort(this.activeRequest);
16228 this.activeRequest = Roo.Ajax.request(o);
16230 this.conn.request(o);
16233 callback.call(scope||this, null, arg, false);
16238 loadResponse : function(o, success, response){
16239 delete this.activeRequest;
16241 this.fireEvent("loadexception", this, o, response);
16242 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16247 result = o.reader.read(response);
16250 o.raw = { errorMsg : response.responseText };
16251 this.fireEvent("loadexception", this, o, response, e);
16252 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16256 this.fireEvent("load", this, o, o.request.arg);
16257 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16261 update : function(dataSet){
16266 updateResponse : function(dataSet){
16271 * Ext JS Library 1.1.1
16272 * Copyright(c) 2006-2007, Ext JS, LLC.
16274 * Originally Released Under LGPL - original licence link has changed is not relivant.
16277 * <script type="text/javascript">
16281 * @class Roo.data.ScriptTagProxy
16282 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16283 * other than the originating domain of the running page.<br><br>
16285 * <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
16286 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16288 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16289 * source code that is used as the source inside a <script> tag.<br><br>
16291 * In order for the browser to process the returned data, the server must wrap the data object
16292 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16293 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16294 * depending on whether the callback name was passed:
16297 boolean scriptTag = false;
16298 String cb = request.getParameter("callback");
16301 response.setContentType("text/javascript");
16303 response.setContentType("application/x-json");
16305 Writer out = response.getWriter();
16307 out.write(cb + "(");
16309 out.print(dataBlock.toJsonString());
16316 * @param {Object} config A configuration object.
16318 Roo.data.ScriptTagProxy = function(config){
16319 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16320 Roo.apply(this, config);
16321 this.head = document.getElementsByTagName("head")[0];
16324 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16326 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16328 * @cfg {String} url The URL from which to request the data object.
16331 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16335 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16336 * the server the name of the callback function set up by the load call to process the returned data object.
16337 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16338 * javascript output which calls this named function passing the data object as its only parameter.
16340 callbackParam : "callback",
16342 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16343 * name to the request.
16348 * Load data from the configured URL, read the data object into
16349 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16350 * process that block using the passed callback.
16351 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16352 * for the request to the remote server.
16353 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16354 * object into a block of Roo.data.Records.
16355 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16356 * The function must be passed <ul>
16357 * <li>The Record block object</li>
16358 * <li>The "arg" argument from the load function</li>
16359 * <li>A boolean success indicator</li>
16361 * @param {Object} scope The scope in which to call the callback
16362 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16364 load : function(params, reader, callback, scope, arg){
16365 if(this.fireEvent("beforeload", this, params) !== false){
16367 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16369 var url = this.url;
16370 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16372 url += "&_dc=" + (new Date().getTime());
16374 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16377 cb : "stcCallback"+transId,
16378 scriptId : "stcScript"+transId,
16382 callback : callback,
16388 window[trans.cb] = function(o){
16389 conn.handleResponse(o, trans);
16392 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16394 if(this.autoAbort !== false){
16398 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16400 var script = document.createElement("script");
16401 script.setAttribute("src", url);
16402 script.setAttribute("type", "text/javascript");
16403 script.setAttribute("id", trans.scriptId);
16404 this.head.appendChild(script);
16406 this.trans = trans;
16408 callback.call(scope||this, null, arg, false);
16413 isLoading : function(){
16414 return this.trans ? true : false;
16418 * Abort the current server request.
16420 abort : function(){
16421 if(this.isLoading()){
16422 this.destroyTrans(this.trans);
16427 destroyTrans : function(trans, isLoaded){
16428 this.head.removeChild(document.getElementById(trans.scriptId));
16429 clearTimeout(trans.timeoutId);
16431 window[trans.cb] = undefined;
16433 delete window[trans.cb];
16436 // if hasn't been loaded, wait for load to remove it to prevent script error
16437 window[trans.cb] = function(){
16438 window[trans.cb] = undefined;
16440 delete window[trans.cb];
16447 handleResponse : function(o, trans){
16448 this.trans = false;
16449 this.destroyTrans(trans, true);
16452 result = trans.reader.readRecords(o);
16454 this.fireEvent("loadexception", this, o, trans.arg, e);
16455 trans.callback.call(trans.scope||window, null, trans.arg, false);
16458 this.fireEvent("load", this, o, trans.arg);
16459 trans.callback.call(trans.scope||window, result, trans.arg, true);
16463 handleFailure : function(trans){
16464 this.trans = false;
16465 this.destroyTrans(trans, false);
16466 this.fireEvent("loadexception", this, null, trans.arg);
16467 trans.callback.call(trans.scope||window, null, trans.arg, false);
16471 * Ext JS Library 1.1.1
16472 * Copyright(c) 2006-2007, Ext JS, LLC.
16474 * Originally Released Under LGPL - original licence link has changed is not relivant.
16477 * <script type="text/javascript">
16481 * @class Roo.data.JsonReader
16482 * @extends Roo.data.DataReader
16483 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16484 * based on mappings in a provided Roo.data.Record constructor.
16486 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16487 * in the reply previously.
16492 var RecordDef = Roo.data.Record.create([
16493 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16494 {name: 'occupation'} // This field will use "occupation" as the mapping.
16496 var myReader = new Roo.data.JsonReader({
16497 totalProperty: "results", // The property which contains the total dataset size (optional)
16498 root: "rows", // The property which contains an Array of row objects
16499 id: "id" // The property within each row object that provides an ID for the record (optional)
16503 * This would consume a JSON file like this:
16505 { 'results': 2, 'rows': [
16506 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16507 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16510 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16511 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16512 * paged from the remote server.
16513 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16514 * @cfg {String} root name of the property which contains the Array of row objects.
16515 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16516 * @cfg {Array} fields Array of field definition objects
16518 * Create a new JsonReader
16519 * @param {Object} meta Metadata configuration options
16520 * @param {Object} recordType Either an Array of field definition objects,
16521 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16523 Roo.data.JsonReader = function(meta, recordType){
16526 // set some defaults:
16527 Roo.applyIf(meta, {
16528 totalProperty: 'total',
16529 successProperty : 'success',
16534 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16536 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16538 readerType : 'Json',
16541 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16542 * Used by Store query builder to append _requestMeta to params.
16545 metaFromRemote : false,
16547 * This method is only used by a DataProxy which has retrieved data from a remote server.
16548 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16549 * @return {Object} data A data block which is used by an Roo.data.Store object as
16550 * a cache of Roo.data.Records.
16552 read : function(response){
16553 var json = response.responseText;
16555 var o = /* eval:var:o */ eval("("+json+")");
16557 throw {message: "JsonReader.read: Json object not found"};
16563 this.metaFromRemote = true;
16564 this.meta = o.metaData;
16565 this.recordType = Roo.data.Record.create(o.metaData.fields);
16566 this.onMetaChange(this.meta, this.recordType, o);
16568 return this.readRecords(o);
16571 // private function a store will implement
16572 onMetaChange : function(meta, recordType, o){
16579 simpleAccess: function(obj, subsc) {
16586 getJsonAccessor: function(){
16588 return function(expr) {
16590 return(re.test(expr))
16591 ? new Function("obj", "return obj." + expr)
16596 return Roo.emptyFn;
16601 * Create a data block containing Roo.data.Records from an XML document.
16602 * @param {Object} o An object which contains an Array of row objects in the property specified
16603 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16604 * which contains the total size of the dataset.
16605 * @return {Object} data A data block which is used by an Roo.data.Store object as
16606 * a cache of Roo.data.Records.
16608 readRecords : function(o){
16610 * After any data loads, the raw JSON data is available for further custom processing.
16614 var s = this.meta, Record = this.recordType,
16615 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16617 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16619 if(s.totalProperty) {
16620 this.getTotal = this.getJsonAccessor(s.totalProperty);
16622 if(s.successProperty) {
16623 this.getSuccess = this.getJsonAccessor(s.successProperty);
16625 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16627 var g = this.getJsonAccessor(s.id);
16628 this.getId = function(rec) {
16630 return (r === undefined || r === "") ? null : r;
16633 this.getId = function(){return null;};
16636 for(var jj = 0; jj < fl; jj++){
16638 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16639 this.ef[jj] = this.getJsonAccessor(map);
16643 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16644 if(s.totalProperty){
16645 var vt = parseInt(this.getTotal(o), 10);
16650 if(s.successProperty){
16651 var vs = this.getSuccess(o);
16652 if(vs === false || vs === 'false'){
16657 for(var i = 0; i < c; i++){
16660 var id = this.getId(n);
16661 for(var j = 0; j < fl; j++){
16663 var v = this.ef[j](n);
16665 Roo.log('missing convert for ' + f.name);
16669 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16673 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16679 var record = new Record(values, id);
16681 records[i] = record;
16687 totalRecords : totalRecords
16690 // used when loading children.. @see loadDataFromChildren
16691 toLoadData: function(rec)
16693 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16694 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16695 return { data : data, total : data.length };
16700 * Ext JS Library 1.1.1
16701 * Copyright(c) 2006-2007, Ext JS, LLC.
16703 * Originally Released Under LGPL - original licence link has changed is not relivant.
16706 * <script type="text/javascript">
16710 * @class Roo.data.ArrayReader
16711 * @extends Roo.data.DataReader
16712 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16713 * Each element of that Array represents a row of data fields. The
16714 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16715 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16719 var RecordDef = Roo.data.Record.create([
16720 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16721 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16723 var myReader = new Roo.data.ArrayReader({
16724 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16728 * This would consume an Array like this:
16730 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16734 * Create a new JsonReader
16735 * @param {Object} meta Metadata configuration options.
16736 * @param {Object|Array} recordType Either an Array of field definition objects
16738 * @cfg {Array} fields Array of field definition objects
16739 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16740 * as specified to {@link Roo.data.Record#create},
16741 * or an {@link Roo.data.Record} object
16744 * created using {@link Roo.data.Record#create}.
16746 Roo.data.ArrayReader = function(meta, recordType)
16748 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16751 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16754 * Create a data block containing Roo.data.Records from an XML document.
16755 * @param {Object} o An Array of row objects which represents the dataset.
16756 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16757 * a cache of Roo.data.Records.
16759 readRecords : function(o)
16761 var sid = this.meta ? this.meta.id : null;
16762 var recordType = this.recordType, fields = recordType.prototype.fields;
16765 for(var i = 0; i < root.length; i++){
16768 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16769 for(var j = 0, jlen = fields.length; j < jlen; j++){
16770 var f = fields.items[j];
16771 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16772 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16774 values[f.name] = v;
16776 var record = new recordType(values, id);
16778 records[records.length] = record;
16782 totalRecords : records.length
16785 // used when loading children.. @see loadDataFromChildren
16786 toLoadData: function(rec)
16788 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16789 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16800 * @class Roo.bootstrap.form.ComboBox
16801 * @extends Roo.bootstrap.form.TriggerField
16802 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16803 * @cfg {Boolean} append (true|false) default false
16804 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16805 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16806 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16807 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16808 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16809 * @cfg {Boolean} animate default true
16810 * @cfg {Boolean} emptyResultText only for touch device
16811 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16812 * @cfg {String} emptyTitle default ''
16813 * @cfg {Number} width fixed with? experimental
16815 * Create a new ComboBox.
16816 * @param {Object} config Configuration options
16818 Roo.bootstrap.form.ComboBox = function(config){
16819 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16823 * Fires when the dropdown list is expanded
16824 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16829 * Fires when the dropdown list is collapsed
16830 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16834 * @event beforeselect
16835 * Fires before a list item is selected. Return false to cancel the selection.
16836 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16837 * @param {Roo.data.Record} record The data record returned from the underlying store
16838 * @param {Number} index The index of the selected item in the dropdown list
16840 'beforeselect' : true,
16843 * Fires when a list item is selected
16844 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16845 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16846 * @param {Number} index The index of the selected item in the dropdown list
16850 * @event beforequery
16851 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16852 * The event object passed has these properties:
16853 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854 * @param {String} query The query
16855 * @param {Boolean} forceAll true to force "all" query
16856 * @param {Boolean} cancel true to cancel the query
16857 * @param {Object} e The query event object
16859 'beforequery': true,
16862 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16863 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16869 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16875 * Fires when the remove value from the combobox array
16876 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880 * @event afterremove
16881 * Fires when the remove value from the combobox array
16882 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16884 'afterremove' : true,
16886 * @event specialfilter
16887 * Fires when specialfilter
16888 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890 'specialfilter' : true,
16893 * Fires when tick the element
16894 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898 * @event touchviewdisplay
16899 * Fires when touch view require special display (default is using displayField)
16900 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901 * @param {Object} cfg set html .
16903 'touchviewdisplay' : true
16908 this.tickItems = [];
16910 this.selectedIndex = -1;
16911 if(this.mode == 'local'){
16912 if(config.queryDelay === undefined){
16913 this.queryDelay = 10;
16915 if(config.minChars === undefined){
16921 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16924 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16925 * rendering into an Roo.Editor, defaults to false)
16928 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16929 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16932 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16935 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16936 * the dropdown list (defaults to undefined, with no header element)
16940 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16944 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16946 listWidth: undefined,
16948 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16949 * mode = 'remote' or 'text' if mode = 'local')
16951 displayField: undefined,
16954 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16955 * mode = 'remote' or 'value' if mode = 'local').
16956 * Note: use of a valueField requires the user make a selection
16957 * in order for a value to be mapped.
16959 valueField: undefined,
16961 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16966 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16967 * field's data value (defaults to the underlying DOM element's name)
16969 hiddenName: undefined,
16971 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16975 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16977 selectedClass: 'active',
16980 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16984 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16985 * anchor positions (defaults to 'tl-bl')
16987 listAlign: 'tl-bl?',
16989 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16993 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16994 * query specified by the allQuery config option (defaults to 'query')
16996 triggerAction: 'query',
16998 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16999 * (defaults to 4, does not apply if editable = false)
17003 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17004 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17008 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17009 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17013 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17014 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17018 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17019 * when editable = true (defaults to false)
17021 selectOnFocus:false,
17023 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17025 queryParam: 'query',
17027 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17028 * when mode = 'remote' (defaults to 'Loading...')
17030 loadingText: 'Loading...',
17032 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17036 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17040 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17041 * traditional select (defaults to true)
17045 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17049 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17053 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17054 * listWidth has a higher value)
17058 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17059 * allow the user to set arbitrary text into the field (defaults to false)
17061 forceSelection:false,
17063 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17064 * if typeAhead = true (defaults to 250)
17066 typeAheadDelay : 250,
17068 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17069 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17071 valueNotFoundText : undefined,
17073 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17075 blockFocus : false,
17078 * @cfg {Boolean} disableClear Disable showing of clear button.
17080 disableClear : false,
17082 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17084 alwaysQuery : false,
17087 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17092 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17094 invalidClass : "has-warning",
17097 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17099 validClass : "has-success",
17102 * @cfg {Boolean} specialFilter (true|false) special filter default false
17104 specialFilter : false,
17107 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17109 mobileTouchView : true,
17112 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17114 useNativeIOS : false,
17117 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17119 mobile_restrict_height : false,
17121 ios_options : false,
17133 btnPosition : 'right',
17134 triggerList : true,
17135 showToggleBtn : true,
17137 emptyResultText: 'Empty',
17138 triggerText : 'Select',
17142 // element that contains real text value.. (when hidden is used..)
17144 getAutoCreate : function()
17149 * Render classic select for iso
17152 if(Roo.isIOS && this.useNativeIOS){
17153 cfg = this.getAutoCreateNativeIOS();
17161 if(Roo.isTouch && this.mobileTouchView){
17162 cfg = this.getAutoCreateTouchView();
17169 if(!this.tickable){
17170 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17175 * ComboBox with tickable selections
17178 var align = this.labelAlign || this.parentLabelAlign();
17181 cls : 'form-group roo-combobox-tickable' //input-group
17184 var btn_text_select = '';
17185 var btn_text_done = '';
17186 var btn_text_cancel = '';
17188 if (this.btn_text_show) {
17189 btn_text_select = 'Select';
17190 btn_text_done = 'Done';
17191 btn_text_cancel = 'Cancel';
17196 cls : 'tickable-buttons',
17201 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17202 //html : this.triggerText
17203 html: btn_text_select
17209 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17211 html: btn_text_done
17217 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17219 html: btn_text_cancel
17225 buttons.cn.unshift({
17227 cls: 'roo-select2-search-field-input'
17233 Roo.each(buttons.cn, function(c){
17235 c.cls += ' btn-' + _this.size;
17238 if (_this.disabled) {
17245 style : 'display: contents',
17250 cls: 'form-hidden-field'
17254 cls: 'roo-select2-choices',
17258 cls: 'roo-select2-search-field',
17269 cls: 'roo-select2-container input-group roo-select2-container-multi',
17275 // cls: 'typeahead typeahead-long dropdown-menu',
17276 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17281 if(this.hasFeedback && !this.allowBlank){
17285 cls: 'glyphicon form-control-feedback'
17288 combobox.cn.push(feedback);
17295 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17296 tooltip : 'This field is required'
17298 if (Roo.bootstrap.version == 4) {
17301 style : 'display:none'
17304 if (align ==='left' && this.fieldLabel.length) {
17306 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17313 cls : 'control-label col-form-label',
17314 html : this.fieldLabel
17326 var labelCfg = cfg.cn[1];
17327 var contentCfg = cfg.cn[2];
17330 if(this.indicatorpos == 'right'){
17336 cls : 'control-label col-form-label',
17340 html : this.fieldLabel
17356 labelCfg = cfg.cn[0];
17357 contentCfg = cfg.cn[1];
17361 if(this.labelWidth > 12){
17362 labelCfg.style = "width: " + this.labelWidth + 'px';
17364 if(this.width * 1 > 0){
17365 contentCfg.style = "width: " + this.width + 'px';
17367 if(this.labelWidth < 13 && this.labelmd == 0){
17368 this.labelmd = this.labelWidth;
17371 if(this.labellg > 0){
17372 labelCfg.cls += ' col-lg-' + this.labellg;
17373 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17376 if(this.labelmd > 0){
17377 labelCfg.cls += ' col-md-' + this.labelmd;
17378 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17381 if(this.labelsm > 0){
17382 labelCfg.cls += ' col-sm-' + this.labelsm;
17383 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17386 if(this.labelxs > 0){
17387 labelCfg.cls += ' col-xs-' + this.labelxs;
17388 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17392 } else if ( this.fieldLabel.length) {
17393 // Roo.log(" label");
17398 //cls : 'input-group-addon',
17399 html : this.fieldLabel
17404 if(this.indicatorpos == 'right'){
17408 //cls : 'input-group-addon',
17409 html : this.fieldLabel
17419 // Roo.log(" no label && no align");
17426 ['xs','sm','md','lg'].map(function(size){
17427 if (settings[size]) {
17428 cfg.cls += ' col-' + size + '-' + settings[size];
17436 _initEventsCalled : false,
17439 initEvents: function()
17441 if (this._initEventsCalled) { // as we call render... prevent looping...
17444 this._initEventsCalled = true;
17447 throw "can not find store for combo";
17450 this.indicator = this.indicatorEl();
17452 this.store = Roo.factory(this.store, Roo.data);
17453 this.store.parent = this;
17455 // if we are building from html. then this element is so complex, that we can not really
17456 // use the rendered HTML.
17457 // so we have to trash and replace the previous code.
17458 if (Roo.XComponent.build_from_html) {
17459 // remove this element....
17460 var e = this.el.dom, k=0;
17461 while (e ) { e = e.previousSibling; ++k;}
17466 this.rendered = false;
17468 this.render(this.parent().getChildContainer(true), k);
17471 if(Roo.isIOS && this.useNativeIOS){
17472 this.initIOSView();
17480 if(Roo.isTouch && this.mobileTouchView){
17481 this.initTouchView();
17486 this.initTickableEvents();
17490 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17492 if(this.hiddenName){
17494 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17496 this.hiddenField.dom.value =
17497 this.hiddenValue !== undefined ? this.hiddenValue :
17498 this.value !== undefined ? this.value : '';
17500 // prevent input submission
17501 this.el.dom.removeAttribute('name');
17502 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17507 // this.el.dom.setAttribute('autocomplete', 'off');
17510 var cls = 'x-combo-list';
17512 //this.list = new Roo.Layer({
17513 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17519 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17520 _this.list.setWidth(lw);
17523 this.list.on('mouseover', this.onViewOver, this);
17524 this.list.on('mousemove', this.onViewMove, this);
17525 this.list.on('scroll', this.onViewScroll, this);
17528 this.list.swallowEvent('mousewheel');
17529 this.assetHeight = 0;
17532 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17533 this.assetHeight += this.header.getHeight();
17536 this.innerList = this.list.createChild({cls:cls+'-inner'});
17537 this.innerList.on('mouseover', this.onViewOver, this);
17538 this.innerList.on('mousemove', this.onViewMove, this);
17539 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17541 if(this.allowBlank && !this.pageSize && !this.disableClear){
17542 this.footer = this.list.createChild({cls:cls+'-ft'});
17543 this.pageTb = new Roo.Toolbar(this.footer);
17547 this.footer = this.list.createChild({cls:cls+'-ft'});
17548 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17549 {pageSize: this.pageSize});
17553 if (this.pageTb && this.allowBlank && !this.disableClear) {
17555 this.pageTb.add(new Roo.Toolbar.Fill(), {
17556 cls: 'x-btn-icon x-btn-clear',
17558 handler: function()
17561 _this.clearValue();
17562 _this.onSelect(false, -1);
17567 this.assetHeight += this.footer.getHeight();
17572 this.tpl = Roo.bootstrap.version == 4 ?
17573 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17574 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17577 this.view = new Roo.View(this.list, this.tpl, {
17578 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17580 //this.view.wrapEl.setDisplayed(false);
17581 this.view.on('click', this.onViewClick, this);
17584 this.store.on('beforeload', this.onBeforeLoad, this);
17585 this.store.on('load', this.onLoad, this);
17586 this.store.on('loadexception', this.onLoadException, this);
17588 if(this.resizable){
17589 this.resizer = new Roo.Resizable(this.list, {
17590 pinned:true, handles:'se'
17592 this.resizer.on('resize', function(r, w, h){
17593 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17594 this.listWidth = w;
17595 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17596 this.restrictHeight();
17598 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17601 if(!this.editable){
17602 this.editable = true;
17603 this.setEditable(false);
17608 if (typeof(this.events.add.listeners) != 'undefined') {
17610 this.addicon = this.wrap.createChild(
17611 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17613 this.addicon.on('click', function(e) {
17614 this.fireEvent('add', this);
17617 if (typeof(this.events.edit.listeners) != 'undefined') {
17619 this.editicon = this.wrap.createChild(
17620 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17621 if (this.addicon) {
17622 this.editicon.setStyle('margin-left', '40px');
17624 this.editicon.on('click', function(e) {
17626 // we fire even if inothing is selected..
17627 this.fireEvent('edit', this, this.lastData );
17633 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17634 "up" : function(e){
17635 this.inKeyMode = true;
17639 "down" : function(e){
17640 if(!this.isExpanded()){
17641 this.onTriggerClick();
17643 this.inKeyMode = true;
17648 "enter" : function(e){
17649 // this.onViewClick();
17653 if(this.fireEvent("specialkey", this, e)){
17654 this.onViewClick(false);
17660 "esc" : function(e){
17664 "tab" : function(e){
17667 if(this.fireEvent("specialkey", this, e)){
17668 this.onViewClick(false);
17676 doRelay : function(foo, bar, hname){
17677 if(hname == 'down' || this.scope.isExpanded()){
17678 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17687 this.queryDelay = Math.max(this.queryDelay || 10,
17688 this.mode == 'local' ? 10 : 250);
17691 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17693 if(this.typeAhead){
17694 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17696 if(this.editable !== false){
17697 this.inputEl().on("keyup", this.onKeyUp, this);
17699 if(this.forceSelection){
17700 this.inputEl().on('blur', this.doForce, this);
17704 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17705 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17709 initTickableEvents: function()
17713 if(this.hiddenName){
17715 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17717 this.hiddenField.dom.value =
17718 this.hiddenValue !== undefined ? this.hiddenValue :
17719 this.value !== undefined ? this.value : '';
17721 // prevent input submission
17722 this.el.dom.removeAttribute('name');
17723 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17728 // this.list = this.el.select('ul.dropdown-menu',true).first();
17730 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17731 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17732 if(this.triggerList){
17733 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17736 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17737 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17739 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17740 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17742 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17743 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17745 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17746 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17747 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17750 this.cancelBtn.hide();
17755 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17756 _this.list.setWidth(lw);
17759 this.list.on('mouseover', this.onViewOver, this);
17760 this.list.on('mousemove', this.onViewMove, this);
17762 this.list.on('scroll', this.onViewScroll, this);
17765 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17766 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17769 this.view = new Roo.View(this.list, this.tpl, {
17774 selectedClass: this.selectedClass
17777 //this.view.wrapEl.setDisplayed(false);
17778 this.view.on('click', this.onViewClick, this);
17782 this.store.on('beforeload', this.onBeforeLoad, this);
17783 this.store.on('load', this.onLoad, this);
17784 this.store.on('loadexception', this.onLoadException, this);
17787 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17788 "up" : function(e){
17789 this.inKeyMode = true;
17793 "down" : function(e){
17794 this.inKeyMode = true;
17798 "enter" : function(e){
17799 if(this.fireEvent("specialkey", this, e)){
17800 this.onViewClick(false);
17806 "esc" : function(e){
17807 this.onTickableFooterButtonClick(e, false, false);
17810 "tab" : function(e){
17811 this.fireEvent("specialkey", this, e);
17813 this.onTickableFooterButtonClick(e, false, false);
17820 doRelay : function(e, fn, key){
17821 if(this.scope.isExpanded()){
17822 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17831 this.queryDelay = Math.max(this.queryDelay || 10,
17832 this.mode == 'local' ? 10 : 250);
17835 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17837 if(this.typeAhead){
17838 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17841 if(this.editable !== false){
17842 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17845 this.indicator = this.indicatorEl();
17847 if(this.indicator){
17848 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17849 this.indicator.hide();
17854 onDestroy : function(){
17856 this.view.setStore(null);
17857 this.view.el.removeAllListeners();
17858 this.view.el.remove();
17859 this.view.purgeListeners();
17862 this.list.dom.innerHTML = '';
17866 this.store.un('beforeload', this.onBeforeLoad, this);
17867 this.store.un('load', this.onLoad, this);
17868 this.store.un('loadexception', this.onLoadException, this);
17870 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17874 fireKey : function(e){
17875 if(e.isNavKeyPress() && !this.list.isVisible()){
17876 this.fireEvent("specialkey", this, e);
17881 onResize: function(w, h)
17885 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17887 // if(typeof w != 'number'){
17888 // // we do not handle it!?!?
17891 // var tw = this.trigger.getWidth();
17892 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17893 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17895 // this.inputEl().setWidth( this.adjustWidth('input', x));
17897 // //this.trigger.setStyle('left', x+'px');
17899 // if(this.list && this.listWidth === undefined){
17900 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17901 // this.list.setWidth(lw);
17902 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17910 * Allow or prevent the user from directly editing the field text. If false is passed,
17911 * the user will only be able to select from the items defined in the dropdown list. This method
17912 * is the runtime equivalent of setting the 'editable' config option at config time.
17913 * @param {Boolean} value True to allow the user to directly edit the field text
17915 setEditable : function(value){
17916 if(value == this.editable){
17919 this.editable = value;
17921 this.inputEl().dom.setAttribute('readOnly', true);
17922 this.inputEl().on('mousedown', this.onTriggerClick, this);
17923 this.inputEl().addClass('x-combo-noedit');
17925 this.inputEl().dom.removeAttribute('readOnly');
17926 this.inputEl().un('mousedown', this.onTriggerClick, this);
17927 this.inputEl().removeClass('x-combo-noedit');
17933 onBeforeLoad : function(combo,opts){
17934 if(!this.hasFocus){
17938 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17940 this.restrictHeight();
17941 this.selectedIndex = -1;
17945 onLoad : function(){
17947 this.hasQuery = false;
17949 if(!this.hasFocus){
17953 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17954 this.loading.hide();
17957 if(this.store.getCount() > 0){
17960 this.restrictHeight();
17961 if(this.lastQuery == this.allQuery){
17962 if(this.editable && !this.tickable){
17963 this.inputEl().dom.select();
17967 !this.selectByValue(this.value, true) &&
17970 !this.store.lastOptions ||
17971 typeof(this.store.lastOptions.add) == 'undefined' ||
17972 this.store.lastOptions.add != true
17975 this.select(0, true);
17978 if(this.autoFocus){
17981 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17982 this.taTask.delay(this.typeAheadDelay);
17986 this.onEmptyResults();
17992 onLoadException : function()
17994 this.hasQuery = false;
17996 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17997 this.loading.hide();
18000 if(this.tickable && this.editable){
18005 // only causes errors at present
18006 //Roo.log(this.store.reader.jsonData);
18007 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18009 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18015 onTypeAhead : function(){
18016 if(this.store.getCount() > 0){
18017 var r = this.store.getAt(0);
18018 var newValue = r.data[this.displayField];
18019 var len = newValue.length;
18020 var selStart = this.getRawValue().length;
18022 if(selStart != len){
18023 this.setRawValue(newValue);
18024 this.selectText(selStart, newValue.length);
18030 onSelect : function(record, index){
18032 if(this.fireEvent('beforeselect', this, record, index) !== false){
18034 this.setFromData(index > -1 ? record.data : false);
18037 this.fireEvent('select', this, record, index);
18042 * Returns the currently selected field value or empty string if no value is set.
18043 * @return {String} value The selected value
18045 getValue : function()
18047 if(Roo.isIOS && this.useNativeIOS){
18048 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18052 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18055 if(this.valueField){
18056 return typeof this.value != 'undefined' ? this.value : '';
18058 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18062 getRawValue : function()
18064 if(Roo.isIOS && this.useNativeIOS){
18065 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18068 var v = this.inputEl().getValue();
18074 * Clears any text/value currently set in the field
18076 clearValue : function(){
18078 if(this.hiddenField){
18079 this.hiddenField.dom.value = '';
18082 this.setRawValue('');
18083 this.lastSelectionText = '';
18084 this.lastData = false;
18086 var close = this.closeTriggerEl();
18097 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18098 * will be displayed in the field. If the value does not match the data value of an existing item,
18099 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18100 * Otherwise the field will be blank (although the value will still be set).
18101 * @param {String} value The value to match
18103 setValue : function(v)
18105 if(Roo.isIOS && this.useNativeIOS){
18106 this.setIOSValue(v);
18116 if(this.valueField){
18117 var r = this.findRecord(this.valueField, v);
18119 text = r.data[this.displayField];
18120 }else if(this.valueNotFoundText !== undefined){
18121 text = this.valueNotFoundText;
18124 this.lastSelectionText = text;
18125 if(this.hiddenField){
18126 this.hiddenField.dom.value = v;
18128 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18131 var close = this.closeTriggerEl();
18134 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18140 * @property {Object} the last set data for the element
18145 * Sets the value of the field based on a object which is related to the record format for the store.
18146 * @param {Object} value the value to set as. or false on reset?
18148 setFromData : function(o){
18155 var dv = ''; // display value
18156 var vv = ''; // value value..
18158 if (this.displayField) {
18159 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18161 // this is an error condition!!!
18162 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18165 if(this.valueField){
18166 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18169 var close = this.closeTriggerEl();
18172 if(dv.length || vv * 1 > 0){
18174 this.blockFocus=true;
18180 if(this.hiddenField){
18181 this.hiddenField.dom.value = vv;
18183 this.lastSelectionText = dv;
18184 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18188 // no hidden field.. - we store the value in 'value', but still display
18189 // display field!!!!
18190 this.lastSelectionText = dv;
18191 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18198 reset : function(){
18199 // overridden so that last data is reset..
18206 this.setValue(this.originalValue);
18207 //this.clearInvalid();
18208 this.lastData = false;
18210 this.view.clearSelections();
18216 findRecord : function(prop, value){
18218 if(this.store.getCount() > 0){
18219 this.store.each(function(r){
18220 if(r.data[prop] == value){
18230 getName: function()
18232 // returns hidden if it's set..
18233 if (!this.rendered) {return ''};
18234 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18238 onViewMove : function(e, t){
18239 this.inKeyMode = false;
18243 onViewOver : function(e, t){
18244 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18247 var item = this.view.findItemFromChild(t);
18250 var index = this.view.indexOf(item);
18251 this.select(index, false);
18256 onViewClick : function(view, doFocus, el, e)
18258 var index = this.view.getSelectedIndexes()[0];
18260 var r = this.store.getAt(index);
18264 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18271 Roo.each(this.tickItems, function(v,k){
18273 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18275 _this.tickItems.splice(k, 1);
18277 if(typeof(e) == 'undefined' && view == false){
18278 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18290 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18291 this.tickItems.push(r.data);
18294 if(typeof(e) == 'undefined' && view == false){
18295 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18302 this.onSelect(r, index);
18304 if(doFocus !== false && !this.blockFocus){
18305 this.inputEl().focus();
18310 restrictHeight : function(){
18311 //this.innerList.dom.style.height = '';
18312 //var inner = this.innerList.dom;
18313 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18314 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18315 //this.list.beginUpdate();
18316 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18317 this.list.alignTo(this.inputEl(), this.listAlign);
18318 this.list.alignTo(this.inputEl(), this.listAlign);
18319 //this.list.endUpdate();
18323 onEmptyResults : function(){
18325 if(this.tickable && this.editable){
18326 this.hasFocus = false;
18327 this.restrictHeight();
18335 * Returns true if the dropdown list is expanded, else false.
18337 isExpanded : function(){
18338 return this.list.isVisible();
18342 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18343 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18344 * @param {String} value The data value of the item to select
18345 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18346 * selected item if it is not currently in view (defaults to true)
18347 * @return {Boolean} True if the value matched an item in the list, else false
18349 selectByValue : function(v, scrollIntoView){
18350 if(v !== undefined && v !== null){
18351 var r = this.findRecord(this.valueField || this.displayField, v);
18353 this.select(this.store.indexOf(r), scrollIntoView);
18361 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18362 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18363 * @param {Number} index The zero-based index of the list item to select
18364 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18365 * selected item if it is not currently in view (defaults to true)
18367 select : function(index, scrollIntoView){
18368 this.selectedIndex = index;
18369 this.view.select(index);
18370 if(scrollIntoView !== false){
18371 var el = this.view.getNode(index);
18373 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18376 this.list.scrollChildIntoView(el, false);
18382 selectNext : function(){
18383 var ct = this.store.getCount();
18385 if(this.selectedIndex == -1){
18387 }else if(this.selectedIndex < ct-1){
18388 this.select(this.selectedIndex+1);
18394 selectPrev : function(){
18395 var ct = this.store.getCount();
18397 if(this.selectedIndex == -1){
18399 }else if(this.selectedIndex != 0){
18400 this.select(this.selectedIndex-1);
18406 onKeyUp : function(e){
18407 if(this.editable !== false && !e.isSpecialKey()){
18408 this.lastKey = e.getKey();
18409 this.dqTask.delay(this.queryDelay);
18414 validateBlur : function(){
18415 return !this.list || !this.list.isVisible();
18419 initQuery : function(){
18421 var v = this.getRawValue();
18423 if(this.tickable && this.editable){
18424 v = this.tickableInputEl().getValue();
18431 doForce : function(){
18432 if(this.inputEl().dom.value.length > 0){
18433 this.inputEl().dom.value =
18434 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18440 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18441 * query allowing the query action to be canceled if needed.
18442 * @param {String} query The SQL query to execute
18443 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18444 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18445 * saved in the current store (defaults to false)
18447 doQuery : function(q, forceAll){
18449 if(q === undefined || q === null){
18454 forceAll: forceAll,
18458 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18463 forceAll = qe.forceAll;
18464 if(forceAll === true || (q.length >= this.minChars)){
18466 this.hasQuery = true;
18468 if(this.lastQuery != q || this.alwaysQuery){
18469 this.lastQuery = q;
18470 if(this.mode == 'local'){
18471 this.selectedIndex = -1;
18473 this.store.clearFilter();
18476 if(this.specialFilter){
18477 this.fireEvent('specialfilter', this);
18482 this.store.filter(this.displayField, q);
18485 this.store.fireEvent("datachanged", this.store);
18492 this.store.baseParams[this.queryParam] = q;
18494 var options = {params : this.getParams(q)};
18497 options.add = true;
18498 options.params.start = this.page * this.pageSize;
18501 this.store.load(options);
18504 * this code will make the page width larger, at the beginning, the list not align correctly,
18505 * we should expand the list on onLoad
18506 * so command out it
18511 this.selectedIndex = -1;
18516 this.loadNext = false;
18520 getParams : function(q){
18522 //p[this.queryParam] = q;
18526 p.limit = this.pageSize;
18532 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18534 collapse : function(){
18535 if(!this.isExpanded()){
18541 this.hasFocus = false;
18545 this.cancelBtn.hide();
18546 this.trigger.show();
18549 this.tickableInputEl().dom.value = '';
18550 this.tickableInputEl().blur();
18555 Roo.get(document).un('mousedown', this.collapseIf, this);
18556 Roo.get(document).un('mousewheel', this.collapseIf, this);
18557 if (!this.editable) {
18558 Roo.get(document).un('keydown', this.listKeyPress, this);
18560 this.fireEvent('collapse', this);
18566 collapseIf : function(e){
18567 var in_combo = e.within(this.el);
18568 var in_list = e.within(this.list);
18569 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18571 if (in_combo || in_list || is_list) {
18572 //e.stopPropagation();
18577 this.onTickableFooterButtonClick(e, false, false);
18585 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18587 expand : function(){
18589 if(this.isExpanded() || !this.hasFocus){
18593 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18594 this.list.setWidth(lw);
18600 this.restrictHeight();
18604 this.tickItems = Roo.apply([], this.item);
18607 this.cancelBtn.show();
18608 this.trigger.hide();
18611 this.tickableInputEl().focus();
18616 Roo.get(document).on('mousedown', this.collapseIf, this);
18617 Roo.get(document).on('mousewheel', this.collapseIf, this);
18618 if (!this.editable) {
18619 Roo.get(document).on('keydown', this.listKeyPress, this);
18622 this.fireEvent('expand', this);
18626 // Implements the default empty TriggerField.onTriggerClick function
18627 onTriggerClick : function(e)
18629 Roo.log('trigger click');
18631 if(this.disabled || !this.triggerList){
18636 this.loadNext = false;
18638 if(this.isExpanded()){
18640 if (!this.blockFocus) {
18641 this.inputEl().focus();
18645 this.hasFocus = true;
18646 if(this.triggerAction == 'all') {
18647 this.doQuery(this.allQuery, true);
18649 this.doQuery(this.getRawValue());
18651 if (!this.blockFocus) {
18652 this.inputEl().focus();
18657 onTickableTriggerClick : function(e)
18664 this.loadNext = false;
18665 this.hasFocus = true;
18667 if(this.triggerAction == 'all') {
18668 this.doQuery(this.allQuery, true);
18670 this.doQuery(this.getRawValue());
18674 onSearchFieldClick : function(e)
18676 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18677 this.onTickableFooterButtonClick(e, false, false);
18681 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18686 this.loadNext = false;
18687 this.hasFocus = true;
18689 if(this.triggerAction == 'all') {
18690 this.doQuery(this.allQuery, true);
18692 this.doQuery(this.getRawValue());
18696 listKeyPress : function(e)
18698 //Roo.log('listkeypress');
18699 // scroll to first matching element based on key pres..
18700 if (e.isSpecialKey()) {
18703 var k = String.fromCharCode(e.getKey()).toUpperCase();
18706 var csel = this.view.getSelectedNodes();
18707 var cselitem = false;
18709 var ix = this.view.indexOf(csel[0]);
18710 cselitem = this.store.getAt(ix);
18711 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18717 this.store.each(function(v) {
18719 // start at existing selection.
18720 if (cselitem.id == v.id) {
18726 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18727 match = this.store.indexOf(v);
18733 if (match === false) {
18734 return true; // no more action?
18737 this.view.select(match);
18738 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18739 sn.scrollIntoView(sn.dom.parentNode, false);
18742 onViewScroll : function(e, t){
18744 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){
18748 this.hasQuery = true;
18750 this.loading = this.list.select('.loading', true).first();
18752 if(this.loading === null){
18753 this.list.createChild({
18755 cls: 'loading roo-select2-more-results roo-select2-active',
18756 html: 'Loading more results...'
18759 this.loading = this.list.select('.loading', true).first();
18761 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18763 this.loading.hide();
18766 this.loading.show();
18771 this.loadNext = true;
18773 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18778 addItem : function(o)
18780 var dv = ''; // display value
18782 if (this.displayField) {
18783 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18785 // this is an error condition!!!
18786 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18793 var choice = this.choices.createChild({
18795 cls: 'roo-select2-search-choice',
18804 cls: 'roo-select2-search-choice-close fa fa-times',
18809 }, this.searchField);
18811 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18813 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18821 this.inputEl().dom.value = '';
18826 onRemoveItem : function(e, _self, o)
18828 e.preventDefault();
18830 this.lastItem = Roo.apply([], this.item);
18832 var index = this.item.indexOf(o.data) * 1;
18835 Roo.log('not this item?!');
18839 this.item.splice(index, 1);
18844 this.fireEvent('remove', this, e);
18850 syncValue : function()
18852 if(!this.item.length){
18859 Roo.each(this.item, function(i){
18860 if(_this.valueField){
18861 value.push(i[_this.valueField]);
18868 this.value = value.join(',');
18870 if(this.hiddenField){
18871 this.hiddenField.dom.value = this.value;
18874 this.store.fireEvent("datachanged", this.store);
18879 clearItem : function()
18881 if(!this.multiple){
18887 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18895 if(this.tickable && !Roo.isTouch){
18896 this.view.refresh();
18900 inputEl: function ()
18902 if(Roo.isIOS && this.useNativeIOS){
18903 return this.el.select('select.roo-ios-select', true).first();
18906 if(Roo.isTouch && this.mobileTouchView){
18907 return this.el.select('input.form-control',true).first();
18911 return this.searchField;
18914 return this.el.select('input.form-control',true).first();
18917 onTickableFooterButtonClick : function(e, btn, el)
18919 e.preventDefault();
18921 this.lastItem = Roo.apply([], this.item);
18923 if(btn && btn.name == 'cancel'){
18924 this.tickItems = Roo.apply([], this.item);
18933 Roo.each(this.tickItems, function(o){
18941 validate : function()
18943 if(this.getVisibilityEl().hasClass('hidden')){
18947 var v = this.getRawValue();
18950 v = this.getValue();
18953 if(this.disabled || this.allowBlank || v.length){
18958 this.markInvalid();
18962 tickableInputEl : function()
18964 if(!this.tickable || !this.editable){
18965 return this.inputEl();
18968 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18972 getAutoCreateTouchView : function()
18977 cls: 'form-group' //input-group
18983 type : this.inputType,
18984 cls : 'form-control x-combo-noedit',
18985 autocomplete: 'new-password',
18986 placeholder : this.placeholder || '',
18991 input.name = this.name;
18995 input.cls += ' input-' + this.size;
18998 if (this.disabled) {
18999 input.disabled = true;
19003 cls : 'roo-combobox-wrap',
19010 inputblock.cls += ' input-group';
19012 inputblock.cn.unshift({
19014 cls : 'input-group-addon input-group-prepend input-group-text',
19019 if(this.removable && !this.multiple){
19020 inputblock.cls += ' roo-removable';
19022 inputblock.cn.push({
19025 cls : 'roo-combo-removable-btn close'
19029 if(this.hasFeedback && !this.allowBlank){
19031 inputblock.cls += ' has-feedback';
19033 inputblock.cn.push({
19035 cls: 'glyphicon form-control-feedback'
19042 inputblock.cls += (this.before) ? '' : ' input-group';
19044 inputblock.cn.push({
19046 cls : 'input-group-addon input-group-append input-group-text',
19052 var ibwrap = inputblock;
19057 cls: 'roo-select2-choices',
19061 cls: 'roo-select2-search-field',
19074 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19079 cls: 'form-hidden-field'
19085 if(!this.multiple && this.showToggleBtn){
19091 if (this.caret != false) {
19094 cls: 'fa fa-' + this.caret
19101 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19103 Roo.bootstrap.version == 3 ? caret : '',
19106 cls: 'combobox-clear',
19120 combobox.cls += ' roo-select2-container-multi';
19123 var required = this.allowBlank ? {
19125 style: 'display: none'
19128 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19129 tooltip : 'This field is required'
19132 var align = this.labelAlign || this.parentLabelAlign();
19134 if (align ==='left' && this.fieldLabel.length) {
19140 cls : 'control-label col-form-label',
19141 html : this.fieldLabel
19145 cls : 'roo-combobox-wrap ',
19152 var labelCfg = cfg.cn[1];
19153 var contentCfg = cfg.cn[2];
19156 if(this.indicatorpos == 'right'){
19161 cls : 'control-label col-form-label',
19165 html : this.fieldLabel
19171 cls : "roo-combobox-wrap ",
19179 labelCfg = cfg.cn[0];
19180 contentCfg = cfg.cn[1];
19185 if(this.labelWidth > 12){
19186 labelCfg.style = "width: " + this.labelWidth + 'px';
19189 if(this.labelWidth < 13 && this.labelmd == 0){
19190 this.labelmd = this.labelWidth;
19193 if(this.labellg > 0){
19194 labelCfg.cls += ' col-lg-' + this.labellg;
19195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19198 if(this.labelmd > 0){
19199 labelCfg.cls += ' col-md-' + this.labelmd;
19200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19203 if(this.labelsm > 0){
19204 labelCfg.cls += ' col-sm-' + this.labelsm;
19205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19208 if(this.labelxs > 0){
19209 labelCfg.cls += ' col-xs-' + this.labelxs;
19210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19214 } else if ( this.fieldLabel.length) {
19219 cls : 'control-label',
19220 html : this.fieldLabel
19231 if(this.indicatorpos == 'right'){
19235 cls : 'control-label',
19236 html : this.fieldLabel,
19254 var settings = this;
19256 ['xs','sm','md','lg'].map(function(size){
19257 if (settings[size]) {
19258 cfg.cls += ' col-' + size + '-' + settings[size];
19265 initTouchView : function()
19267 this.renderTouchView();
19269 this.touchViewEl.on('scroll', function(){
19270 this.el.dom.scrollTop = 0;
19273 this.originalValue = this.getValue();
19275 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19277 this.inputEl().on("click", this.showTouchView, this);
19278 if (this.triggerEl) {
19279 this.triggerEl.on("click", this.showTouchView, this);
19283 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19284 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19286 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19288 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19289 this.store.on('load', this.onTouchViewLoad, this);
19290 this.store.on('loadexception', this.onTouchViewLoadException, this);
19292 if(this.hiddenName){
19294 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19296 this.hiddenField.dom.value =
19297 this.hiddenValue !== undefined ? this.hiddenValue :
19298 this.value !== undefined ? this.value : '';
19300 this.el.dom.removeAttribute('name');
19301 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19305 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19306 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19309 if(this.removable && !this.multiple){
19310 var close = this.closeTriggerEl();
19312 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19313 close.on('click', this.removeBtnClick, this, close);
19317 * fix the bug in Safari iOS8
19319 this.inputEl().on("focus", function(e){
19320 document.activeElement.blur();
19323 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19330 renderTouchView : function()
19332 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19333 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19335 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19336 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19338 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19339 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340 this.touchViewBodyEl.setStyle('overflow', 'auto');
19342 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19343 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19345 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19346 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19350 showTouchView : function()
19356 this.touchViewHeaderEl.hide();
19358 if(this.modalTitle.length){
19359 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19360 this.touchViewHeaderEl.show();
19363 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19364 this.touchViewEl.show();
19366 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19368 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19369 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19371 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19373 if(this.modalTitle.length){
19374 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19377 this.touchViewBodyEl.setHeight(bodyHeight);
19381 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19383 this.touchViewEl.addClass(['in','show']);
19386 if(this._touchViewMask){
19387 Roo.get(document.body).addClass("x-body-masked");
19388 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19389 this._touchViewMask.setStyle('z-index', 10000);
19390 this._touchViewMask.addClass('show');
19393 this.doTouchViewQuery();
19397 hideTouchView : function()
19399 this.touchViewEl.removeClass(['in','show']);
19403 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19405 this.touchViewEl.setStyle('display', 'none');
19408 if(this._touchViewMask){
19409 this._touchViewMask.removeClass('show');
19410 Roo.get(document.body).removeClass("x-body-masked");
19414 setTouchViewValue : function()
19421 Roo.each(this.tickItems, function(o){
19426 this.hideTouchView();
19429 doTouchViewQuery : function()
19438 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19442 if(!this.alwaysQuery || this.mode == 'local'){
19443 this.onTouchViewLoad();
19450 onTouchViewBeforeLoad : function(combo,opts)
19456 onTouchViewLoad : function()
19458 if(this.store.getCount() < 1){
19459 this.onTouchViewEmptyResults();
19463 this.clearTouchView();
19465 var rawValue = this.getRawValue();
19467 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19469 this.tickItems = [];
19471 this.store.data.each(function(d, rowIndex){
19472 var row = this.touchViewListGroup.createChild(template);
19474 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19475 row.addClass(d.data.cls);
19478 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19481 html : d.data[this.displayField]
19484 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19485 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19488 row.removeClass('selected');
19489 if(!this.multiple && this.valueField &&
19490 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19493 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19494 row.addClass('selected');
19497 if(this.multiple && this.valueField &&
19498 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19502 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19503 this.tickItems.push(d.data);
19506 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19510 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19512 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19514 if(this.modalTitle.length){
19515 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19518 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19520 if(this.mobile_restrict_height && listHeight < bodyHeight){
19521 this.touchViewBodyEl.setHeight(listHeight);
19526 if(firstChecked && listHeight > bodyHeight){
19527 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19532 onTouchViewLoadException : function()
19534 this.hideTouchView();
19537 onTouchViewEmptyResults : function()
19539 this.clearTouchView();
19541 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19543 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19547 clearTouchView : function()
19549 this.touchViewListGroup.dom.innerHTML = '';
19552 onTouchViewClick : function(e, el, o)
19554 e.preventDefault();
19557 var rowIndex = o.rowIndex;
19559 var r = this.store.getAt(rowIndex);
19561 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19563 if(!this.multiple){
19564 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19565 c.dom.removeAttribute('checked');
19568 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19570 this.setFromData(r.data);
19572 var close = this.closeTriggerEl();
19578 this.hideTouchView();
19580 this.fireEvent('select', this, r, rowIndex);
19585 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19586 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19587 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19591 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19592 this.addItem(r.data);
19593 this.tickItems.push(r.data);
19597 getAutoCreateNativeIOS : function()
19600 cls: 'form-group' //input-group,
19605 cls : 'roo-ios-select'
19609 combobox.name = this.name;
19612 if (this.disabled) {
19613 combobox.disabled = true;
19616 var settings = this;
19618 ['xs','sm','md','lg'].map(function(size){
19619 if (settings[size]) {
19620 cfg.cls += ' col-' + size + '-' + settings[size];
19630 initIOSView : function()
19632 this.store.on('load', this.onIOSViewLoad, this);
19637 onIOSViewLoad : function()
19639 if(this.store.getCount() < 1){
19643 this.clearIOSView();
19645 if(this.allowBlank) {
19647 var default_text = '-- SELECT --';
19649 if(this.placeholder.length){
19650 default_text = this.placeholder;
19653 if(this.emptyTitle.length){
19654 default_text += ' - ' + this.emptyTitle + ' -';
19657 var opt = this.inputEl().createChild({
19660 html : default_text
19664 o[this.valueField] = 0;
19665 o[this.displayField] = default_text;
19667 this.ios_options.push({
19674 this.store.data.each(function(d, rowIndex){
19678 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19679 html = d.data[this.displayField];
19684 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19685 value = d.data[this.valueField];
19694 if(this.value == d.data[this.valueField]){
19695 option['selected'] = true;
19698 var opt = this.inputEl().createChild(option);
19700 this.ios_options.push({
19707 this.inputEl().on('change', function(){
19708 this.fireEvent('select', this);
19713 clearIOSView: function()
19715 this.inputEl().dom.innerHTML = '';
19717 this.ios_options = [];
19720 setIOSValue: function(v)
19724 if(!this.ios_options){
19728 Roo.each(this.ios_options, function(opts){
19730 opts.el.dom.removeAttribute('selected');
19732 if(opts.data[this.valueField] != v){
19736 opts.el.dom.setAttribute('selected', true);
19742 * @cfg {Boolean} grow
19746 * @cfg {Number} growMin
19750 * @cfg {Number} growMax
19759 Roo.apply(Roo.bootstrap.form.ComboBox, {
19763 cls: 'modal-header',
19785 cls: 'list-group-item',
19789 cls: 'roo-combobox-list-group-item-value'
19793 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19807 listItemCheckbox : {
19809 cls: 'list-group-item',
19813 cls: 'roo-combobox-list-group-item-value'
19817 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19833 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19838 cls: 'modal-footer',
19846 cls: 'col-xs-6 text-left',
19849 cls: 'btn btn-danger roo-touch-view-cancel',
19855 cls: 'col-xs-6 text-right',
19858 cls: 'btn btn-success roo-touch-view-ok',
19869 Roo.apply(Roo.bootstrap.form.ComboBox, {
19871 touchViewTemplate : {
19873 cls: 'modal fade roo-combobox-touch-view',
19877 cls: 'modal-dialog',
19878 style : 'position:fixed', // we have to fix position....
19882 cls: 'modal-content',
19884 Roo.bootstrap.form.ComboBox.header,
19885 Roo.bootstrap.form.ComboBox.body,
19886 Roo.bootstrap.form.ComboBox.footer
19895 * Ext JS Library 1.1.1
19896 * Copyright(c) 2006-2007, Ext JS, LLC.
19898 * Originally Released Under LGPL - original licence link has changed is not relivant.
19901 * <script type="text/javascript">
19906 * @extends Roo.util.Observable
19907 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19908 * This class also supports single and multi selection modes. <br>
19909 * Create a data model bound view:
19911 var store = new Roo.data.Store(...);
19913 var view = new Roo.View({
19915 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19917 singleSelect: true,
19918 selectedClass: "ydataview-selected",
19922 // listen for node click?
19923 view.on("click", function(vw, index, node, e){
19924 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19928 dataModel.load("foobar.xml");
19930 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19932 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19933 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19935 * Note: old style constructor is still suported (container, template, config)
19938 * Create a new View
19939 * @param {Object} config The config object
19942 Roo.View = function(config, depreciated_tpl, depreciated_config){
19944 this.parent = false;
19946 if (typeof(depreciated_tpl) == 'undefined') {
19947 // new way.. - universal constructor.
19948 Roo.apply(this, config);
19949 this.el = Roo.get(this.el);
19952 this.el = Roo.get(config);
19953 this.tpl = depreciated_tpl;
19954 Roo.apply(this, depreciated_config);
19956 this.wrapEl = this.el.wrap().wrap();
19957 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19960 if(typeof(this.tpl) == "string"){
19961 this.tpl = new Roo.Template(this.tpl);
19963 // support xtype ctors..
19964 this.tpl = new Roo.factory(this.tpl, Roo);
19968 this.tpl.compile();
19973 * @event beforeclick
19974 * Fires before a click is processed. Returns false to cancel the default action.
19975 * @param {Roo.View} this
19976 * @param {Number} index The index of the target node
19977 * @param {HTMLElement} node The target node
19978 * @param {Roo.EventObject} e The raw event object
19980 "beforeclick" : true,
19983 * Fires when a template node is clicked.
19984 * @param {Roo.View} this
19985 * @param {Number} index The index of the target node
19986 * @param {HTMLElement} node The target node
19987 * @param {Roo.EventObject} e The raw event object
19992 * Fires when a template node is double clicked.
19993 * @param {Roo.View} this
19994 * @param {Number} index The index of the target node
19995 * @param {HTMLElement} node The target node
19996 * @param {Roo.EventObject} e The raw event object
20000 * @event contextmenu
20001 * Fires when a template node is right clicked.
20002 * @param {Roo.View} this
20003 * @param {Number} index The index of the target node
20004 * @param {HTMLElement} node The target node
20005 * @param {Roo.EventObject} e The raw event object
20007 "contextmenu" : true,
20009 * @event selectionchange
20010 * Fires when the selected nodes change.
20011 * @param {Roo.View} this
20012 * @param {Array} selections Array of the selected nodes
20014 "selectionchange" : true,
20017 * @event beforeselect
20018 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20019 * @param {Roo.View} this
20020 * @param {HTMLElement} node The node to be selected
20021 * @param {Array} selections Array of currently selected nodes
20023 "beforeselect" : true,
20025 * @event preparedata
20026 * Fires on every row to render, to allow you to change the data.
20027 * @param {Roo.View} this
20028 * @param {Object} data to be rendered (change this)
20030 "preparedata" : true
20038 "click": this.onClick,
20039 "dblclick": this.onDblClick,
20040 "contextmenu": this.onContextMenu,
20044 this.selections = [];
20046 this.cmp = new Roo.CompositeElementLite([]);
20048 this.store = Roo.factory(this.store, Roo.data);
20049 this.setStore(this.store, true);
20052 if ( this.footer && this.footer.xtype) {
20054 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20056 this.footer.dataSource = this.store;
20057 this.footer.container = fctr;
20058 this.footer = Roo.factory(this.footer, Roo);
20059 fctr.insertFirst(this.el);
20061 // this is a bit insane - as the paging toolbar seems to detach the el..
20062 // dom.parentNode.parentNode.parentNode
20063 // they get detached?
20067 Roo.View.superclass.constructor.call(this);
20072 Roo.extend(Roo.View, Roo.util.Observable, {
20075 * @cfg {Roo.data.Store} store Data store to load data from.
20080 * @cfg {String|Roo.Element} el The container element.
20085 * @cfg {String|Roo.Template} tpl The template used by this View
20089 * @cfg {String} dataName the named area of the template to use as the data area
20090 * Works with domtemplates roo-name="name"
20094 * @cfg {String} selectedClass The css class to add to selected nodes
20096 selectedClass : "x-view-selected",
20098 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20103 * @cfg {String} text to display on mask (default Loading)
20107 * @cfg {Boolean} multiSelect Allow multiple selection
20109 multiSelect : false,
20111 * @cfg {Boolean} singleSelect Allow single selection
20113 singleSelect: false,
20116 * @cfg {Boolean} toggleSelect - selecting
20118 toggleSelect : false,
20121 * @cfg {Boolean} tickable - selecting
20126 * Returns the element this view is bound to.
20127 * @return {Roo.Element}
20129 getEl : function(){
20130 return this.wrapEl;
20136 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20138 refresh : function(){
20139 //Roo.log('refresh');
20142 // if we are using something like 'domtemplate', then
20143 // the what gets used is:
20144 // t.applySubtemplate(NAME, data, wrapping data..)
20145 // the outer template then get' applied with
20146 // the store 'extra data'
20147 // and the body get's added to the
20148 // roo-name="data" node?
20149 // <span class='roo-tpl-{name}'></span> ?????
20153 this.clearSelections();
20154 this.el.update("");
20156 var records = this.store.getRange();
20157 if(records.length < 1) {
20159 // is this valid?? = should it render a template??
20161 this.el.update(this.emptyText);
20165 if (this.dataName) {
20166 this.el.update(t.apply(this.store.meta)); //????
20167 el = this.el.child('.roo-tpl-' + this.dataName);
20170 for(var i = 0, len = records.length; i < len; i++){
20171 var data = this.prepareData(records[i].data, i, records[i]);
20172 this.fireEvent("preparedata", this, data, i, records[i]);
20174 var d = Roo.apply({}, data);
20177 Roo.apply(d, {'roo-id' : Roo.id()});
20181 Roo.each(this.parent.item, function(item){
20182 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20185 Roo.apply(d, {'roo-data-checked' : 'checked'});
20189 html[html.length] = Roo.util.Format.trim(
20191 t.applySubtemplate(this.dataName, d, this.store.meta) :
20198 el.update(html.join(""));
20199 this.nodes = el.dom.childNodes;
20200 this.updateIndexes(0);
20205 * Function to override to reformat the data that is sent to
20206 * the template for each node.
20207 * DEPRICATED - use the preparedata event handler.
20208 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20209 * a JSON object for an UpdateManager bound view).
20211 prepareData : function(data, index, record)
20213 this.fireEvent("preparedata", this, data, index, record);
20217 onUpdate : function(ds, record){
20218 // Roo.log('on update');
20219 this.clearSelections();
20220 var index = this.store.indexOf(record);
20221 var n = this.nodes[index];
20222 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20223 n.parentNode.removeChild(n);
20224 this.updateIndexes(index, index);
20230 onAdd : function(ds, records, index)
20232 //Roo.log(['on Add', ds, records, index] );
20233 this.clearSelections();
20234 if(this.nodes.length == 0){
20238 var n = this.nodes[index];
20239 for(var i = 0, len = records.length; i < len; i++){
20240 var d = this.prepareData(records[i].data, i, records[i]);
20242 this.tpl.insertBefore(n, d);
20245 this.tpl.append(this.el, d);
20248 this.updateIndexes(index);
20251 onRemove : function(ds, record, index){
20252 // Roo.log('onRemove');
20253 this.clearSelections();
20254 var el = this.dataName ?
20255 this.el.child('.roo-tpl-' + this.dataName) :
20258 el.dom.removeChild(this.nodes[index]);
20259 this.updateIndexes(index);
20263 * Refresh an individual node.
20264 * @param {Number} index
20266 refreshNode : function(index){
20267 this.onUpdate(this.store, this.store.getAt(index));
20270 updateIndexes : function(startIndex, endIndex){
20271 var ns = this.nodes;
20272 startIndex = startIndex || 0;
20273 endIndex = endIndex || ns.length - 1;
20274 for(var i = startIndex; i <= endIndex; i++){
20275 ns[i].nodeIndex = i;
20280 * Changes the data store this view uses and refresh the view.
20281 * @param {Store} store
20283 setStore : function(store, initial){
20284 if(!initial && this.store){
20285 this.store.un("datachanged", this.refresh);
20286 this.store.un("add", this.onAdd);
20287 this.store.un("remove", this.onRemove);
20288 this.store.un("update", this.onUpdate);
20289 this.store.un("clear", this.refresh);
20290 this.store.un("beforeload", this.onBeforeLoad);
20291 this.store.un("load", this.onLoad);
20292 this.store.un("loadexception", this.onLoad);
20296 store.on("datachanged", this.refresh, this);
20297 store.on("add", this.onAdd, this);
20298 store.on("remove", this.onRemove, this);
20299 store.on("update", this.onUpdate, this);
20300 store.on("clear", this.refresh, this);
20301 store.on("beforeload", this.onBeforeLoad, this);
20302 store.on("load", this.onLoad, this);
20303 store.on("loadexception", this.onLoad, this);
20311 * onbeforeLoad - masks the loading area.
20314 onBeforeLoad : function(store,opts)
20316 //Roo.log('onBeforeLoad');
20318 this.el.update("");
20320 this.el.mask(this.mask ? this.mask : "Loading" );
20322 onLoad : function ()
20329 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20330 * @param {HTMLElement} node
20331 * @return {HTMLElement} The template node
20333 findItemFromChild : function(node){
20334 var el = this.dataName ?
20335 this.el.child('.roo-tpl-' + this.dataName,true) :
20338 if(!node || node.parentNode == el){
20341 var p = node.parentNode;
20342 while(p && p != el){
20343 if(p.parentNode == el){
20352 onClick : function(e){
20353 var item = this.findItemFromChild(e.getTarget());
20355 var index = this.indexOf(item);
20356 if(this.onItemClick(item, index, e) !== false){
20357 this.fireEvent("click", this, index, item, e);
20360 this.clearSelections();
20365 onContextMenu : function(e){
20366 var item = this.findItemFromChild(e.getTarget());
20368 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20373 onDblClick : function(e){
20374 var item = this.findItemFromChild(e.getTarget());
20376 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20380 onItemClick : function(item, index, e)
20382 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20385 if (this.toggleSelect) {
20386 var m = this.isSelected(item) ? 'unselect' : 'select';
20389 _t[m](item, true, false);
20392 if(this.multiSelect || this.singleSelect){
20393 if(this.multiSelect && e.shiftKey && this.lastSelection){
20394 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20396 this.select(item, this.multiSelect && e.ctrlKey);
20397 this.lastSelection = item;
20400 if(!this.tickable){
20401 e.preventDefault();
20409 * Get the number of selected nodes.
20412 getSelectionCount : function(){
20413 return this.selections.length;
20417 * Get the currently selected nodes.
20418 * @return {Array} An array of HTMLElements
20420 getSelectedNodes : function(){
20421 return this.selections;
20425 * Get the indexes of the selected nodes.
20428 getSelectedIndexes : function(){
20429 var indexes = [], s = this.selections;
20430 for(var i = 0, len = s.length; i < len; i++){
20431 indexes.push(s[i].nodeIndex);
20437 * Clear all selections
20438 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20440 clearSelections : function(suppressEvent){
20441 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20442 this.cmp.elements = this.selections;
20443 this.cmp.removeClass(this.selectedClass);
20444 this.selections = [];
20445 if(!suppressEvent){
20446 this.fireEvent("selectionchange", this, this.selections);
20452 * Returns true if the passed node is selected
20453 * @param {HTMLElement/Number} node The node or node index
20454 * @return {Boolean}
20456 isSelected : function(node){
20457 var s = this.selections;
20461 node = this.getNode(node);
20462 return s.indexOf(node) !== -1;
20467 * @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
20468 * @param {Boolean} keepExisting (optional) true to keep existing selections
20469 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20471 select : function(nodeInfo, keepExisting, suppressEvent){
20472 if(nodeInfo instanceof Array){
20474 this.clearSelections(true);
20476 for(var i = 0, len = nodeInfo.length; i < len; i++){
20477 this.select(nodeInfo[i], true, true);
20481 var node = this.getNode(nodeInfo);
20482 if(!node || this.isSelected(node)){
20483 return; // already selected.
20486 this.clearSelections(true);
20489 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20490 Roo.fly(node).addClass(this.selectedClass);
20491 this.selections.push(node);
20492 if(!suppressEvent){
20493 this.fireEvent("selectionchange", this, this.selections);
20501 * @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
20502 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20503 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20505 unselect : function(nodeInfo, keepExisting, suppressEvent)
20507 if(nodeInfo instanceof Array){
20508 Roo.each(this.selections, function(s) {
20509 this.unselect(s, nodeInfo);
20513 var node = this.getNode(nodeInfo);
20514 if(!node || !this.isSelected(node)){
20515 //Roo.log("not selected");
20516 return; // not selected.
20520 Roo.each(this.selections, function(s) {
20522 Roo.fly(node).removeClass(this.selectedClass);
20529 this.selections= ns;
20530 this.fireEvent("selectionchange", this, this.selections);
20534 * Gets a template node.
20535 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20536 * @return {HTMLElement} The node or null if it wasn't found
20538 getNode : function(nodeInfo){
20539 if(typeof nodeInfo == "string"){
20540 return document.getElementById(nodeInfo);
20541 }else if(typeof nodeInfo == "number"){
20542 return this.nodes[nodeInfo];
20548 * Gets a range template nodes.
20549 * @param {Number} startIndex
20550 * @param {Number} endIndex
20551 * @return {Array} An array of nodes
20553 getNodes : function(start, end){
20554 var ns = this.nodes;
20555 start = start || 0;
20556 end = typeof end == "undefined" ? ns.length - 1 : end;
20559 for(var i = start; i <= end; i++){
20563 for(var i = start; i >= end; i--){
20571 * Finds the index of the passed node
20572 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20573 * @return {Number} The index of the node or -1
20575 indexOf : function(node){
20576 node = this.getNode(node);
20577 if(typeof node.nodeIndex == "number"){
20578 return node.nodeIndex;
20580 var ns = this.nodes;
20581 for(var i = 0, len = ns.length; i < len; i++){
20592 * based on jquery fullcalendar
20596 Roo.bootstrap = Roo.bootstrap || {};
20598 * @class Roo.bootstrap.Calendar
20599 * @extends Roo.bootstrap.Component
20600 * Bootstrap Calendar class
20601 * @cfg {Boolean} loadMask (true|false) default false
20602 * @cfg {Object} header generate the user specific header of the calendar, default false
20605 * Create a new Container
20606 * @param {Object} config The config object
20611 Roo.bootstrap.Calendar = function(config){
20612 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20616 * Fires when a date is selected
20617 * @param {DatePicker} this
20618 * @param {Date} date The selected date
20622 * @event monthchange
20623 * Fires when the displayed month changes
20624 * @param {DatePicker} this
20625 * @param {Date} date The selected month
20627 'monthchange': true,
20629 * @event evententer
20630 * Fires when mouse over an event
20631 * @param {Calendar} this
20632 * @param {event} Event
20634 'evententer': true,
20636 * @event eventleave
20637 * Fires when the mouse leaves an
20638 * @param {Calendar} this
20641 'eventleave': true,
20643 * @event eventclick
20644 * Fires when the mouse click an
20645 * @param {Calendar} this
20654 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20657 * @cfg {Roo.data.Store} store
20658 * The data source for the calendar
20662 * @cfg {Number} startDay
20663 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20671 getAutoCreate : function(){
20674 var fc_button = function(name, corner, style, content ) {
20675 return Roo.apply({},{
20677 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20679 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20682 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20693 style : 'width:100%',
20700 cls : 'fc-header-left',
20702 fc_button('prev', 'left', 'arrow', '‹' ),
20703 fc_button('next', 'right', 'arrow', '›' ),
20704 { tag: 'span', cls: 'fc-header-space' },
20705 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20713 cls : 'fc-header-center',
20717 cls: 'fc-header-title',
20720 html : 'month / year'
20728 cls : 'fc-header-right',
20730 /* fc_button('month', 'left', '', 'month' ),
20731 fc_button('week', '', '', 'week' ),
20732 fc_button('day', 'right', '', 'day' )
20744 header = this.header;
20747 var cal_heads = function() {
20749 // fixme - handle this.
20751 for (var i =0; i < Date.dayNames.length; i++) {
20752 var d = Date.dayNames[i];
20755 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20756 html : d.substring(0,3)
20760 ret[0].cls += ' fc-first';
20761 ret[6].cls += ' fc-last';
20764 var cal_cell = function(n) {
20767 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20772 cls: 'fc-day-number',
20776 cls: 'fc-day-content',
20780 style: 'position: relative;' // height: 17px;
20792 var cal_rows = function() {
20795 for (var r = 0; r < 6; r++) {
20802 for (var i =0; i < Date.dayNames.length; i++) {
20803 var d = Date.dayNames[i];
20804 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20807 row.cn[0].cls+=' fc-first';
20808 row.cn[0].cn[0].style = 'min-height:90px';
20809 row.cn[6].cls+=' fc-last';
20813 ret[0].cls += ' fc-first';
20814 ret[4].cls += ' fc-prev-last';
20815 ret[5].cls += ' fc-last';
20822 cls: 'fc-border-separate',
20823 style : 'width:100%',
20831 cls : 'fc-first fc-last',
20849 cls : 'fc-content',
20850 style : "position: relative;",
20853 cls : 'fc-view fc-view-month fc-grid',
20854 style : 'position: relative',
20855 unselectable : 'on',
20858 cls : 'fc-event-container',
20859 style : 'position:absolute;z-index:8;top:0;left:0;'
20877 initEvents : function()
20880 throw "can not find store for calendar";
20886 style: "text-align:center",
20890 style: "background-color:white;width:50%;margin:250 auto",
20894 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20905 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20907 var size = this.el.select('.fc-content', true).first().getSize();
20908 this.maskEl.setSize(size.width, size.height);
20909 this.maskEl.enableDisplayMode("block");
20910 if(!this.loadMask){
20911 this.maskEl.hide();
20914 this.store = Roo.factory(this.store, Roo.data);
20915 this.store.on('load', this.onLoad, this);
20916 this.store.on('beforeload', this.onBeforeLoad, this);
20920 this.cells = this.el.select('.fc-day',true);
20921 //Roo.log(this.cells);
20922 this.textNodes = this.el.query('.fc-day-number');
20923 this.cells.addClassOnOver('fc-state-hover');
20925 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20926 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20927 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20928 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20930 this.on('monthchange', this.onMonthChange, this);
20932 this.update(new Date().clearTime());
20935 resize : function() {
20936 var sz = this.el.getSize();
20938 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20939 this.el.select('.fc-day-content div',true).setHeight(34);
20944 showPrevMonth : function(e){
20945 this.update(this.activeDate.add("mo", -1));
20947 showToday : function(e){
20948 this.update(new Date().clearTime());
20951 showNextMonth : function(e){
20952 this.update(this.activeDate.add("mo", 1));
20956 showPrevYear : function(){
20957 this.update(this.activeDate.add("y", -1));
20961 showNextYear : function(){
20962 this.update(this.activeDate.add("y", 1));
20967 update : function(date)
20969 var vd = this.activeDate;
20970 this.activeDate = date;
20971 // if(vd && this.el){
20972 // var t = date.getTime();
20973 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20974 // Roo.log('using add remove');
20976 // this.fireEvent('monthchange', this, date);
20978 // this.cells.removeClass("fc-state-highlight");
20979 // this.cells.each(function(c){
20980 // if(c.dateValue == t){
20981 // c.addClass("fc-state-highlight");
20982 // setTimeout(function(){
20983 // try{c.dom.firstChild.focus();}catch(e){}
20993 var days = date.getDaysInMonth();
20995 var firstOfMonth = date.getFirstDateOfMonth();
20996 var startingPos = firstOfMonth.getDay()-this.startDay;
20998 if(startingPos < this.startDay){
21002 var pm = date.add(Date.MONTH, -1);
21003 var prevStart = pm.getDaysInMonth()-startingPos;
21005 this.cells = this.el.select('.fc-day',true);
21006 this.textNodes = this.el.query('.fc-day-number');
21007 this.cells.addClassOnOver('fc-state-hover');
21009 var cells = this.cells.elements;
21010 var textEls = this.textNodes;
21012 Roo.each(cells, function(cell){
21013 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21016 days += startingPos;
21018 // convert everything to numbers so it's fast
21019 var day = 86400000;
21020 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21023 //Roo.log(prevStart);
21025 var today = new Date().clearTime().getTime();
21026 var sel = date.clearTime().getTime();
21027 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21028 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21029 var ddMatch = this.disabledDatesRE;
21030 var ddText = this.disabledDatesText;
21031 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21032 var ddaysText = this.disabledDaysText;
21033 var format = this.format;
21035 var setCellClass = function(cal, cell){
21039 //Roo.log('set Cell Class');
21041 var t = d.getTime();
21045 cell.dateValue = t;
21047 cell.className += " fc-today";
21048 cell.className += " fc-state-highlight";
21049 cell.title = cal.todayText;
21052 // disable highlight in other month..
21053 //cell.className += " fc-state-highlight";
21058 cell.className = " fc-state-disabled";
21059 cell.title = cal.minText;
21063 cell.className = " fc-state-disabled";
21064 cell.title = cal.maxText;
21068 if(ddays.indexOf(d.getDay()) != -1){
21069 cell.title = ddaysText;
21070 cell.className = " fc-state-disabled";
21073 if(ddMatch && format){
21074 var fvalue = d.dateFormat(format);
21075 if(ddMatch.test(fvalue)){
21076 cell.title = ddText.replace("%0", fvalue);
21077 cell.className = " fc-state-disabled";
21081 if (!cell.initialClassName) {
21082 cell.initialClassName = cell.dom.className;
21085 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21090 for(; i < startingPos; i++) {
21091 textEls[i].innerHTML = (++prevStart);
21092 d.setDate(d.getDate()+1);
21094 cells[i].className = "fc-past fc-other-month";
21095 setCellClass(this, cells[i]);
21100 for(; i < days; i++){
21101 intDay = i - startingPos + 1;
21102 textEls[i].innerHTML = (intDay);
21103 d.setDate(d.getDate()+1);
21105 cells[i].className = ''; // "x-date-active";
21106 setCellClass(this, cells[i]);
21110 for(; i < 42; i++) {
21111 textEls[i].innerHTML = (++extraDays);
21112 d.setDate(d.getDate()+1);
21114 cells[i].className = "fc-future fc-other-month";
21115 setCellClass(this, cells[i]);
21118 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21120 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21122 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21123 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21125 if(totalRows != 6){
21126 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21127 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21130 this.fireEvent('monthchange', this, date);
21134 if(!this.internalRender){
21135 var main = this.el.dom.firstChild;
21136 var w = main.offsetWidth;
21137 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21138 Roo.fly(main).setWidth(w);
21139 this.internalRender = true;
21140 // opera does not respect the auto grow header center column
21141 // then, after it gets a width opera refuses to recalculate
21142 // without a second pass
21143 if(Roo.isOpera && !this.secondPass){
21144 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21145 this.secondPass = true;
21146 this.update.defer(10, this, [date]);
21153 findCell : function(dt) {
21154 dt = dt.clearTime().getTime();
21156 this.cells.each(function(c){
21157 //Roo.log("check " +c.dateValue + '?=' + dt);
21158 if(c.dateValue == dt){
21168 findCells : function(ev) {
21169 var s = ev.start.clone().clearTime().getTime();
21171 var e= ev.end.clone().clearTime().getTime();
21174 this.cells.each(function(c){
21175 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21177 if(c.dateValue > e){
21180 if(c.dateValue < s){
21189 // findBestRow: function(cells)
21193 // for (var i =0 ; i < cells.length;i++) {
21194 // ret = Math.max(cells[i].rows || 0,ret);
21201 addItem : function(ev)
21203 // look for vertical location slot in
21204 var cells = this.findCells(ev);
21206 // ev.row = this.findBestRow(cells);
21208 // work out the location.
21212 for(var i =0; i < cells.length; i++) {
21214 cells[i].row = cells[0].row;
21217 cells[i].row = cells[i].row + 1;
21227 if (crow.start.getY() == cells[i].getY()) {
21229 crow.end = cells[i];
21246 cells[0].events.push(ev);
21248 this.calevents.push(ev);
21251 clearEvents: function() {
21253 if(!this.calevents){
21257 Roo.each(this.cells.elements, function(c){
21263 Roo.each(this.calevents, function(e) {
21264 Roo.each(e.els, function(el) {
21265 el.un('mouseenter' ,this.onEventEnter, this);
21266 el.un('mouseleave' ,this.onEventLeave, this);
21271 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21277 renderEvents: function()
21281 this.cells.each(function(c) {
21290 if(c.row != c.events.length){
21291 r = 4 - (4 - (c.row - c.events.length));
21294 c.events = ev.slice(0, r);
21295 c.more = ev.slice(r);
21297 if(c.more.length && c.more.length == 1){
21298 c.events.push(c.more.pop());
21301 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21305 this.cells.each(function(c) {
21307 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21310 for (var e = 0; e < c.events.length; e++){
21311 var ev = c.events[e];
21312 var rows = ev.rows;
21314 for(var i = 0; i < rows.length; i++) {
21316 // how many rows should it span..
21319 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21320 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21322 unselectable : "on",
21325 cls: 'fc-event-inner',
21329 // cls: 'fc-event-time',
21330 // html : cells.length > 1 ? '' : ev.time
21334 cls: 'fc-event-title',
21335 html : String.format('{0}', ev.title)
21342 cls: 'ui-resizable-handle ui-resizable-e',
21343 html : '  '
21350 cfg.cls += ' fc-event-start';
21352 if ((i+1) == rows.length) {
21353 cfg.cls += ' fc-event-end';
21356 var ctr = _this.el.select('.fc-event-container',true).first();
21357 var cg = ctr.createChild(cfg);
21359 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21360 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21362 var r = (c.more.length) ? 1 : 0;
21363 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21364 cg.setWidth(ebox.right - sbox.x -2);
21366 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21367 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21368 cg.on('click', _this.onEventClick, _this, ev);
21379 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21380 style : 'position: absolute',
21381 unselectable : "on",
21384 cls: 'fc-event-inner',
21388 cls: 'fc-event-title',
21396 cls: 'ui-resizable-handle ui-resizable-e',
21397 html : '  '
21403 var ctr = _this.el.select('.fc-event-container',true).first();
21404 var cg = ctr.createChild(cfg);
21406 var sbox = c.select('.fc-day-content',true).first().getBox();
21407 var ebox = c.select('.fc-day-content',true).first().getBox();
21409 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21410 cg.setWidth(ebox.right - sbox.x -2);
21412 cg.on('click', _this.onMoreEventClick, _this, c.more);
21422 onEventEnter: function (e, el,event,d) {
21423 this.fireEvent('evententer', this, el, event);
21426 onEventLeave: function (e, el,event,d) {
21427 this.fireEvent('eventleave', this, el, event);
21430 onEventClick: function (e, el,event,d) {
21431 this.fireEvent('eventclick', this, el, event);
21434 onMonthChange: function () {
21438 onMoreEventClick: function(e, el, more)
21442 this.calpopover.placement = 'right';
21443 this.calpopover.setTitle('More');
21445 this.calpopover.setContent('');
21447 var ctr = this.calpopover.el.select('.popover-content', true).first();
21449 Roo.each(more, function(m){
21451 cls : 'fc-event-hori fc-event-draggable',
21454 var cg = ctr.createChild(cfg);
21456 cg.on('click', _this.onEventClick, _this, m);
21459 this.calpopover.show(el);
21464 onLoad: function ()
21466 this.calevents = [];
21469 if(this.store.getCount() > 0){
21470 this.store.data.each(function(d){
21473 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21474 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21475 time : d.data.start_time,
21476 title : d.data.title,
21477 description : d.data.description,
21478 venue : d.data.venue
21483 this.renderEvents();
21485 if(this.calevents.length && this.loadMask){
21486 this.maskEl.hide();
21490 onBeforeLoad: function()
21492 this.clearEvents();
21494 this.maskEl.show();
21508 * @class Roo.bootstrap.Popover
21509 * @extends Roo.bootstrap.Component
21510 * @parent none builder
21511 * @children Roo.bootstrap.Component
21512 * Bootstrap Popover class
21513 * @cfg {String} html contents of the popover (or false to use children..)
21514 * @cfg {String} title of popover (or false to hide)
21515 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21516 * @cfg {String} trigger click || hover (or false to trigger manually)
21517 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21518 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21519 * - if false and it has a 'parent' then it will be automatically added to that element
21520 * - if string - Roo.get will be called
21521 * @cfg {Number} delay - delay before showing
21524 * Create a new Popover
21525 * @param {Object} config The config object
21528 Roo.bootstrap.Popover = function(config){
21529 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21535 * After the popover show
21537 * @param {Roo.bootstrap.Popover} this
21542 * After the popover hide
21544 * @param {Roo.bootstrap.Popover} this
21550 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21555 placement : 'right',
21556 trigger : 'hover', // hover
21562 can_build_overlaid : false,
21564 maskEl : false, // the mask element
21567 alignEl : false, // when show is called with an element - this get's stored.
21569 getChildContainer : function()
21571 return this.contentEl;
21574 getPopoverHeader : function()
21576 this.title = true; // flag not to hide it..
21577 this.headerEl.addClass('p-0');
21578 return this.headerEl
21582 getAutoCreate : function(){
21585 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21586 style: 'display:block',
21592 cls : 'popover-inner ',
21596 cls: 'popover-title popover-header',
21597 html : this.title === false ? '' : this.title
21600 cls : 'popover-content popover-body ' + (this.cls || ''),
21601 html : this.html || ''
21612 * @param {string} the title
21614 setTitle: function(str)
21618 this.headerEl.dom.innerHTML = str;
21623 * @param {string} the body content
21625 setContent: function(str)
21628 if (this.contentEl) {
21629 this.contentEl.dom.innerHTML = str;
21633 // as it get's added to the bottom of the page.
21634 onRender : function(ct, position)
21636 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21641 var cfg = Roo.apply({}, this.getAutoCreate());
21645 cfg.cls += ' ' + this.cls;
21648 cfg.style = this.style;
21650 //Roo.log("adding to ");
21651 this.el = Roo.get(document.body).createChild(cfg, position);
21652 // Roo.log(this.el);
21655 this.contentEl = this.el.select('.popover-content',true).first();
21656 this.headerEl = this.el.select('.popover-title',true).first();
21659 if(typeof(this.items) != 'undefined'){
21660 var items = this.items;
21663 for(var i =0;i < items.length;i++) {
21664 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21668 this.items = nitems;
21670 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21671 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21678 resizeMask : function()
21680 this.maskEl.setSize(
21681 Roo.lib.Dom.getViewWidth(true),
21682 Roo.lib.Dom.getViewHeight(true)
21686 initEvents : function()
21690 Roo.bootstrap.Popover.register(this);
21693 this.arrowEl = this.el.select('.arrow',true).first();
21694 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21695 this.el.enableDisplayMode('block');
21699 if (this.over === false && !this.parent()) {
21702 if (this.triggers === false) {
21707 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21708 var triggers = this.trigger ? this.trigger.split(' ') : [];
21709 Roo.each(triggers, function(trigger) {
21711 if (trigger == 'click') {
21712 on_el.on('click', this.toggle, this);
21713 } else if (trigger != 'manual') {
21714 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21715 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21717 on_el.on(eventIn ,this.enter, this);
21718 on_el.on(eventOut, this.leave, this);
21728 toggle : function () {
21729 this.hoverState == 'in' ? this.leave() : this.enter();
21732 enter : function () {
21734 clearTimeout(this.timeout);
21736 this.hoverState = 'in';
21738 if (!this.delay || !this.delay.show) {
21743 this.timeout = setTimeout(function () {
21744 if (_t.hoverState == 'in') {
21747 }, this.delay.show)
21750 leave : function() {
21751 clearTimeout(this.timeout);
21753 this.hoverState = 'out';
21755 if (!this.delay || !this.delay.hide) {
21760 this.timeout = setTimeout(function () {
21761 if (_t.hoverState == 'out') {
21764 }, this.delay.hide)
21768 * update the position of the dialog
21769 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21774 doAlign : function()
21777 if (this.alignEl) {
21778 this.updatePosition(this.placement, true);
21781 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21782 var es = this.el.getSize();
21783 var x = Roo.lib.Dom.getViewWidth()/2;
21784 var y = Roo.lib.Dom.getViewHeight()/2;
21785 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21797 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21798 * @param {string} (left|right|top|bottom) position
21800 show : function (on_el, placement)
21802 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21803 on_el = on_el || false; // default to false
21806 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21807 on_el = this.parent().el;
21808 } else if (this.over) {
21809 on_el = Roo.get(this.over);
21814 this.alignEl = Roo.get( on_el );
21817 this.render(document.body);
21823 if (this.title === false) {
21824 this.headerEl.hide();
21829 this.el.dom.style.display = 'block';
21833 //var arrow = this.el.select('.arrow',true).first();
21834 //arrow.set(align[2],
21836 this.el.addClass('in');
21840 this.hoverState = 'in';
21843 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21844 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21845 this.maskEl.dom.style.display = 'block';
21846 this.maskEl.addClass('show');
21848 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21850 this.fireEvent('show', this);
21854 * fire this manually after loading a grid in the table for example
21855 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21856 * @param {Boolean} try and move it if we cant get right position.
21858 updatePosition : function(placement, try_move)
21860 // allow for calling with no parameters
21861 placement = placement ? placement : this.placement;
21862 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21864 this.el.removeClass([
21865 'fade','top','bottom', 'left', 'right','in',
21866 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21868 this.el.addClass(placement + ' bs-popover-' + placement);
21870 if (!this.alignEl ) {
21874 switch (placement) {
21876 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21877 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21878 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21879 //normal display... or moved up/down.
21880 this.el.setXY(offset);
21881 var xy = this.alignEl.getAnchorXY('tr', false);
21883 this.arrowEl.setXY(xy);
21886 // continue through...
21887 return this.updatePosition('left', false);
21891 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21892 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21893 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21894 //normal display... or moved up/down.
21895 this.el.setXY(offset);
21896 var xy = this.alignEl.getAnchorXY('tl', false);
21897 xy[0]-=10;xy[1]+=5; // << fix me
21898 this.arrowEl.setXY(xy);
21902 return this.updatePosition('right', false);
21905 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21906 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21907 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21908 //normal display... or moved up/down.
21909 this.el.setXY(offset);
21910 var xy = this.alignEl.getAnchorXY('t', false);
21911 xy[1]-=10; // << fix me
21912 this.arrowEl.setXY(xy);
21916 return this.updatePosition('bottom', false);
21919 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21920 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21921 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21922 //normal display... or moved up/down.
21923 this.el.setXY(offset);
21924 var xy = this.alignEl.getAnchorXY('b', false);
21925 xy[1]+=2; // << fix me
21926 this.arrowEl.setXY(xy);
21930 return this.updatePosition('top', false);
21941 this.el.setXY([0,0]);
21942 this.el.removeClass('in');
21944 this.hoverState = null;
21945 this.maskEl.hide(); // always..
21946 this.fireEvent('hide', this);
21952 Roo.apply(Roo.bootstrap.Popover, {
21955 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21956 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21957 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21958 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21963 clickHander : false,
21967 onMouseDown : function(e)
21969 if (this.popups.length && !e.getTarget(".roo-popover")) {
21970 /// what is nothing is showing..
21979 register : function(popup)
21981 if (!Roo.bootstrap.Popover.clickHandler) {
21982 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21984 // hide other popups.
21985 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21986 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21987 this.hideAll(); //<< why?
21988 //this.popups.push(popup);
21990 hideAll : function()
21992 this.popups.forEach(function(p) {
21996 onShow : function() {
21997 Roo.bootstrap.Popover.popups.push(this);
21999 onHide : function() {
22000 Roo.bootstrap.Popover.popups.remove(this);
22005 * @class Roo.bootstrap.PopoverNav
22006 * @extends Roo.bootstrap.nav.Simplebar
22007 * @parent Roo.bootstrap.Popover
22008 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22010 * Bootstrap Popover header navigation class
22011 * FIXME? should this go under nav?
22015 * Create a new Popover Header Navigation
22016 * @param {Object} config The config object
22019 Roo.bootstrap.PopoverNav = function(config){
22020 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22023 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22026 container_method : 'getPopoverHeader'
22044 * @class Roo.bootstrap.Progress
22045 * @extends Roo.bootstrap.Component
22046 * @children Roo.bootstrap.ProgressBar
22047 * Bootstrap Progress class
22048 * @cfg {Boolean} striped striped of the progress bar
22049 * @cfg {Boolean} active animated of the progress bar
22053 * Create a new Progress
22054 * @param {Object} config The config object
22057 Roo.bootstrap.Progress = function(config){
22058 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22061 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22066 getAutoCreate : function(){
22074 cfg.cls += ' progress-striped';
22078 cfg.cls += ' active';
22097 * @class Roo.bootstrap.ProgressBar
22098 * @extends Roo.bootstrap.Component
22099 * Bootstrap ProgressBar class
22100 * @cfg {Number} aria_valuenow aria-value now
22101 * @cfg {Number} aria_valuemin aria-value min
22102 * @cfg {Number} aria_valuemax aria-value max
22103 * @cfg {String} label label for the progress bar
22104 * @cfg {String} panel (success | info | warning | danger )
22105 * @cfg {String} role role of the progress bar
22106 * @cfg {String} sr_only text
22110 * Create a new ProgressBar
22111 * @param {Object} config The config object
22114 Roo.bootstrap.ProgressBar = function(config){
22115 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22118 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22122 aria_valuemax : 100,
22128 getAutoCreate : function()
22133 cls: 'progress-bar',
22134 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22146 cfg.role = this.role;
22149 if(this.aria_valuenow){
22150 cfg['aria-valuenow'] = this.aria_valuenow;
22153 if(this.aria_valuemin){
22154 cfg['aria-valuemin'] = this.aria_valuemin;
22157 if(this.aria_valuemax){
22158 cfg['aria-valuemax'] = this.aria_valuemax;
22161 if(this.label && !this.sr_only){
22162 cfg.html = this.label;
22166 cfg.cls += ' progress-bar-' + this.panel;
22172 update : function(aria_valuenow)
22174 this.aria_valuenow = aria_valuenow;
22176 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22184 * @class Roo.bootstrap.TabGroup
22185 * @extends Roo.bootstrap.Column
22186 * @children Roo.bootstrap.TabPanel
22187 * Bootstrap Column class
22188 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22189 * @cfg {Boolean} carousel true to make the group behave like a carousel
22190 * @cfg {Boolean} bullets show bullets for the panels
22191 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22192 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22193 * @cfg {Boolean} showarrow (true|false) show arrow default true
22196 * Create a new TabGroup
22197 * @param {Object} config The config object
22200 Roo.bootstrap.TabGroup = function(config){
22201 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22203 this.navId = Roo.id();
22206 Roo.bootstrap.TabGroup.register(this);
22210 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22213 transition : false,
22218 slideOnTouch : false,
22221 getAutoCreate : function()
22223 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22225 cfg.cls += ' tab-content';
22227 if (this.carousel) {
22228 cfg.cls += ' carousel slide';
22231 cls : 'carousel-inner',
22235 if(this.bullets && !Roo.isTouch){
22238 cls : 'carousel-bullets',
22242 if(this.bullets_cls){
22243 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22250 cfg.cn[0].cn.push(bullets);
22253 if(this.showarrow){
22254 cfg.cn[0].cn.push({
22256 class : 'carousel-arrow',
22260 class : 'carousel-prev',
22264 class : 'fa fa-chevron-left'
22270 class : 'carousel-next',
22274 class : 'fa fa-chevron-right'
22287 initEvents: function()
22289 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22290 // this.el.on("touchstart", this.onTouchStart, this);
22293 if(this.autoslide){
22296 this.slideFn = window.setInterval(function() {
22297 _this.showPanelNext();
22301 if(this.showarrow){
22302 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22303 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22309 // onTouchStart : function(e, el, o)
22311 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22315 // this.showPanelNext();
22319 getChildContainer : function()
22321 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22325 * register a Navigation item
22326 * @param {Roo.bootstrap.nav.Item} the navitem to add
22328 register : function(item)
22330 this.tabs.push( item);
22331 item.navId = this.navId; // not really needed..
22336 getActivePanel : function()
22339 Roo.each(this.tabs, function(t) {
22349 getPanelByName : function(n)
22352 Roo.each(this.tabs, function(t) {
22353 if (t.tabId == n) {
22361 indexOfPanel : function(p)
22364 Roo.each(this.tabs, function(t,i) {
22365 if (t.tabId == p.tabId) {
22374 * show a specific panel
22375 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22376 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22378 showPanel : function (pan)
22380 if(this.transition || typeof(pan) == 'undefined'){
22381 Roo.log("waiting for the transitionend");
22385 if (typeof(pan) == 'number') {
22386 pan = this.tabs[pan];
22389 if (typeof(pan) == 'string') {
22390 pan = this.getPanelByName(pan);
22393 var cur = this.getActivePanel();
22396 Roo.log('pan or acitve pan is undefined');
22400 if (pan.tabId == this.getActivePanel().tabId) {
22404 if (false === cur.fireEvent('beforedeactivate')) {
22408 if(this.bullets > 0 && !Roo.isTouch){
22409 this.setActiveBullet(this.indexOfPanel(pan));
22412 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22414 //class="carousel-item carousel-item-next carousel-item-left"
22416 this.transition = true;
22417 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22418 var lr = dir == 'next' ? 'left' : 'right';
22419 pan.el.addClass(dir); // or prev
22420 pan.el.addClass('carousel-item-' + dir); // or prev
22421 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22422 cur.el.addClass(lr); // or right
22423 pan.el.addClass(lr);
22424 cur.el.addClass('carousel-item-' +lr); // or right
22425 pan.el.addClass('carousel-item-' +lr);
22429 cur.el.on('transitionend', function() {
22430 Roo.log("trans end?");
22432 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22433 pan.setActive(true);
22435 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22436 cur.setActive(false);
22438 _this.transition = false;
22440 }, this, { single: true } );
22445 cur.setActive(false);
22446 pan.setActive(true);
22451 showPanelNext : function()
22453 var i = this.indexOfPanel(this.getActivePanel());
22455 if (i >= this.tabs.length - 1 && !this.autoslide) {
22459 if (i >= this.tabs.length - 1 && this.autoslide) {
22463 this.showPanel(this.tabs[i+1]);
22466 showPanelPrev : function()
22468 var i = this.indexOfPanel(this.getActivePanel());
22470 if (i < 1 && !this.autoslide) {
22474 if (i < 1 && this.autoslide) {
22475 i = this.tabs.length;
22478 this.showPanel(this.tabs[i-1]);
22482 addBullet: function()
22484 if(!this.bullets || Roo.isTouch){
22487 var ctr = this.el.select('.carousel-bullets',true).first();
22488 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22489 var bullet = ctr.createChild({
22490 cls : 'bullet bullet-' + i
22491 },ctr.dom.lastChild);
22496 bullet.on('click', (function(e, el, o, ii, t){
22498 e.preventDefault();
22500 this.showPanel(ii);
22502 if(this.autoslide && this.slideFn){
22503 clearInterval(this.slideFn);
22504 this.slideFn = window.setInterval(function() {
22505 _this.showPanelNext();
22509 }).createDelegate(this, [i, bullet], true));
22514 setActiveBullet : function(i)
22520 Roo.each(this.el.select('.bullet', true).elements, function(el){
22521 el.removeClass('selected');
22524 var bullet = this.el.select('.bullet-' + i, true).first();
22530 bullet.addClass('selected');
22541 Roo.apply(Roo.bootstrap.TabGroup, {
22545 * register a Navigation Group
22546 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22548 register : function(navgrp)
22550 this.groups[navgrp.navId] = navgrp;
22554 * fetch a Navigation Group based on the navigation ID
22555 * if one does not exist , it will get created.
22556 * @param {string} the navgroup to add
22557 * @returns {Roo.bootstrap.nav.Group} the navgroup
22559 get: function(navId) {
22560 if (typeof(this.groups[navId]) == 'undefined') {
22561 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22563 return this.groups[navId] ;
22578 * @class Roo.bootstrap.TabPanel
22579 * @extends Roo.bootstrap.Component
22580 * @children Roo.bootstrap.Component
22581 * Bootstrap TabPanel class
22582 * @cfg {Boolean} active panel active
22583 * @cfg {String} html panel content
22584 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22585 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22586 * @cfg {String} href click to link..
22587 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22591 * Create a new TabPanel
22592 * @param {Object} config The config object
22595 Roo.bootstrap.TabPanel = function(config){
22596 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22600 * Fires when the active status changes
22601 * @param {Roo.bootstrap.TabPanel} this
22602 * @param {Boolean} state the new state
22607 * @event beforedeactivate
22608 * Fires before a tab is de-activated - can be used to do validation on a form.
22609 * @param {Roo.bootstrap.TabPanel} this
22610 * @return {Boolean} false if there is an error
22613 'beforedeactivate': true
22616 this.tabId = this.tabId || Roo.id();
22620 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22627 touchSlide : false,
22628 getAutoCreate : function(){
22633 // item is needed for carousel - not sure if it has any effect otherwise
22634 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22635 html: this.html || ''
22639 cfg.cls += ' active';
22643 cfg.tabId = this.tabId;
22651 initEvents: function()
22653 var p = this.parent();
22655 this.navId = this.navId || p.navId;
22657 if (typeof(this.navId) != 'undefined') {
22658 // not really needed.. but just in case.. parent should be a NavGroup.
22659 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22663 var i = tg.tabs.length - 1;
22665 if(this.active && tg.bullets > 0 && i < tg.bullets){
22666 tg.setActiveBullet(i);
22670 this.el.on('click', this.onClick, this);
22672 if(Roo.isTouch && this.touchSlide){
22673 this.el.on("touchstart", this.onTouchStart, this);
22674 this.el.on("touchmove", this.onTouchMove, this);
22675 this.el.on("touchend", this.onTouchEnd, this);
22680 onRender : function(ct, position)
22682 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22685 setActive : function(state)
22687 Roo.log("panel - set active " + this.tabId + "=" + state);
22689 this.active = state;
22691 this.el.removeClass('active');
22693 } else if (!this.el.hasClass('active')) {
22694 this.el.addClass('active');
22697 this.fireEvent('changed', this, state);
22700 onClick : function(e)
22702 e.preventDefault();
22704 if(!this.href.length){
22708 window.location.href = this.href;
22717 onTouchStart : function(e)
22719 this.swiping = false;
22721 this.startX = e.browserEvent.touches[0].clientX;
22722 this.startY = e.browserEvent.touches[0].clientY;
22725 onTouchMove : function(e)
22727 this.swiping = true;
22729 this.endX = e.browserEvent.touches[0].clientX;
22730 this.endY = e.browserEvent.touches[0].clientY;
22733 onTouchEnd : function(e)
22740 var tabGroup = this.parent();
22742 if(this.endX > this.startX){ // swiping right
22743 tabGroup.showPanelPrev();
22747 if(this.startX > this.endX){ // swiping left
22748 tabGroup.showPanelNext();
22767 * @class Roo.bootstrap.form.DateField
22768 * @extends Roo.bootstrap.form.Input
22769 * Bootstrap DateField class
22770 * @cfg {Number} weekStart default 0
22771 * @cfg {String} viewMode default empty, (months|years)
22772 * @cfg {String} minViewMode default empty, (months|years)
22773 * @cfg {Number} startDate default -Infinity
22774 * @cfg {Number} endDate default Infinity
22775 * @cfg {Boolean} todayHighlight default false
22776 * @cfg {Boolean} todayBtn default false
22777 * @cfg {Boolean} calendarWeeks default false
22778 * @cfg {Object} daysOfWeekDisabled default empty
22779 * @cfg {Boolean} singleMode default false (true | false)
22781 * @cfg {Boolean} keyboardNavigation default true
22782 * @cfg {String} language default en
22785 * Create a new DateField
22786 * @param {Object} config The config object
22789 Roo.bootstrap.form.DateField = function(config){
22790 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22794 * Fires when this field show.
22795 * @param {Roo.bootstrap.form.DateField} this
22796 * @param {Mixed} date The date value
22801 * Fires when this field hide.
22802 * @param {Roo.bootstrap.form.DateField} this
22803 * @param {Mixed} date The date value
22808 * Fires when select a date.
22809 * @param {Roo.bootstrap.form.DateField} this
22810 * @param {Mixed} date The date value
22814 * @event beforeselect
22815 * Fires when before select a date.
22816 * @param {Roo.bootstrap.form.DateField} this
22817 * @param {Mixed} date The date value
22819 beforeselect : true
22823 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22826 * @cfg {String} format
22827 * The default date format string which can be overriden for localization support. The format must be
22828 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22832 * @cfg {String} altFormats
22833 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22834 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22836 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22844 todayHighlight : false,
22850 keyboardNavigation: true,
22852 calendarWeeks: false,
22854 startDate: -Infinity,
22858 daysOfWeekDisabled: [],
22862 singleMode : false,
22864 UTCDate: function()
22866 return new Date(Date.UTC.apply(Date, arguments));
22869 UTCToday: function()
22871 var today = new Date();
22872 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22875 getDate: function() {
22876 var d = this.getUTCDate();
22877 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22880 getUTCDate: function() {
22884 setDate: function(d) {
22885 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22888 setUTCDate: function(d) {
22890 this.setValue(this.formatDate(this.date));
22893 onRender: function(ct, position)
22896 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22898 this.language = this.language || 'en';
22899 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22900 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22902 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22903 this.format = this.format || 'm/d/y';
22904 this.isInline = false;
22905 this.isInput = true;
22906 this.component = this.el.select('.add-on', true).first() || false;
22907 this.component = (this.component && this.component.length === 0) ? false : this.component;
22908 this.hasInput = this.component && this.inputEl().length;
22910 if (typeof(this.minViewMode === 'string')) {
22911 switch (this.minViewMode) {
22913 this.minViewMode = 1;
22916 this.minViewMode = 2;
22919 this.minViewMode = 0;
22924 if (typeof(this.viewMode === 'string')) {
22925 switch (this.viewMode) {
22938 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22940 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22942 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22944 this.picker().on('mousedown', this.onMousedown, this);
22945 this.picker().on('click', this.onClick, this);
22947 this.picker().addClass('datepicker-dropdown');
22949 this.startViewMode = this.viewMode;
22951 if(this.singleMode){
22952 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22953 v.setVisibilityMode(Roo.Element.DISPLAY);
22957 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22958 v.setStyle('width', '189px');
22962 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22963 if(!this.calendarWeeks){
22968 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22969 v.attr('colspan', function(i, val){
22970 return parseInt(val) + 1;
22975 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22977 this.setStartDate(this.startDate);
22978 this.setEndDate(this.endDate);
22980 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22987 if(this.isInline) {
22992 picker : function()
22994 return this.pickerEl;
22995 // return this.el.select('.datepicker', true).first();
22998 fillDow: function()
23000 var dowCnt = this.weekStart;
23009 if(this.calendarWeeks){
23017 while (dowCnt < this.weekStart + 7) {
23021 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23025 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23028 fillMonths: function()
23031 var months = this.picker().select('>.datepicker-months td', true).first();
23033 months.dom.innerHTML = '';
23039 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23042 months.createChild(month);
23049 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;
23051 if (this.date < this.startDate) {
23052 this.viewDate = new Date(this.startDate);
23053 } else if (this.date > this.endDate) {
23054 this.viewDate = new Date(this.endDate);
23056 this.viewDate = new Date(this.date);
23064 var d = new Date(this.viewDate),
23065 year = d.getUTCFullYear(),
23066 month = d.getUTCMonth(),
23067 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23068 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23069 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23070 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23071 currentDate = this.date && this.date.valueOf(),
23072 today = this.UTCToday();
23074 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23076 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23078 // this.picker.select('>tfoot th.today').
23079 // .text(dates[this.language].today)
23080 // .toggle(this.todayBtn !== false);
23082 this.updateNavArrows();
23085 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23087 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23089 prevMonth.setUTCDate(day);
23091 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23093 var nextMonth = new Date(prevMonth);
23095 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23097 nextMonth = nextMonth.valueOf();
23099 var fillMonths = false;
23101 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23103 while(prevMonth.valueOf() <= nextMonth) {
23106 if (prevMonth.getUTCDay() === this.weekStart) {
23108 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23116 if(this.calendarWeeks){
23117 // ISO 8601: First week contains first thursday.
23118 // ISO also states week starts on Monday, but we can be more abstract here.
23120 // Start of current week: based on weekstart/current date
23121 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23122 // Thursday of this week
23123 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23124 // First Thursday of year, year from thursday
23125 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23126 // Calendar week: ms between thursdays, div ms per day, div 7 days
23127 calWeek = (th - yth) / 864e5 / 7 + 1;
23129 fillMonths.cn.push({
23137 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23139 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23142 if (this.todayHighlight &&
23143 prevMonth.getUTCFullYear() == today.getFullYear() &&
23144 prevMonth.getUTCMonth() == today.getMonth() &&
23145 prevMonth.getUTCDate() == today.getDate()) {
23146 clsName += ' today';
23149 if (currentDate && prevMonth.valueOf() === currentDate) {
23150 clsName += ' active';
23153 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23154 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23155 clsName += ' disabled';
23158 fillMonths.cn.push({
23160 cls: 'day ' + clsName,
23161 html: prevMonth.getDate()
23164 prevMonth.setDate(prevMonth.getDate()+1);
23167 var currentYear = this.date && this.date.getUTCFullYear();
23168 var currentMonth = this.date && this.date.getUTCMonth();
23170 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23172 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23173 v.removeClass('active');
23175 if(currentYear === year && k === currentMonth){
23176 v.addClass('active');
23179 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23180 v.addClass('disabled');
23186 year = parseInt(year/10, 10) * 10;
23188 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23190 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23193 for (var i = -1; i < 11; i++) {
23194 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23196 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23204 showMode: function(dir)
23207 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23210 Roo.each(this.picker().select('>div',true).elements, function(v){
23211 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23214 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23219 if(this.isInline) {
23223 this.picker().removeClass(['bottom', 'top']);
23225 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23227 * place to the top of element!
23231 this.picker().addClass('top');
23232 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23237 this.picker().addClass('bottom');
23239 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23242 parseDate : function(value)
23244 if(!value || value instanceof Date){
23247 var v = Date.parseDate(value, this.format);
23248 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23249 v = Date.parseDate(value, 'Y-m-d');
23251 if(!v && this.altFormats){
23252 if(!this.altFormatsArray){
23253 this.altFormatsArray = this.altFormats.split("|");
23255 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23256 v = Date.parseDate(value, this.altFormatsArray[i]);
23262 formatDate : function(date, fmt)
23264 return (!date || !(date instanceof Date)) ?
23265 date : date.dateFormat(fmt || this.format);
23268 onFocus : function()
23270 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23274 onBlur : function()
23276 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23278 var d = this.inputEl().getValue();
23285 showPopup : function()
23287 this.picker().show();
23291 this.fireEvent('showpopup', this, this.date);
23294 hidePopup : function()
23296 if(this.isInline) {
23299 this.picker().hide();
23300 this.viewMode = this.startViewMode;
23303 this.fireEvent('hidepopup', this, this.date);
23307 onMousedown: function(e)
23309 e.stopPropagation();
23310 e.preventDefault();
23315 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23319 setValue: function(v)
23321 if(this.fireEvent('beforeselect', this, v) !== false){
23322 var d = new Date(this.parseDate(v) ).clearTime();
23324 if(isNaN(d.getTime())){
23325 this.date = this.viewDate = '';
23326 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23330 v = this.formatDate(d);
23332 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23334 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23338 this.fireEvent('select', this, this.date);
23342 getValue: function()
23344 return this.formatDate(this.date);
23347 fireKey: function(e)
23349 if (!this.picker().isVisible()){
23350 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23356 var dateChanged = false,
23358 newDate, newViewDate;
23363 e.preventDefault();
23367 if (!this.keyboardNavigation) {
23370 dir = e.keyCode == 37 ? -1 : 1;
23373 newDate = this.moveYear(this.date, dir);
23374 newViewDate = this.moveYear(this.viewDate, dir);
23375 } else if (e.shiftKey){
23376 newDate = this.moveMonth(this.date, dir);
23377 newViewDate = this.moveMonth(this.viewDate, dir);
23379 newDate = new Date(this.date);
23380 newDate.setUTCDate(this.date.getUTCDate() + dir);
23381 newViewDate = new Date(this.viewDate);
23382 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23384 if (this.dateWithinRange(newDate)){
23385 this.date = newDate;
23386 this.viewDate = newViewDate;
23387 this.setValue(this.formatDate(this.date));
23389 e.preventDefault();
23390 dateChanged = true;
23395 if (!this.keyboardNavigation) {
23398 dir = e.keyCode == 38 ? -1 : 1;
23400 newDate = this.moveYear(this.date, dir);
23401 newViewDate = this.moveYear(this.viewDate, dir);
23402 } else if (e.shiftKey){
23403 newDate = this.moveMonth(this.date, dir);
23404 newViewDate = this.moveMonth(this.viewDate, dir);
23406 newDate = new Date(this.date);
23407 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23408 newViewDate = new Date(this.viewDate);
23409 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23411 if (this.dateWithinRange(newDate)){
23412 this.date = newDate;
23413 this.viewDate = newViewDate;
23414 this.setValue(this.formatDate(this.date));
23416 e.preventDefault();
23417 dateChanged = true;
23421 this.setValue(this.formatDate(this.date));
23423 e.preventDefault();
23426 this.setValue(this.formatDate(this.date));
23440 onClick: function(e)
23442 e.stopPropagation();
23443 e.preventDefault();
23445 var target = e.getTarget();
23447 if(target.nodeName.toLowerCase() === 'i'){
23448 target = Roo.get(target).dom.parentNode;
23451 var nodeName = target.nodeName;
23452 var className = target.className;
23453 var html = target.innerHTML;
23454 //Roo.log(nodeName);
23456 switch(nodeName.toLowerCase()) {
23458 switch(className) {
23464 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23465 switch(this.viewMode){
23467 this.viewDate = this.moveMonth(this.viewDate, dir);
23471 this.viewDate = this.moveYear(this.viewDate, dir);
23477 var date = new Date();
23478 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23480 this.setValue(this.formatDate(this.date));
23487 if (className.indexOf('disabled') < 0) {
23488 if (!this.viewDate) {
23489 this.viewDate = new Date();
23491 this.viewDate.setUTCDate(1);
23492 if (className.indexOf('month') > -1) {
23493 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23495 var year = parseInt(html, 10) || 0;
23496 this.viewDate.setUTCFullYear(year);
23500 if(this.singleMode){
23501 this.setValue(this.formatDate(this.viewDate));
23512 //Roo.log(className);
23513 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23514 var day = parseInt(html, 10) || 1;
23515 var year = (this.viewDate || new Date()).getUTCFullYear(),
23516 month = (this.viewDate || new Date()).getUTCMonth();
23518 if (className.indexOf('old') > -1) {
23525 } else if (className.indexOf('new') > -1) {
23533 //Roo.log([year,month,day]);
23534 this.date = this.UTCDate(year, month, day,0,0,0,0);
23535 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23537 //Roo.log(this.formatDate(this.date));
23538 this.setValue(this.formatDate(this.date));
23545 setStartDate: function(startDate)
23547 this.startDate = startDate || -Infinity;
23548 if (this.startDate !== -Infinity) {
23549 this.startDate = this.parseDate(this.startDate);
23552 this.updateNavArrows();
23555 setEndDate: function(endDate)
23557 this.endDate = endDate || Infinity;
23558 if (this.endDate !== Infinity) {
23559 this.endDate = this.parseDate(this.endDate);
23562 this.updateNavArrows();
23565 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23567 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23568 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23569 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23571 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23572 return parseInt(d, 10);
23575 this.updateNavArrows();
23578 updateNavArrows: function()
23580 if(this.singleMode){
23584 var d = new Date(this.viewDate),
23585 year = d.getUTCFullYear(),
23586 month = d.getUTCMonth();
23588 Roo.each(this.picker().select('.prev', true).elements, function(v){
23590 switch (this.viewMode) {
23593 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23599 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23606 Roo.each(this.picker().select('.next', true).elements, function(v){
23608 switch (this.viewMode) {
23611 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23617 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23625 moveMonth: function(date, dir)
23630 var new_date = new Date(date.valueOf()),
23631 day = new_date.getUTCDate(),
23632 month = new_date.getUTCMonth(),
23633 mag = Math.abs(dir),
23635 dir = dir > 0 ? 1 : -1;
23638 // If going back one month, make sure month is not current month
23639 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23641 return new_date.getUTCMonth() == month;
23643 // If going forward one month, make sure month is as expected
23644 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23646 return new_date.getUTCMonth() != new_month;
23648 new_month = month + dir;
23649 new_date.setUTCMonth(new_month);
23650 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23651 if (new_month < 0 || new_month > 11) {
23652 new_month = (new_month + 12) % 12;
23655 // For magnitudes >1, move one month at a time...
23656 for (var i=0; i<mag; i++) {
23657 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23658 new_date = this.moveMonth(new_date, dir);
23660 // ...then reset the day, keeping it in the new month
23661 new_month = new_date.getUTCMonth();
23662 new_date.setUTCDate(day);
23664 return new_month != new_date.getUTCMonth();
23667 // Common date-resetting loop -- if date is beyond end of month, make it
23670 new_date.setUTCDate(--day);
23671 new_date.setUTCMonth(new_month);
23676 moveYear: function(date, dir)
23678 return this.moveMonth(date, dir*12);
23681 dateWithinRange: function(date)
23683 return date >= this.startDate && date <= this.endDate;
23689 this.picker().remove();
23692 validateValue : function(value)
23694 if(this.getVisibilityEl().hasClass('hidden')){
23698 if(value.length < 1) {
23699 if(this.allowBlank){
23705 if(value.length < this.minLength){
23708 if(value.length > this.maxLength){
23712 var vt = Roo.form.VTypes;
23713 if(!vt[this.vtype](value, this)){
23717 if(typeof this.validator == "function"){
23718 var msg = this.validator(value);
23724 if(this.regex && !this.regex.test(value)){
23728 if(typeof(this.parseDate(value)) == 'undefined'){
23732 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23736 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23746 this.date = this.viewDate = '';
23748 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23753 Roo.apply(Roo.bootstrap.form.DateField, {
23764 html: '<i class="fa fa-arrow-left"/>'
23774 html: '<i class="fa fa-arrow-right"/>'
23816 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23817 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23818 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23819 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23820 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23833 navFnc: 'FullYear',
23838 navFnc: 'FullYear',
23843 Roo.apply(Roo.bootstrap.form.DateField, {
23847 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23851 cls: 'datepicker-days',
23855 cls: 'table-condensed',
23857 Roo.bootstrap.form.DateField.head,
23861 Roo.bootstrap.form.DateField.footer
23868 cls: 'datepicker-months',
23872 cls: 'table-condensed',
23874 Roo.bootstrap.form.DateField.head,
23875 Roo.bootstrap.form.DateField.content,
23876 Roo.bootstrap.form.DateField.footer
23883 cls: 'datepicker-years',
23887 cls: 'table-condensed',
23889 Roo.bootstrap.form.DateField.head,
23890 Roo.bootstrap.form.DateField.content,
23891 Roo.bootstrap.form.DateField.footer
23910 * @class Roo.bootstrap.form.TimeField
23911 * @extends Roo.bootstrap.form.Input
23912 * Bootstrap DateField class
23916 * Create a new TimeField
23917 * @param {Object} config The config object
23920 Roo.bootstrap.form.TimeField = function(config){
23921 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23925 * Fires when this field show.
23926 * @param {Roo.bootstrap.form.DateField} thisthis
23927 * @param {Mixed} date The date value
23932 * Fires when this field hide.
23933 * @param {Roo.bootstrap.form.DateField} this
23934 * @param {Mixed} date The date value
23939 * Fires when select a date.
23940 * @param {Roo.bootstrap.form.DateField} this
23941 * @param {Mixed} date The date value
23947 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23950 * @cfg {String} format
23951 * The default time format string which can be overriden for localization support. The format must be
23952 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23956 getAutoCreate : function()
23958 this.after = '<i class="fa far fa-clock"></i>';
23959 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23963 onRender: function(ct, position)
23966 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23968 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23970 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23972 this.pop = this.picker().select('>.datepicker-time',true).first();
23973 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23975 this.picker().on('mousedown', this.onMousedown, this);
23976 this.picker().on('click', this.onClick, this);
23978 this.picker().addClass('datepicker-dropdown');
23983 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23984 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23985 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23986 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23987 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23988 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23992 fireKey: function(e){
23993 if (!this.picker().isVisible()){
23994 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24000 e.preventDefault();
24008 this.onTogglePeriod();
24011 this.onIncrementMinutes();
24014 this.onDecrementMinutes();
24023 onClick: function(e) {
24024 e.stopPropagation();
24025 e.preventDefault();
24028 picker : function()
24030 return this.pickerEl;
24033 fillTime: function()
24035 var time = this.pop.select('tbody', true).first();
24037 time.dom.innerHTML = '';
24052 cls: 'hours-up fa fas fa-chevron-up'
24072 cls: 'minutes-up fa fas fa-chevron-up'
24093 cls: 'timepicker-hour',
24108 cls: 'timepicker-minute',
24123 cls: 'btn btn-primary period',
24145 cls: 'hours-down fa fas fa-chevron-down'
24165 cls: 'minutes-down fa fas fa-chevron-down'
24183 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24190 var hours = this.time.getHours();
24191 var minutes = this.time.getMinutes();
24204 hours = hours - 12;
24208 hours = '0' + hours;
24212 minutes = '0' + minutes;
24215 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24216 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24217 this.pop.select('button', true).first().dom.innerHTML = period;
24223 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24225 var cls = ['bottom'];
24227 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24234 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24238 //this.picker().setXY(20000,20000);
24239 this.picker().addClass(cls.join('-'));
24243 Roo.each(cls, function(c){
24248 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24249 //_this.picker().setTop(_this.inputEl().getHeight());
24253 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24255 //_this.picker().setTop(0 - _this.picker().getHeight());
24260 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24264 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24272 onFocus : function()
24274 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24278 onBlur : function()
24280 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24286 this.picker().show();
24291 this.fireEvent('show', this, this.date);
24296 this.picker().hide();
24299 this.fireEvent('hide', this, this.date);
24302 setTime : function()
24305 this.setValue(this.time.format(this.format));
24307 this.fireEvent('select', this, this.date);
24312 onMousedown: function(e){
24313 e.stopPropagation();
24314 e.preventDefault();
24317 onIncrementHours: function()
24319 Roo.log('onIncrementHours');
24320 this.time = this.time.add(Date.HOUR, 1);
24325 onDecrementHours: function()
24327 Roo.log('onDecrementHours');
24328 this.time = this.time.add(Date.HOUR, -1);
24332 onIncrementMinutes: function()
24334 Roo.log('onIncrementMinutes');
24335 this.time = this.time.add(Date.MINUTE, 1);
24339 onDecrementMinutes: function()
24341 Roo.log('onDecrementMinutes');
24342 this.time = this.time.add(Date.MINUTE, -1);
24346 onTogglePeriod: function()
24348 Roo.log('onTogglePeriod');
24349 this.time = this.time.add(Date.HOUR, 12);
24357 Roo.apply(Roo.bootstrap.form.TimeField, {
24361 cls: 'datepicker dropdown-menu',
24365 cls: 'datepicker-time',
24369 cls: 'table-condensed',
24398 cls: 'btn btn-info ok',
24426 * @class Roo.bootstrap.form.MonthField
24427 * @extends Roo.bootstrap.form.Input
24428 * Bootstrap MonthField class
24430 * @cfg {String} language default en
24433 * Create a new MonthField
24434 * @param {Object} config The config object
24437 Roo.bootstrap.form.MonthField = function(config){
24438 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24443 * Fires when this field show.
24444 * @param {Roo.bootstrap.form.MonthField} this
24445 * @param {Mixed} date The date value
24450 * Fires when this field hide.
24451 * @param {Roo.bootstrap.form.MonthField} this
24452 * @param {Mixed} date The date value
24457 * Fires when select a date.
24458 * @param {Roo.bootstrap.form.MonthField} this
24459 * @param {String} oldvalue The old value
24460 * @param {String} newvalue The new value
24466 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24468 onRender: function(ct, position)
24471 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24473 this.language = this.language || 'en';
24474 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24475 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24477 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24478 this.isInline = false;
24479 this.isInput = true;
24480 this.component = this.el.select('.add-on', true).first() || false;
24481 this.component = (this.component && this.component.length === 0) ? false : this.component;
24482 this.hasInput = this.component && this.inputEL().length;
24484 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24486 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24488 this.picker().on('mousedown', this.onMousedown, this);
24489 this.picker().on('click', this.onClick, this);
24491 this.picker().addClass('datepicker-dropdown');
24493 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24494 v.setStyle('width', '189px');
24501 if(this.isInline) {
24507 setValue: function(v, suppressEvent)
24509 var o = this.getValue();
24511 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24515 if(suppressEvent !== true){
24516 this.fireEvent('select', this, o, v);
24521 getValue: function()
24526 onClick: function(e)
24528 e.stopPropagation();
24529 e.preventDefault();
24531 var target = e.getTarget();
24533 if(target.nodeName.toLowerCase() === 'i'){
24534 target = Roo.get(target).dom.parentNode;
24537 var nodeName = target.nodeName;
24538 var className = target.className;
24539 var html = target.innerHTML;
24541 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24545 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24547 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24553 picker : function()
24555 return this.pickerEl;
24558 fillMonths: function()
24561 var months = this.picker().select('>.datepicker-months td', true).first();
24563 months.dom.innerHTML = '';
24569 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24572 months.createChild(month);
24581 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24582 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24585 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24586 e.removeClass('active');
24588 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24589 e.addClass('active');
24596 if(this.isInline) {
24600 this.picker().removeClass(['bottom', 'top']);
24602 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24604 * place to the top of element!
24608 this.picker().addClass('top');
24609 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24614 this.picker().addClass('bottom');
24616 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24619 onFocus : function()
24621 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24625 onBlur : function()
24627 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24629 var d = this.inputEl().getValue();
24638 this.picker().show();
24639 this.picker().select('>.datepicker-months', true).first().show();
24643 this.fireEvent('show', this, this.date);
24648 if(this.isInline) {
24651 this.picker().hide();
24652 this.fireEvent('hide', this, this.date);
24656 onMousedown: function(e)
24658 e.stopPropagation();
24659 e.preventDefault();
24664 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24668 fireKey: function(e)
24670 if (!this.picker().isVisible()){
24671 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24682 e.preventDefault();
24686 dir = e.keyCode == 37 ? -1 : 1;
24688 this.vIndex = this.vIndex + dir;
24690 if(this.vIndex < 0){
24694 if(this.vIndex > 11){
24698 if(isNaN(this.vIndex)){
24702 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24708 dir = e.keyCode == 38 ? -1 : 1;
24710 this.vIndex = this.vIndex + dir * 4;
24712 if(this.vIndex < 0){
24716 if(this.vIndex > 11){
24720 if(isNaN(this.vIndex)){
24724 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24729 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24730 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734 e.preventDefault();
24737 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24738 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24754 this.picker().remove();
24759 Roo.apply(Roo.bootstrap.form.MonthField, {
24778 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24779 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24784 Roo.apply(Roo.bootstrap.form.MonthField, {
24788 cls: 'datepicker dropdown-menu roo-dynamic',
24792 cls: 'datepicker-months',
24796 cls: 'table-condensed',
24798 Roo.bootstrap.form.DateField.content
24818 * @class Roo.bootstrap.form.CheckBox
24819 * @extends Roo.bootstrap.form.Input
24820 * Bootstrap CheckBox class
24822 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24823 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24824 * @cfg {String} boxLabel The text that appears beside the checkbox
24825 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24826 * @cfg {Boolean} checked initnal the element
24827 * @cfg {Boolean} inline inline the element (default false)
24828 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24829 * @cfg {String} tooltip label tooltip
24832 * Create a new CheckBox
24833 * @param {Object} config The config object
24836 Roo.bootstrap.form.CheckBox = function(config){
24837 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24842 * Fires when the element is checked or unchecked.
24843 * @param {Roo.bootstrap.form.CheckBox} this This input
24844 * @param {Boolean} checked The new checked value
24849 * Fires when the element is click.
24850 * @param {Roo.bootstrap.form.CheckBox} this This input
24857 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24859 inputType: 'checkbox',
24868 // checkbox success does not make any sense really..
24873 getAutoCreate : function()
24875 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24881 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24884 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24890 type : this.inputType,
24891 value : this.inputValue,
24892 cls : 'roo-' + this.inputType, //'form-box',
24893 placeholder : this.placeholder || ''
24897 if(this.inputType != 'radio'){
24901 cls : 'roo-hidden-value',
24902 value : this.checked ? this.inputValue : this.valueOff
24907 if (this.weight) { // Validity check?
24908 cfg.cls += " " + this.inputType + "-" + this.weight;
24911 if (this.disabled) {
24912 input.disabled=true;
24916 input.checked = this.checked;
24921 input.name = this.name;
24923 if(this.inputType != 'radio'){
24924 hidden.name = this.name;
24925 input.name = '_hidden_' + this.name;
24930 input.cls += ' input-' + this.size;
24935 ['xs','sm','md','lg'].map(function(size){
24936 if (settings[size]) {
24937 cfg.cls += ' col-' + size + '-' + settings[size];
24941 var inputblock = input;
24943 if (this.before || this.after) {
24946 cls : 'input-group',
24951 inputblock.cn.push({
24953 cls : 'input-group-addon',
24958 inputblock.cn.push(input);
24960 if(this.inputType != 'radio'){
24961 inputblock.cn.push(hidden);
24965 inputblock.cn.push({
24967 cls : 'input-group-addon',
24973 var boxLabelCfg = false;
24979 //'for': id, // box label is handled by onclick - so no for...
24981 html: this.boxLabel
24984 boxLabelCfg.tooltip = this.tooltip;
24990 if (align ==='left' && this.fieldLabel.length) {
24991 // Roo.log("left and has label");
24996 cls : 'control-label',
24997 html : this.fieldLabel
25008 cfg.cn[1].cn.push(boxLabelCfg);
25011 if(this.labelWidth > 12){
25012 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25015 if(this.labelWidth < 13 && this.labelmd == 0){
25016 this.labelmd = this.labelWidth;
25019 if(this.labellg > 0){
25020 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25021 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25024 if(this.labelmd > 0){
25025 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25026 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25029 if(this.labelsm > 0){
25030 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25031 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25034 if(this.labelxs > 0){
25035 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25036 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25039 } else if ( this.fieldLabel.length) {
25040 // Roo.log(" label");
25044 tag: this.boxLabel ? 'span' : 'label',
25046 cls: 'control-label box-input-label',
25047 //cls : 'input-group-addon',
25048 html : this.fieldLabel
25055 cfg.cn.push(boxLabelCfg);
25060 // Roo.log(" no label && no align");
25061 cfg.cn = [ inputblock ] ;
25063 cfg.cn.push(boxLabelCfg);
25071 if(this.inputType != 'radio'){
25072 cfg.cn.push(hidden);
25080 * return the real input element.
25082 inputEl: function ()
25084 return this.el.select('input.roo-' + this.inputType,true).first();
25086 hiddenEl: function ()
25088 return this.el.select('input.roo-hidden-value',true).first();
25091 labelEl: function()
25093 return this.el.select('label.control-label',true).first();
25095 /* depricated... */
25099 return this.labelEl();
25102 boxLabelEl: function()
25104 return this.el.select('label.box-label',true).first();
25107 initEvents : function()
25109 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25111 this.inputEl().on('click', this.onClick, this);
25113 if (this.boxLabel) {
25114 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25117 this.startValue = this.getValue();
25120 Roo.bootstrap.form.CheckBox.register(this);
25124 onClick : function(e)
25126 if(this.fireEvent('click', this, e) !== false){
25127 this.setChecked(!this.checked);
25132 setChecked : function(state,suppressEvent)
25134 this.startValue = this.getValue();
25136 if(this.inputType == 'radio'){
25138 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25139 e.dom.checked = false;
25142 this.inputEl().dom.checked = true;
25144 this.inputEl().dom.value = this.inputValue;
25146 if(suppressEvent !== true){
25147 this.fireEvent('check', this, true);
25155 this.checked = state;
25157 this.inputEl().dom.checked = state;
25160 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25162 if(suppressEvent !== true){
25163 this.fireEvent('check', this, state);
25169 getValue : function()
25171 if(this.inputType == 'radio'){
25172 return this.getGroupValue();
25175 return this.hiddenEl().dom.value;
25179 getGroupValue : function()
25181 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25185 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25188 setValue : function(v,suppressEvent)
25190 if(this.inputType == 'radio'){
25191 this.setGroupValue(v, suppressEvent);
25195 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25200 setGroupValue : function(v, suppressEvent)
25202 this.startValue = this.getValue();
25204 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25205 e.dom.checked = false;
25207 if(e.dom.value == v){
25208 e.dom.checked = true;
25212 if(suppressEvent !== true){
25213 this.fireEvent('check', this, true);
25221 validate : function()
25223 if(this.getVisibilityEl().hasClass('hidden')){
25229 (this.inputType == 'radio' && this.validateRadio()) ||
25230 (this.inputType == 'checkbox' && this.validateCheckbox())
25236 this.markInvalid();
25240 validateRadio : function()
25242 if(this.getVisibilityEl().hasClass('hidden')){
25246 if(this.allowBlank){
25252 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25253 if(!e.dom.checked){
25265 validateCheckbox : function()
25268 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25269 //return (this.getValue() == this.inputValue) ? true : false;
25272 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25280 for(var i in group){
25281 if(group[i].el.isVisible(true)){
25289 for(var i in group){
25294 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25301 * Mark this field as valid
25303 markValid : function()
25307 this.fireEvent('valid', this);
25309 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25312 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25319 if(this.inputType == 'radio'){
25320 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25321 var fg = e.findParent('.form-group', false, true);
25322 if (Roo.bootstrap.version == 3) {
25323 fg.removeClass([_this.invalidClass, _this.validClass]);
25324 fg.addClass(_this.validClass);
25326 fg.removeClass(['is-valid', 'is-invalid']);
25327 fg.addClass('is-valid');
25335 var fg = this.el.findParent('.form-group', false, true);
25336 if (Roo.bootstrap.version == 3) {
25337 fg.removeClass([this.invalidClass, this.validClass]);
25338 fg.addClass(this.validClass);
25340 fg.removeClass(['is-valid', 'is-invalid']);
25341 fg.addClass('is-valid');
25346 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25352 for(var i in group){
25353 var fg = group[i].el.findParent('.form-group', false, true);
25354 if (Roo.bootstrap.version == 3) {
25355 fg.removeClass([this.invalidClass, this.validClass]);
25356 fg.addClass(this.validClass);
25358 fg.removeClass(['is-valid', 'is-invalid']);
25359 fg.addClass('is-valid');
25365 * Mark this field as invalid
25366 * @param {String} msg The validation message
25368 markInvalid : function(msg)
25370 if(this.allowBlank){
25376 this.fireEvent('invalid', this, msg);
25378 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25381 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25385 label.markInvalid();
25388 if(this.inputType == 'radio'){
25390 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25391 var fg = e.findParent('.form-group', false, true);
25392 if (Roo.bootstrap.version == 3) {
25393 fg.removeClass([_this.invalidClass, _this.validClass]);
25394 fg.addClass(_this.invalidClass);
25396 fg.removeClass(['is-invalid', 'is-valid']);
25397 fg.addClass('is-invalid');
25405 var fg = this.el.findParent('.form-group', false, true);
25406 if (Roo.bootstrap.version == 3) {
25407 fg.removeClass([_this.invalidClass, _this.validClass]);
25408 fg.addClass(_this.invalidClass);
25410 fg.removeClass(['is-invalid', 'is-valid']);
25411 fg.addClass('is-invalid');
25416 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25422 for(var i in group){
25423 var fg = group[i].el.findParent('.form-group', false, true);
25424 if (Roo.bootstrap.version == 3) {
25425 fg.removeClass([_this.invalidClass, _this.validClass]);
25426 fg.addClass(_this.invalidClass);
25428 fg.removeClass(['is-invalid', 'is-valid']);
25429 fg.addClass('is-invalid');
25435 clearInvalid : function()
25437 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25439 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25441 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25443 if (label && label.iconEl) {
25444 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25445 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25449 disable : function()
25451 if(this.inputType != 'radio'){
25452 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25459 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25460 _this.getActionEl().addClass(this.disabledClass);
25461 e.dom.disabled = true;
25465 this.disabled = true;
25466 this.fireEvent("disable", this);
25470 enable : function()
25472 if(this.inputType != 'radio'){
25473 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25480 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25481 _this.getActionEl().removeClass(this.disabledClass);
25482 e.dom.disabled = false;
25486 this.disabled = false;
25487 this.fireEvent("enable", this);
25491 setBoxLabel : function(v)
25496 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25502 Roo.apply(Roo.bootstrap.form.CheckBox, {
25507 * register a CheckBox Group
25508 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25510 register : function(checkbox)
25512 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25513 this.groups[checkbox.groupId] = {};
25516 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25520 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25524 * fetch a CheckBox Group based on the group ID
25525 * @param {string} the group ID
25526 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25528 get: function(groupId) {
25529 if (typeof(this.groups[groupId]) == 'undefined') {
25533 return this.groups[groupId] ;
25546 * @class Roo.bootstrap.form.Radio
25547 * @extends Roo.bootstrap.Component
25548 * Bootstrap Radio class
25549 * @cfg {String} boxLabel - the label associated
25550 * @cfg {String} value - the value of radio
25553 * Create a new Radio
25554 * @param {Object} config The config object
25556 Roo.bootstrap.form.Radio = function(config){
25557 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25561 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25567 getAutoCreate : function()
25571 cls : 'form-group radio',
25576 html : this.boxLabel
25584 initEvents : function()
25586 this.parent().register(this);
25588 this.el.on('click', this.onClick, this);
25592 onClick : function(e)
25594 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25595 this.setChecked(true);
25599 setChecked : function(state, suppressEvent)
25601 this.parent().setValue(this.value, suppressEvent);
25605 setBoxLabel : function(v)
25610 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25625 * @class Roo.bootstrap.form.SecurePass
25626 * @extends Roo.bootstrap.form.Input
25627 * Bootstrap SecurePass class
25631 * Create a new SecurePass
25632 * @param {Object} config The config object
25635 Roo.bootstrap.form.SecurePass = function (config) {
25636 // these go here, so the translation tool can replace them..
25638 PwdEmpty: "Please type a password, and then retype it to confirm.",
25639 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25640 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25641 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25642 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25643 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25644 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25645 TooWeak: "Your password is Too Weak."
25647 this.meterLabel = "Password strength:";
25648 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25649 this.meterClass = [
25650 "roo-password-meter-tooweak",
25651 "roo-password-meter-weak",
25652 "roo-password-meter-medium",
25653 "roo-password-meter-strong",
25654 "roo-password-meter-grey"
25659 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25662 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25664 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25666 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25667 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25668 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25669 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25670 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25671 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25672 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25682 * @cfg {String/Object} Label for the strength meter (defaults to
25683 * 'Password strength:')
25688 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25689 * ['Weak', 'Medium', 'Strong'])
25692 pwdStrengths: false,
25705 initEvents: function ()
25707 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25709 if (this.el.is('input[type=password]') && Roo.isSafari) {
25710 this.el.on('keydown', this.SafariOnKeyDown, this);
25713 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25716 onRender: function (ct, position)
25718 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25719 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25720 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25722 this.trigger.createChild({
25727 cls: 'roo-password-meter-grey col-xs-12',
25730 //width: this.meterWidth + 'px'
25734 cls: 'roo-password-meter-text'
25740 if (this.hideTrigger) {
25741 this.trigger.setDisplayed(false);
25743 this.setSize(this.width || '', this.height || '');
25746 onDestroy: function ()
25748 if (this.trigger) {
25749 this.trigger.removeAllListeners();
25750 this.trigger.remove();
25753 this.wrap.remove();
25755 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25758 checkStrength: function ()
25760 var pwd = this.inputEl().getValue();
25761 if (pwd == this._lastPwd) {
25766 if (this.ClientSideStrongPassword(pwd)) {
25768 } else if (this.ClientSideMediumPassword(pwd)) {
25770 } else if (this.ClientSideWeakPassword(pwd)) {
25776 Roo.log('strength1: ' + strength);
25778 //var pm = this.trigger.child('div/div/div').dom;
25779 var pm = this.trigger.child('div/div');
25780 pm.removeClass(this.meterClass);
25781 pm.addClass(this.meterClass[strength]);
25784 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25786 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25788 this._lastPwd = pwd;
25792 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25794 this._lastPwd = '';
25796 var pm = this.trigger.child('div/div');
25797 pm.removeClass(this.meterClass);
25798 pm.addClass('roo-password-meter-grey');
25801 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25804 this.inputEl().dom.type='password';
25807 validateValue: function (value)
25809 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25812 if (value.length == 0) {
25813 if (this.allowBlank) {
25814 this.clearInvalid();
25818 this.markInvalid(this.errors.PwdEmpty);
25819 this.errorMsg = this.errors.PwdEmpty;
25827 if (!value.match(/[\x21-\x7e]+/)) {
25828 this.markInvalid(this.errors.PwdBadChar);
25829 this.errorMsg = this.errors.PwdBadChar;
25832 if (value.length < 6) {
25833 this.markInvalid(this.errors.PwdShort);
25834 this.errorMsg = this.errors.PwdShort;
25837 if (value.length > 16) {
25838 this.markInvalid(this.errors.PwdLong);
25839 this.errorMsg = this.errors.PwdLong;
25843 if (this.ClientSideStrongPassword(value)) {
25845 } else if (this.ClientSideMediumPassword(value)) {
25847 } else if (this.ClientSideWeakPassword(value)) {
25854 if (strength < 2) {
25855 //this.markInvalid(this.errors.TooWeak);
25856 this.errorMsg = this.errors.TooWeak;
25861 console.log('strength2: ' + strength);
25863 //var pm = this.trigger.child('div/div/div').dom;
25865 var pm = this.trigger.child('div/div');
25866 pm.removeClass(this.meterClass);
25867 pm.addClass(this.meterClass[strength]);
25869 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25871 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25873 this.errorMsg = '';
25877 CharacterSetChecks: function (type)
25880 this.fResult = false;
25883 isctype: function (character, type)
25886 case this.kCapitalLetter:
25887 if (character >= 'A' && character <= 'Z') {
25892 case this.kSmallLetter:
25893 if (character >= 'a' && character <= 'z') {
25899 if (character >= '0' && character <= '9') {
25904 case this.kPunctuation:
25905 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25916 IsLongEnough: function (pwd, size)
25918 return !(pwd == null || isNaN(size) || pwd.length < size);
25921 SpansEnoughCharacterSets: function (word, nb)
25923 if (!this.IsLongEnough(word, nb))
25928 var characterSetChecks = new Array(
25929 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25930 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25933 for (var index = 0; index < word.length; ++index) {
25934 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25935 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25936 characterSetChecks[nCharSet].fResult = true;
25943 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25944 if (characterSetChecks[nCharSet].fResult) {
25949 if (nCharSets < nb) {
25955 ClientSideStrongPassword: function (pwd)
25957 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25960 ClientSideMediumPassword: function (pwd)
25962 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25965 ClientSideWeakPassword: function (pwd)
25967 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25970 })//<script type="text/javascript">
25973 * Based Ext JS Library 1.1.1
25974 * Copyright(c) 2006-2007, Ext JS, LLC.
25980 * @class Roo.HtmlEditorCore
25981 * @extends Roo.Component
25982 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25984 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25987 Roo.HtmlEditorCore = function(config){
25990 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25995 * @event initialize
25996 * Fires when the editor is fully initialized (including the iframe)
25997 * @param {Roo.HtmlEditorCore} this
26002 * Fires when the editor is first receives the focus. Any insertion must wait
26003 * until after this event.
26004 * @param {Roo.HtmlEditorCore} this
26008 * @event beforesync
26009 * Fires before the textarea is updated with content from the editor iframe. Return false
26010 * to cancel the sync.
26011 * @param {Roo.HtmlEditorCore} this
26012 * @param {String} html
26016 * @event beforepush
26017 * Fires before the iframe editor is updated with content from the textarea. Return false
26018 * to cancel the push.
26019 * @param {Roo.HtmlEditorCore} this
26020 * @param {String} html
26025 * Fires when the textarea is updated with content from the editor iframe.
26026 * @param {Roo.HtmlEditorCore} this
26027 * @param {String} html
26032 * Fires when the iframe editor is updated with content from the textarea.
26033 * @param {Roo.HtmlEditorCore} this
26034 * @param {String} html
26039 * @event editorevent
26040 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26041 * @param {Roo.HtmlEditorCore} this
26047 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26049 // defaults : white / black...
26050 this.applyBlacklists();
26057 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26061 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26067 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26072 * @cfg {Number} height (in pixels)
26076 * @cfg {Number} width (in pixels)
26081 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26084 stylesheets: false,
26087 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26089 allowComments: false,
26093 // private properties
26094 validationEvent : false,
26096 initialized : false,
26098 sourceEditMode : false,
26099 onFocus : Roo.emptyFn,
26101 hideMode:'offsets',
26105 // blacklist + whitelisted elements..
26112 * Protected method that will not generally be called directly. It
26113 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26114 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26116 getDocMarkup : function(){
26120 // inherit styels from page...??
26121 if (this.stylesheets === false) {
26123 Roo.get(document.head).select('style').each(function(node) {
26124 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26127 Roo.get(document.head).select('link').each(function(node) {
26128 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26131 } else if (!this.stylesheets.length) {
26133 st = '<style type="text/css">' +
26134 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26137 for (var i in this.stylesheets) {
26138 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26143 st += '<style type="text/css">' +
26144 'IMG { cursor: pointer } ' +
26147 var cls = 'roo-htmleditor-body';
26149 if(this.bodyCls.length){
26150 cls += ' ' + this.bodyCls;
26153 return '<html><head>' + st +
26154 //<style type="text/css">' +
26155 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26157 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26161 onRender : function(ct, position)
26164 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26165 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26168 this.el.dom.style.border = '0 none';
26169 this.el.dom.setAttribute('tabIndex', -1);
26170 this.el.addClass('x-hidden hide');
26174 if(Roo.isIE){ // fix IE 1px bogus margin
26175 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26179 this.frameId = Roo.id();
26183 var iframe = this.owner.wrap.createChild({
26185 cls: 'form-control', // bootstrap..
26187 name: this.frameId,
26188 frameBorder : 'no',
26189 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26194 this.iframe = iframe.dom;
26196 this.assignDocWin();
26198 this.doc.designMode = 'on';
26201 this.doc.write(this.getDocMarkup());
26205 var task = { // must defer to wait for browser to be ready
26207 //console.log("run task?" + this.doc.readyState);
26208 this.assignDocWin();
26209 if(this.doc.body || this.doc.readyState == 'complete'){
26211 this.doc.designMode="on";
26215 Roo.TaskMgr.stop(task);
26216 this.initEditor.defer(10, this);
26223 Roo.TaskMgr.start(task);
26228 onResize : function(w, h)
26230 Roo.log('resize: ' +w + ',' + h );
26231 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26235 if(typeof w == 'number'){
26237 this.iframe.style.width = w + 'px';
26239 if(typeof h == 'number'){
26241 this.iframe.style.height = h + 'px';
26243 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26250 * Toggles the editor between standard and source edit mode.
26251 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26253 toggleSourceEdit : function(sourceEditMode){
26255 this.sourceEditMode = sourceEditMode === true;
26257 if(this.sourceEditMode){
26259 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26262 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26263 //this.iframe.className = '';
26266 //this.setSize(this.owner.wrap.getSize());
26267 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26274 * Protected method that will not generally be called directly. If you need/want
26275 * custom HTML cleanup, this is the method you should override.
26276 * @param {String} html The HTML to be cleaned
26277 * return {String} The cleaned HTML
26279 cleanHtml : function(html){
26280 html = String(html);
26281 if(html.length > 5){
26282 if(Roo.isSafari){ // strip safari nonsense
26283 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26286 if(html == ' '){
26293 * HTML Editor -> Textarea
26294 * Protected method that will not generally be called directly. Syncs the contents
26295 * of the editor iframe with the textarea.
26297 syncValue : function(){
26298 if(this.initialized){
26299 var bd = (this.doc.body || this.doc.documentElement);
26300 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26301 var html = bd.innerHTML;
26303 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26304 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26306 html = '<div style="'+m[0]+'">' + html + '</div>';
26309 html = this.cleanHtml(html);
26310 // fix up the special chars.. normaly like back quotes in word...
26311 // however we do not want to do this with chinese..
26312 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26314 var cc = match.charCodeAt();
26316 // Get the character value, handling surrogate pairs
26317 if (match.length == 2) {
26318 // It's a surrogate pair, calculate the Unicode code point
26319 var high = match.charCodeAt(0) - 0xD800;
26320 var low = match.charCodeAt(1) - 0xDC00;
26321 cc = (high * 0x400) + low + 0x10000;
26323 (cc >= 0x4E00 && cc < 0xA000 ) ||
26324 (cc >= 0x3400 && cc < 0x4E00 ) ||
26325 (cc >= 0xf900 && cc < 0xfb00 )
26330 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26331 return "&#" + cc + ";";
26338 if(this.owner.fireEvent('beforesync', this, html) !== false){
26339 this.el.dom.value = html;
26340 this.owner.fireEvent('sync', this, html);
26346 * Protected method that will not generally be called directly. Pushes the value of the textarea
26347 * into the iframe editor.
26349 pushValue : function(){
26350 if(this.initialized){
26351 var v = this.el.dom.value.trim();
26353 // if(v.length < 1){
26357 if(this.owner.fireEvent('beforepush', this, v) !== false){
26358 var d = (this.doc.body || this.doc.documentElement);
26360 this.cleanUpPaste();
26361 this.el.dom.value = d.innerHTML;
26362 this.owner.fireEvent('push', this, v);
26368 deferFocus : function(){
26369 this.focus.defer(10, this);
26373 focus : function(){
26374 if(this.win && !this.sourceEditMode){
26381 assignDocWin: function()
26383 var iframe = this.iframe;
26386 this.doc = iframe.contentWindow.document;
26387 this.win = iframe.contentWindow;
26389 // if (!Roo.get(this.frameId)) {
26392 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26393 // this.win = Roo.get(this.frameId).dom.contentWindow;
26395 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26399 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26400 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26405 initEditor : function(){
26406 //console.log("INIT EDITOR");
26407 this.assignDocWin();
26411 this.doc.designMode="on";
26413 this.doc.write(this.getDocMarkup());
26416 var dbody = (this.doc.body || this.doc.documentElement);
26417 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26418 // this copies styles from the containing element into thsi one..
26419 // not sure why we need all of this..
26420 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26422 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26423 //ss['background-attachment'] = 'fixed'; // w3c
26424 dbody.bgProperties = 'fixed'; // ie
26425 //Roo.DomHelper.applyStyles(dbody, ss);
26426 Roo.EventManager.on(this.doc, {
26427 //'mousedown': this.onEditorEvent,
26428 'mouseup': this.onEditorEvent,
26429 'dblclick': this.onEditorEvent,
26430 'click': this.onEditorEvent,
26431 'keyup': this.onEditorEvent,
26436 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26438 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26439 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26441 this.initialized = true;
26443 this.owner.fireEvent('initialize', this);
26448 onDestroy : function(){
26454 //for (var i =0; i < this.toolbars.length;i++) {
26455 // // fixme - ask toolbars for heights?
26456 // this.toolbars[i].onDestroy();
26459 //this.wrap.dom.innerHTML = '';
26460 //this.wrap.remove();
26465 onFirstFocus : function(){
26467 this.assignDocWin();
26470 this.activated = true;
26473 if(Roo.isGecko){ // prevent silly gecko errors
26475 var s = this.win.getSelection();
26476 if(!s.focusNode || s.focusNode.nodeType != 3){
26477 var r = s.getRangeAt(0);
26478 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26483 this.execCmd('useCSS', true);
26484 this.execCmd('styleWithCSS', false);
26487 this.owner.fireEvent('activate', this);
26491 adjustFont: function(btn){
26492 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26493 //if(Roo.isSafari){ // safari
26496 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26497 if(Roo.isSafari){ // safari
26498 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26499 v = (v < 10) ? 10 : v;
26500 v = (v > 48) ? 48 : v;
26501 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26506 v = Math.max(1, v+adjust);
26508 this.execCmd('FontSize', v );
26511 onEditorEvent : function(e)
26513 this.owner.fireEvent('editorevent', this, e);
26514 // this.updateToolbar();
26515 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26518 insertTag : function(tg)
26520 // could be a bit smarter... -> wrap the current selected tRoo..
26521 if (tg.toLowerCase() == 'span' ||
26522 tg.toLowerCase() == 'code' ||
26523 tg.toLowerCase() == 'sup' ||
26524 tg.toLowerCase() == 'sub'
26527 range = this.createRange(this.getSelection());
26528 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26529 wrappingNode.appendChild(range.extractContents());
26530 range.insertNode(wrappingNode);
26537 this.execCmd("formatblock", tg);
26541 insertText : function(txt)
26545 var range = this.createRange();
26546 range.deleteContents();
26547 //alert(Sender.getAttribute('label'));
26549 range.insertNode(this.doc.createTextNode(txt));
26555 * Executes a Midas editor command on the editor document and performs necessary focus and
26556 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26557 * @param {String} cmd The Midas command
26558 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26560 relayCmd : function(cmd, value){
26562 this.execCmd(cmd, value);
26563 this.owner.fireEvent('editorevent', this);
26564 //this.updateToolbar();
26565 this.owner.deferFocus();
26569 * Executes a Midas editor command directly on the editor document.
26570 * For visual commands, you should use {@link #relayCmd} instead.
26571 * <b>This should only be called after the editor is initialized.</b>
26572 * @param {String} cmd The Midas command
26573 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26575 execCmd : function(cmd, value){
26576 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26583 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26585 * @param {String} text | dom node..
26587 insertAtCursor : function(text)
26590 if(!this.activated){
26596 var r = this.doc.selection.createRange();
26607 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26611 // from jquery ui (MIT licenced)
26613 var win = this.win;
26615 if (win.getSelection && win.getSelection().getRangeAt) {
26616 range = win.getSelection().getRangeAt(0);
26617 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26618 range.insertNode(node);
26619 } else if (win.document.selection && win.document.selection.createRange) {
26620 // no firefox support
26621 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26622 win.document.selection.createRange().pasteHTML(txt);
26624 // no firefox support
26625 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26626 this.execCmd('InsertHTML', txt);
26635 mozKeyPress : function(e){
26637 var c = e.getCharCode(), cmd;
26640 c = String.fromCharCode(c).toLowerCase();
26654 this.cleanUpPaste.defer(100, this);
26662 e.preventDefault();
26670 fixKeys : function(){ // load time branching for fastest keydown performance
26672 return function(e){
26673 var k = e.getKey(), r;
26676 r = this.doc.selection.createRange();
26679 r.pasteHTML('    ');
26686 r = this.doc.selection.createRange();
26688 var target = r.parentElement();
26689 if(!target || target.tagName.toLowerCase() != 'li'){
26691 r.pasteHTML('<br />');
26697 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26698 this.cleanUpPaste.defer(100, this);
26704 }else if(Roo.isOpera){
26705 return function(e){
26706 var k = e.getKey();
26710 this.execCmd('InsertHTML','    ');
26713 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26714 this.cleanUpPaste.defer(100, this);
26719 }else if(Roo.isSafari){
26720 return function(e){
26721 var k = e.getKey();
26725 this.execCmd('InsertText','\t');
26729 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26730 this.cleanUpPaste.defer(100, this);
26738 getAllAncestors: function()
26740 var p = this.getSelectedNode();
26743 a.push(p); // push blank onto stack..
26744 p = this.getParentElement();
26748 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26752 a.push(this.doc.body);
26756 lastSelNode : false,
26759 getSelection : function()
26761 this.assignDocWin();
26762 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26765 getSelectedNode: function()
26767 // this may only work on Gecko!!!
26769 // should we cache this!!!!
26774 var range = this.createRange(this.getSelection()).cloneRange();
26777 var parent = range.parentElement();
26779 var testRange = range.duplicate();
26780 testRange.moveToElementText(parent);
26781 if (testRange.inRange(range)) {
26784 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26787 parent = parent.parentElement;
26792 // is ancestor a text element.
26793 var ac = range.commonAncestorContainer;
26794 if (ac.nodeType == 3) {
26795 ac = ac.parentNode;
26798 var ar = ac.childNodes;
26801 var other_nodes = [];
26802 var has_other_nodes = false;
26803 for (var i=0;i<ar.length;i++) {
26804 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26807 // fullly contained node.
26809 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26814 // probably selected..
26815 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26816 other_nodes.push(ar[i]);
26820 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26825 has_other_nodes = true;
26827 if (!nodes.length && other_nodes.length) {
26828 nodes= other_nodes;
26830 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26836 createRange: function(sel)
26838 // this has strange effects when using with
26839 // top toolbar - not sure if it's a great idea.
26840 //this.editor.contentWindow.focus();
26841 if (typeof sel != "undefined") {
26843 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26845 return this.doc.createRange();
26848 return this.doc.createRange();
26851 getParentElement: function()
26854 this.assignDocWin();
26855 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26857 var range = this.createRange(sel);
26860 var p = range.commonAncestorContainer;
26861 while (p.nodeType == 3) { // text node
26872 * Range intersection.. the hard stuff...
26876 * [ -- selected range --- ]
26880 * if end is before start or hits it. fail.
26881 * if start is after end or hits it fail.
26883 * if either hits (but other is outside. - then it's not
26889 // @see http://www.thismuchiknow.co.uk/?p=64.
26890 rangeIntersectsNode : function(range, node)
26892 var nodeRange = node.ownerDocument.createRange();
26894 nodeRange.selectNode(node);
26896 nodeRange.selectNodeContents(node);
26899 var rangeStartRange = range.cloneRange();
26900 rangeStartRange.collapse(true);
26902 var rangeEndRange = range.cloneRange();
26903 rangeEndRange.collapse(false);
26905 var nodeStartRange = nodeRange.cloneRange();
26906 nodeStartRange.collapse(true);
26908 var nodeEndRange = nodeRange.cloneRange();
26909 nodeEndRange.collapse(false);
26911 return rangeStartRange.compareBoundaryPoints(
26912 Range.START_TO_START, nodeEndRange) == -1 &&
26913 rangeEndRange.compareBoundaryPoints(
26914 Range.START_TO_START, nodeStartRange) == 1;
26918 rangeCompareNode : function(range, node)
26920 var nodeRange = node.ownerDocument.createRange();
26922 nodeRange.selectNode(node);
26924 nodeRange.selectNodeContents(node);
26928 range.collapse(true);
26930 nodeRange.collapse(true);
26932 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26933 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26935 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26937 var nodeIsBefore = ss == 1;
26938 var nodeIsAfter = ee == -1;
26940 if (nodeIsBefore && nodeIsAfter) {
26943 if (!nodeIsBefore && nodeIsAfter) {
26944 return 1; //right trailed.
26947 if (nodeIsBefore && !nodeIsAfter) {
26948 return 2; // left trailed.
26954 // private? - in a new class?
26955 cleanUpPaste : function()
26957 // cleans up the whole document..
26958 Roo.log('cleanuppaste');
26960 this.cleanUpChildren(this.doc.body);
26961 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26962 if (clean != this.doc.body.innerHTML) {
26963 this.doc.body.innerHTML = clean;
26968 cleanWordChars : function(input) {// change the chars to hex code
26969 var he = Roo.HtmlEditorCore;
26971 var output = input;
26972 Roo.each(he.swapCodes, function(sw) {
26973 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26975 output = output.replace(swapper, sw[1]);
26982 cleanUpChildren : function (n)
26984 if (!n.childNodes.length) {
26987 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26988 this.cleanUpChild(n.childNodes[i]);
26995 cleanUpChild : function (node)
26998 //console.log(node);
26999 if (node.nodeName == "#text") {
27000 // clean up silly Windows -- stuff?
27003 if (node.nodeName == "#comment") {
27004 if (!this.allowComments) {
27005 node.parentNode.removeChild(node);
27007 // clean up silly Windows -- stuff?
27010 var lcname = node.tagName.toLowerCase();
27011 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27012 // whitelist of tags..
27014 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27016 node.parentNode.removeChild(node);
27021 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27023 // spans with no attributes - just remove them..
27024 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27025 remove_keep_children = true;
27028 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27029 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27031 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27032 // remove_keep_children = true;
27035 if (remove_keep_children) {
27036 this.cleanUpChildren(node);
27037 // inserts everything just before this node...
27038 while (node.childNodes.length) {
27039 var cn = node.childNodes[0];
27040 node.removeChild(cn);
27041 node.parentNode.insertBefore(cn, node);
27043 node.parentNode.removeChild(node);
27047 if (!node.attributes || !node.attributes.length) {
27052 this.cleanUpChildren(node);
27056 function cleanAttr(n,v)
27059 if (v.match(/^\./) || v.match(/^\//)) {
27062 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27065 if (v.match(/^#/)) {
27068 if (v.match(/^\{/)) { // allow template editing.
27071 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27072 node.removeAttribute(n);
27076 var cwhite = this.cwhite;
27077 var cblack = this.cblack;
27079 function cleanStyle(n,v)
27081 if (v.match(/expression/)) { //XSS?? should we even bother..
27082 node.removeAttribute(n);
27086 var parts = v.split(/;/);
27089 Roo.each(parts, function(p) {
27090 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27094 var l = p.split(':').shift().replace(/\s+/g,'');
27095 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27097 if ( cwhite.length && cblack.indexOf(l) > -1) {
27098 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27099 //node.removeAttribute(n);
27103 // only allow 'c whitelisted system attributes'
27104 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27105 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27106 //node.removeAttribute(n);
27116 if (clean.length) {
27117 node.setAttribute(n, clean.join(';'));
27119 node.removeAttribute(n);
27125 for (var i = node.attributes.length-1; i > -1 ; i--) {
27126 var a = node.attributes[i];
27129 if (a.name.toLowerCase().substr(0,2)=='on') {
27130 node.removeAttribute(a.name);
27133 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27134 node.removeAttribute(a.name);
27137 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27138 cleanAttr(a.name,a.value); // fixme..
27141 if (a.name == 'style') {
27142 cleanStyle(a.name,a.value);
27145 /// clean up MS crap..
27146 // tecnically this should be a list of valid class'es..
27149 if (a.name == 'class') {
27150 if (a.value.match(/^Mso/)) {
27151 node.removeAttribute('class');
27154 if (a.value.match(/^body$/)) {
27155 node.removeAttribute('class');
27166 this.cleanUpChildren(node);
27172 * Clean up MS wordisms...
27174 cleanWord : function(node)
27177 this.cleanWord(this.doc.body);
27182 node.nodeName == 'SPAN' &&
27183 !node.hasAttributes() &&
27184 node.childNodes.length == 1 &&
27185 node.firstChild.nodeName == "#text"
27187 var textNode = node.firstChild;
27188 node.removeChild(textNode);
27189 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27190 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27192 node.parentNode.insertBefore(textNode, node);
27193 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27194 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27196 node.parentNode.removeChild(node);
27199 if (node.nodeName == "#text") {
27200 // clean up silly Windows -- stuff?
27203 if (node.nodeName == "#comment") {
27204 node.parentNode.removeChild(node);
27205 // clean up silly Windows -- stuff?
27209 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27210 node.parentNode.removeChild(node);
27213 //Roo.log(node.tagName);
27214 // remove - but keep children..
27215 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27216 //Roo.log('-- removed');
27217 while (node.childNodes.length) {
27218 var cn = node.childNodes[0];
27219 node.removeChild(cn);
27220 node.parentNode.insertBefore(cn, node);
27221 // move node to parent - and clean it..
27222 this.cleanWord(cn);
27224 node.parentNode.removeChild(node);
27225 /// no need to iterate chidlren = it's got none..
27226 //this.iterateChildren(node, this.cleanWord);
27230 if (node.className.length) {
27232 var cn = node.className.split(/\W+/);
27234 Roo.each(cn, function(cls) {
27235 if (cls.match(/Mso[a-zA-Z]+/)) {
27240 node.className = cna.length ? cna.join(' ') : '';
27242 node.removeAttribute("class");
27246 if (node.hasAttribute("lang")) {
27247 node.removeAttribute("lang");
27250 if (node.hasAttribute("style")) {
27252 var styles = node.getAttribute("style").split(";");
27254 Roo.each(styles, function(s) {
27255 if (!s.match(/:/)) {
27258 var kv = s.split(":");
27259 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27262 // what ever is left... we allow.
27265 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27266 if (!nstyle.length) {
27267 node.removeAttribute('style');
27270 this.iterateChildren(node, this.cleanWord);
27276 * iterateChildren of a Node, calling fn each time, using this as the scole..
27277 * @param {DomNode} node node to iterate children of.
27278 * @param {Function} fn method of this class to call on each item.
27280 iterateChildren : function(node, fn)
27282 if (!node.childNodes.length) {
27285 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27286 fn.call(this, node.childNodes[i])
27292 * cleanTableWidths.
27294 * Quite often pasting from word etc.. results in tables with column and widths.
27295 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27298 cleanTableWidths : function(node)
27303 this.cleanTableWidths(this.doc.body);
27308 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27311 Roo.log(node.tagName);
27312 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27313 this.iterateChildren(node, this.cleanTableWidths);
27316 if (node.hasAttribute('width')) {
27317 node.removeAttribute('width');
27321 if (node.hasAttribute("style")) {
27324 var styles = node.getAttribute("style").split(";");
27326 Roo.each(styles, function(s) {
27327 if (!s.match(/:/)) {
27330 var kv = s.split(":");
27331 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27334 // what ever is left... we allow.
27337 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27338 if (!nstyle.length) {
27339 node.removeAttribute('style');
27343 this.iterateChildren(node, this.cleanTableWidths);
27351 domToHTML : function(currentElement, depth, nopadtext) {
27353 depth = depth || 0;
27354 nopadtext = nopadtext || false;
27356 if (!currentElement) {
27357 return this.domToHTML(this.doc.body);
27360 //Roo.log(currentElement);
27362 var allText = false;
27363 var nodeName = currentElement.nodeName;
27364 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27366 if (nodeName == '#text') {
27368 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27373 if (nodeName != 'BODY') {
27376 // Prints the node tagName, such as <A>, <IMG>, etc
27379 for(i = 0; i < currentElement.attributes.length;i++) {
27381 var aname = currentElement.attributes.item(i).name;
27382 if (!currentElement.attributes.item(i).value.length) {
27385 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27388 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27397 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27400 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27405 // Traverse the tree
27407 var currentElementChild = currentElement.childNodes.item(i);
27408 var allText = true;
27409 var innerHTML = '';
27411 while (currentElementChild) {
27412 // Formatting code (indent the tree so it looks nice on the screen)
27413 var nopad = nopadtext;
27414 if (lastnode == 'SPAN') {
27418 if (currentElementChild.nodeName == '#text') {
27419 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27420 toadd = nopadtext ? toadd : toadd.trim();
27421 if (!nopad && toadd.length > 80) {
27422 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27424 innerHTML += toadd;
27427 currentElementChild = currentElement.childNodes.item(i);
27433 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27435 // Recursively traverse the tree structure of the child node
27436 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27437 lastnode = currentElementChild.nodeName;
27439 currentElementChild=currentElement.childNodes.item(i);
27445 // The remaining code is mostly for formatting the tree
27446 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27451 ret+= "</"+tagName+">";
27457 applyBlacklists : function()
27459 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27460 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27464 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27465 if (b.indexOf(tag) > -1) {
27468 this.white.push(tag);
27472 Roo.each(w, function(tag) {
27473 if (b.indexOf(tag) > -1) {
27476 if (this.white.indexOf(tag) > -1) {
27479 this.white.push(tag);
27484 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27485 if (w.indexOf(tag) > -1) {
27488 this.black.push(tag);
27492 Roo.each(b, function(tag) {
27493 if (w.indexOf(tag) > -1) {
27496 if (this.black.indexOf(tag) > -1) {
27499 this.black.push(tag);
27504 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27505 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27509 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27510 if (b.indexOf(tag) > -1) {
27513 this.cwhite.push(tag);
27517 Roo.each(w, function(tag) {
27518 if (b.indexOf(tag) > -1) {
27521 if (this.cwhite.indexOf(tag) > -1) {
27524 this.cwhite.push(tag);
27529 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27530 if (w.indexOf(tag) > -1) {
27533 this.cblack.push(tag);
27537 Roo.each(b, function(tag) {
27538 if (w.indexOf(tag) > -1) {
27541 if (this.cblack.indexOf(tag) > -1) {
27544 this.cblack.push(tag);
27549 setStylesheets : function(stylesheets)
27551 if(typeof(stylesheets) == 'string'){
27552 Roo.get(this.iframe.contentDocument.head).createChild({
27554 rel : 'stylesheet',
27563 Roo.each(stylesheets, function(s) {
27568 Roo.get(_this.iframe.contentDocument.head).createChild({
27570 rel : 'stylesheet',
27579 removeStylesheets : function()
27583 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27588 setStyle : function(style)
27590 Roo.get(this.iframe.contentDocument.head).createChild({
27599 // hide stuff that is not compatible
27613 * @event specialkey
27617 * @cfg {String} fieldClass @hide
27620 * @cfg {String} focusClass @hide
27623 * @cfg {String} autoCreate @hide
27626 * @cfg {String} inputType @hide
27629 * @cfg {String} invalidClass @hide
27632 * @cfg {String} invalidText @hide
27635 * @cfg {String} msgFx @hide
27638 * @cfg {String} validateOnBlur @hide
27642 Roo.HtmlEditorCore.white = [
27643 'area', 'br', 'img', 'input', 'hr', 'wbr',
27645 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27646 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27647 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27648 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27649 'table', 'ul', 'xmp',
27651 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27654 'dir', 'menu', 'ol', 'ul', 'dl',
27660 Roo.HtmlEditorCore.black = [
27661 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27663 'base', 'basefont', 'bgsound', 'blink', 'body',
27664 'frame', 'frameset', 'head', 'html', 'ilayer',
27665 'iframe', 'layer', 'link', 'meta', 'object',
27666 'script', 'style' ,'title', 'xml' // clean later..
27668 Roo.HtmlEditorCore.clean = [
27669 'script', 'style', 'title', 'xml'
27671 Roo.HtmlEditorCore.remove = [
27676 Roo.HtmlEditorCore.ablack = [
27680 Roo.HtmlEditorCore.aclean = [
27681 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27685 Roo.HtmlEditorCore.pwhite= [
27686 'http', 'https', 'mailto'
27689 // white listed style attributes.
27690 Roo.HtmlEditorCore.cwhite= [
27691 // 'text-align', /// default is to allow most things..
27697 // black listed style attributes.
27698 Roo.HtmlEditorCore.cblack= [
27699 // 'font-size' -- this can be set by the project
27703 Roo.HtmlEditorCore.swapCodes =[
27704 [ 8211, "–" ],
27705 [ 8212, "—" ],
27722 * @class Roo.bootstrap.form.HtmlEditor
27723 * @extends Roo.bootstrap.form.TextArea
27724 * Bootstrap HtmlEditor class
27727 * Create a new HtmlEditor
27728 * @param {Object} config The config object
27731 Roo.bootstrap.form.HtmlEditor = function(config){
27732 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27733 if (!this.toolbars) {
27734 this.toolbars = [];
27737 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27740 * @event initialize
27741 * Fires when the editor is fully initialized (including the iframe)
27742 * @param {HtmlEditor} this
27747 * Fires when the editor is first receives the focus. Any insertion must wait
27748 * until after this event.
27749 * @param {HtmlEditor} this
27753 * @event beforesync
27754 * Fires before the textarea is updated with content from the editor iframe. Return false
27755 * to cancel the sync.
27756 * @param {HtmlEditor} this
27757 * @param {String} html
27761 * @event beforepush
27762 * Fires before the iframe editor is updated with content from the textarea. Return false
27763 * to cancel the push.
27764 * @param {HtmlEditor} this
27765 * @param {String} html
27770 * Fires when the textarea is updated with content from the editor iframe.
27771 * @param {HtmlEditor} this
27772 * @param {String} html
27777 * Fires when the iframe editor is updated with content from the textarea.
27778 * @param {HtmlEditor} this
27779 * @param {String} html
27783 * @event editmodechange
27784 * Fires when the editor switches edit modes
27785 * @param {HtmlEditor} this
27786 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27788 editmodechange: true,
27790 * @event editorevent
27791 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27792 * @param {HtmlEditor} this
27796 * @event firstfocus
27797 * Fires when on first focus - needed by toolbars..
27798 * @param {HtmlEditor} this
27803 * Auto save the htmlEditor value as a file into Events
27804 * @param {HtmlEditor} this
27808 * @event savedpreview
27809 * preview the saved version of htmlEditor
27810 * @param {HtmlEditor} this
27817 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27821 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27826 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27831 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27836 * @cfg {Number} height (in pixels)
27840 * @cfg {Number} width (in pixels)
27845 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27848 stylesheets: false,
27853 // private properties
27854 validationEvent : false,
27856 initialized : false,
27859 onFocus : Roo.emptyFn,
27861 hideMode:'offsets',
27863 tbContainer : false,
27867 toolbarContainer :function() {
27868 return this.wrap.select('.x-html-editor-tb',true).first();
27872 * Protected method that will not generally be called directly. It
27873 * is called when the editor creates its toolbar. Override this method if you need to
27874 * add custom toolbar buttons.
27875 * @param {HtmlEditor} editor
27877 createToolbar : function(){
27878 Roo.log('renewing');
27879 Roo.log("create toolbars");
27881 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27882 this.toolbars[0].render(this.toolbarContainer());
27886 // if (!editor.toolbars || !editor.toolbars.length) {
27887 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27890 // for (var i =0 ; i < editor.toolbars.length;i++) {
27891 // editor.toolbars[i] = Roo.factory(
27892 // typeof(editor.toolbars[i]) == 'string' ?
27893 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27894 // Roo.bootstrap.form.HtmlEditor);
27895 // editor.toolbars[i].init(editor);
27901 onRender : function(ct, position)
27903 // Roo.log("Call onRender: " + this.xtype);
27905 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27907 this.wrap = this.inputEl().wrap({
27908 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27911 this.editorcore.onRender(ct, position);
27913 if (this.resizable) {
27914 this.resizeEl = new Roo.Resizable(this.wrap, {
27918 minHeight : this.height,
27919 height: this.height,
27920 handles : this.resizable,
27923 resize : function(r, w, h) {
27924 _t.onResize(w,h); // -something
27930 this.createToolbar(this);
27933 if(!this.width && this.resizable){
27934 this.setSize(this.wrap.getSize());
27936 if (this.resizeEl) {
27937 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27938 // should trigger onReize..
27944 onResize : function(w, h)
27946 Roo.log('resize: ' +w + ',' + h );
27947 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27951 if(this.inputEl() ){
27952 if(typeof w == 'number'){
27953 var aw = w - this.wrap.getFrameWidth('lr');
27954 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27957 if(typeof h == 'number'){
27958 var tbh = -11; // fixme it needs to tool bar size!
27959 for (var i =0; i < this.toolbars.length;i++) {
27960 // fixme - ask toolbars for heights?
27961 tbh += this.toolbars[i].el.getHeight();
27962 //if (this.toolbars[i].footer) {
27963 // tbh += this.toolbars[i].footer.el.getHeight();
27971 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27972 ah -= 5; // knock a few pixes off for look..
27973 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27977 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27978 this.editorcore.onResize(ew,eh);
27983 * Toggles the editor between standard and source edit mode.
27984 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27986 toggleSourceEdit : function(sourceEditMode)
27988 this.editorcore.toggleSourceEdit(sourceEditMode);
27990 if(this.editorcore.sourceEditMode){
27991 Roo.log('editor - showing textarea');
27994 // Roo.log(this.syncValue());
27996 this.inputEl().removeClass(['hide', 'x-hidden']);
27997 this.inputEl().dom.removeAttribute('tabIndex');
27998 this.inputEl().focus();
28000 Roo.log('editor - hiding textarea');
28002 // Roo.log(this.pushValue());
28005 this.inputEl().addClass(['hide', 'x-hidden']);
28006 this.inputEl().dom.setAttribute('tabIndex', -1);
28007 //this.deferFocus();
28010 if(this.resizable){
28011 this.setSize(this.wrap.getSize());
28014 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28017 // private (for BoxComponent)
28018 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28020 // private (for BoxComponent)
28021 getResizeEl : function(){
28025 // private (for BoxComponent)
28026 getPositionEl : function(){
28031 initEvents : function(){
28032 this.originalValue = this.getValue();
28036 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28039 // markInvalid : Roo.emptyFn,
28041 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28044 // clearInvalid : Roo.emptyFn,
28046 setValue : function(v){
28047 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28048 this.editorcore.pushValue();
28053 deferFocus : function(){
28054 this.focus.defer(10, this);
28058 focus : function(){
28059 this.editorcore.focus();
28065 onDestroy : function(){
28071 for (var i =0; i < this.toolbars.length;i++) {
28072 // fixme - ask toolbars for heights?
28073 this.toolbars[i].onDestroy();
28076 this.wrap.dom.innerHTML = '';
28077 this.wrap.remove();
28082 onFirstFocus : function(){
28083 //Roo.log("onFirstFocus");
28084 this.editorcore.onFirstFocus();
28085 for (var i =0; i < this.toolbars.length;i++) {
28086 this.toolbars[i].onFirstFocus();
28092 syncValue : function()
28094 this.editorcore.syncValue();
28097 pushValue : function()
28099 this.editorcore.pushValue();
28103 // hide stuff that is not compatible
28117 * @event specialkey
28121 * @cfg {String} fieldClass @hide
28124 * @cfg {String} focusClass @hide
28127 * @cfg {String} autoCreate @hide
28130 * @cfg {String} inputType @hide
28134 * @cfg {String} invalidText @hide
28137 * @cfg {String} msgFx @hide
28140 * @cfg {String} validateOnBlur @hide
28149 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28151 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28152 * @parent Roo.bootstrap.form.HtmlEditor
28153 * @extends Roo.bootstrap.nav.Simplebar
28159 new Roo.bootstrap.form.HtmlEditor({
28162 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28163 disable : { fonts: 1 , format: 1, ..., ... , ...],
28169 * @cfg {Object} disable List of elements to disable..
28170 * @cfg {Array} btns List of additional buttons.
28174 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28177 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28180 Roo.apply(this, config);
28182 // default disabled, based on 'good practice'..
28183 this.disable = this.disable || {};
28184 Roo.applyIf(this.disable, {
28187 specialElements : true
28189 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28191 this.editor = config.editor;
28192 this.editorcore = config.editor.editorcore;
28194 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28196 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28197 // dont call parent... till later.
28199 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28204 editorcore : false,
28209 "h1","h2","h3","h4","h5","h6",
28211 "abbr", "acronym", "address", "cite", "samp", "var",
28215 onRender : function(ct, position)
28217 // Roo.log("Call onRender: " + this.xtype);
28219 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28221 this.el.dom.style.marginBottom = '0';
28223 var editorcore = this.editorcore;
28224 var editor= this.editor;
28227 var btn = function(id,cmd , toggle, handler, html){
28229 var event = toggle ? 'toggle' : 'click';
28234 xns: Roo.bootstrap,
28238 enableToggle:toggle !== false,
28240 pressed : toggle ? false : null,
28243 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28244 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28250 // var cb_box = function...
28255 xns: Roo.bootstrap,
28260 xns: Roo.bootstrap,
28264 Roo.each(this.formats, function(f) {
28265 style.menu.items.push({
28267 xns: Roo.bootstrap,
28268 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28273 editorcore.insertTag(this.tagname);
28280 children.push(style);
28282 btn('bold',false,true);
28283 btn('italic',false,true);
28284 btn('align-left', 'justifyleft',true);
28285 btn('align-center', 'justifycenter',true);
28286 btn('align-right' , 'justifyright',true);
28287 btn('link', false, false, function(btn) {
28288 //Roo.log("create link?");
28289 var url = prompt(this.createLinkText, this.defaultLinkValue);
28290 if(url && url != 'http:/'+'/'){
28291 this.editorcore.relayCmd('createlink', url);
28294 btn('list','insertunorderedlist',true);
28295 btn('pencil', false,true, function(btn){
28297 this.toggleSourceEdit(btn.pressed);
28300 if (this.editor.btns.length > 0) {
28301 for (var i = 0; i<this.editor.btns.length; i++) {
28302 children.push(this.editor.btns[i]);
28310 xns: Roo.bootstrap,
28315 xns: Roo.bootstrap,
28320 cog.menu.items.push({
28322 xns: Roo.bootstrap,
28323 html : Clean styles,
28328 editorcore.insertTag(this.tagname);
28337 this.xtype = 'NavSimplebar';
28339 for(var i=0;i< children.length;i++) {
28341 this.buttons.add(this.addxtypeChild(children[i]));
28345 editor.on('editorevent', this.updateToolbar, this);
28347 onBtnClick : function(id)
28349 this.editorcore.relayCmd(id);
28350 this.editorcore.focus();
28354 * Protected method that will not generally be called directly. It triggers
28355 * a toolbar update by reading the markup state of the current selection in the editor.
28357 updateToolbar: function(){
28359 if(!this.editorcore.activated){
28360 this.editor.onFirstFocus(); // is this neeed?
28364 var btns = this.buttons;
28365 var doc = this.editorcore.doc;
28366 btns.get('bold').setActive(doc.queryCommandState('bold'));
28367 btns.get('italic').setActive(doc.queryCommandState('italic'));
28368 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28370 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28371 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28372 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28374 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28375 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28378 var ans = this.editorcore.getAllAncestors();
28379 if (this.formatCombo) {
28382 var store = this.formatCombo.store;
28383 this.formatCombo.setValue("");
28384 for (var i =0; i < ans.length;i++) {
28385 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28387 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28395 // hides menus... - so this cant be on a menu...
28396 Roo.bootstrap.MenuMgr.hideAll();
28398 Roo.bootstrap.menu.Manager.hideAll();
28399 //this.editorsyncValue();
28401 onFirstFocus: function() {
28402 this.buttons.each(function(item){
28406 toggleSourceEdit : function(sourceEditMode){
28409 if(sourceEditMode){
28410 Roo.log("disabling buttons");
28411 this.buttons.each( function(item){
28412 if(item.cmd != 'pencil'){
28418 Roo.log("enabling buttons");
28419 if(this.editorcore.initialized){
28420 this.buttons.each( function(item){
28426 Roo.log("calling toggole on editor");
28427 // tell the editor that it's been pressed..
28428 this.editor.toggleSourceEdit(sourceEditMode);
28442 * @class Roo.bootstrap.form.Markdown
28443 * @extends Roo.bootstrap.form.TextArea
28444 * Bootstrap Showdown editable area
28445 * @cfg {string} content
28448 * Create a new Showdown
28451 Roo.bootstrap.form.Markdown = function(config){
28452 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28456 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28460 initEvents : function()
28463 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28464 this.markdownEl = this.el.createChild({
28465 cls : 'roo-markdown-area'
28467 this.inputEl().addClass('d-none');
28468 if (this.getValue() == '') {
28469 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28472 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28474 this.markdownEl.on('click', this.toggleTextEdit, this);
28475 this.on('blur', this.toggleTextEdit, this);
28476 this.on('specialkey', this.resizeTextArea, this);
28479 toggleTextEdit : function()
28481 var sh = this.markdownEl.getHeight();
28482 this.inputEl().addClass('d-none');
28483 this.markdownEl.addClass('d-none');
28484 if (!this.editing) {
28486 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28487 this.inputEl().removeClass('d-none');
28488 this.inputEl().focus();
28489 this.editing = true;
28492 // show showdown...
28493 this.updateMarkdown();
28494 this.markdownEl.removeClass('d-none');
28495 this.editing = false;
28498 updateMarkdown : function()
28500 if (this.getValue() == '') {
28501 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28505 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28508 resizeTextArea: function () {
28511 Roo.log([sh, this.getValue().split("\n").length * 30]);
28512 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28514 setValue : function(val)
28516 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28517 if (!this.editing) {
28518 this.updateMarkdown();
28524 if (!this.editing) {
28525 this.toggleTextEdit();
28533 * Ext JS Library 1.1.1
28534 * Copyright(c) 2006-2007, Ext JS, LLC.
28536 * Originally Released Under LGPL - original licence link has changed is not relivant.
28539 * <script type="text/javascript">
28543 * @class Roo.bootstrap.PagingToolbar
28544 * @extends Roo.bootstrap.nav.Simplebar
28545 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28547 * Create a new PagingToolbar
28548 * @param {Object} config The config object
28549 * @param {Roo.data.Store} store
28551 Roo.bootstrap.PagingToolbar = function(config)
28553 // old args format still supported... - xtype is prefered..
28554 // created from xtype...
28556 this.ds = config.dataSource;
28558 if (config.store && !this.ds) {
28559 this.store= Roo.factory(config.store, Roo.data);
28560 this.ds = this.store;
28561 this.ds.xmodule = this.xmodule || false;
28564 this.toolbarItems = [];
28565 if (config.items) {
28566 this.toolbarItems = config.items;
28569 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28574 this.bind(this.ds);
28577 if (Roo.bootstrap.version == 4) {
28578 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28580 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28585 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28587 * @cfg {Roo.bootstrap.Button} buttons[]
28588 * Buttons for the toolbar
28591 * @cfg {Roo.data.Store} store
28592 * The underlying data store providing the paged data
28595 * @cfg {String/HTMLElement/Element} container
28596 * container The id or element that will contain the toolbar
28599 * @cfg {Boolean} displayInfo
28600 * True to display the displayMsg (defaults to false)
28603 * @cfg {Number} pageSize
28604 * The number of records to display per page (defaults to 20)
28608 * @cfg {String} displayMsg
28609 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28611 displayMsg : 'Displaying {0} - {1} of {2}',
28613 * @cfg {String} emptyMsg
28614 * The message to display when no records are found (defaults to "No data to display")
28616 emptyMsg : 'No data to display',
28618 * Customizable piece of the default paging text (defaults to "Page")
28621 beforePageText : "Page",
28623 * Customizable piece of the default paging text (defaults to "of %0")
28626 afterPageText : "of {0}",
28628 * Customizable piece of the default paging text (defaults to "First Page")
28631 firstText : "First Page",
28633 * Customizable piece of the default paging text (defaults to "Previous Page")
28636 prevText : "Previous Page",
28638 * Customizable piece of the default paging text (defaults to "Next Page")
28641 nextText : "Next Page",
28643 * Customizable piece of the default paging text (defaults to "Last Page")
28646 lastText : "Last Page",
28648 * Customizable piece of the default paging text (defaults to "Refresh")
28651 refreshText : "Refresh",
28655 onRender : function(ct, position)
28657 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28658 this.navgroup.parentId = this.id;
28659 this.navgroup.onRender(this.el, null);
28660 // add the buttons to the navgroup
28662 if(this.displayInfo){
28663 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28664 this.displayEl = this.el.select('.x-paging-info', true).first();
28665 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28666 // this.displayEl = navel.el.select('span',true).first();
28672 Roo.each(_this.buttons, function(e){ // this might need to use render????
28673 Roo.factory(e).render(_this.el);
28677 Roo.each(_this.toolbarItems, function(e) {
28678 _this.navgroup.addItem(e);
28682 this.first = this.navgroup.addItem({
28683 tooltip: this.firstText,
28684 cls: "prev btn-outline-secondary",
28685 html : ' <i class="fa fa-step-backward"></i>',
28687 preventDefault: true,
28688 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28691 this.prev = this.navgroup.addItem({
28692 tooltip: this.prevText,
28693 cls: "prev btn-outline-secondary",
28694 html : ' <i class="fa fa-backward"></i>',
28696 preventDefault: true,
28697 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28699 //this.addSeparator();
28702 var field = this.navgroup.addItem( {
28704 cls : 'x-paging-position btn-outline-secondary',
28706 html : this.beforePageText +
28707 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28708 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28711 this.field = field.el.select('input', true).first();
28712 this.field.on("keydown", this.onPagingKeydown, this);
28713 this.field.on("focus", function(){this.dom.select();});
28716 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28717 //this.field.setHeight(18);
28718 //this.addSeparator();
28719 this.next = this.navgroup.addItem({
28720 tooltip: this.nextText,
28721 cls: "next btn-outline-secondary",
28722 html : ' <i class="fa fa-forward"></i>',
28724 preventDefault: true,
28725 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28727 this.last = this.navgroup.addItem({
28728 tooltip: this.lastText,
28729 html : ' <i class="fa fa-step-forward"></i>',
28730 cls: "next btn-outline-secondary",
28732 preventDefault: true,
28733 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28735 //this.addSeparator();
28736 this.loading = this.navgroup.addItem({
28737 tooltip: this.refreshText,
28738 cls: "btn-outline-secondary",
28739 html : ' <i class="fa fa-refresh"></i>',
28740 preventDefault: true,
28741 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28747 updateInfo : function(){
28748 if(this.displayEl){
28749 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28750 var msg = count == 0 ?
28754 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28756 this.displayEl.update(msg);
28761 onLoad : function(ds, r, o)
28763 this.cursor = o.params && o.params.start ? o.params.start : 0;
28765 var d = this.getPageData(),
28770 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28771 this.field.dom.value = ap;
28772 this.first.setDisabled(ap == 1);
28773 this.prev.setDisabled(ap == 1);
28774 this.next.setDisabled(ap == ps);
28775 this.last.setDisabled(ap == ps);
28776 this.loading.enable();
28781 getPageData : function(){
28782 var total = this.ds.getTotalCount();
28785 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28786 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28791 onLoadError : function(proxy, o){
28792 this.loading.enable();
28793 if (this.ds.events.loadexception.listeners.length < 2) {
28794 // nothing has been assigned to loadexception except this...
28796 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28802 onPagingKeydown : function(e){
28803 var k = e.getKey();
28804 var d = this.getPageData();
28806 var v = this.field.dom.value, pageNum;
28807 if(!v || isNaN(pageNum = parseInt(v, 10))){
28808 this.field.dom.value = d.activePage;
28811 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28812 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28815 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))
28817 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28818 this.field.dom.value = pageNum;
28819 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28822 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28824 var v = this.field.dom.value, pageNum;
28825 var increment = (e.shiftKey) ? 10 : 1;
28826 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28829 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28830 this.field.dom.value = d.activePage;
28833 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28835 this.field.dom.value = parseInt(v, 10) + increment;
28836 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28837 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28844 beforeLoad : function(){
28846 this.loading.disable();
28851 onClick : function(which){
28860 ds.load({params:{start: 0, limit: this.pageSize}});
28863 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28866 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28869 var total = ds.getTotalCount();
28870 var extra = total % this.pageSize;
28871 var lastStart = extra ? (total - extra) : total-this.pageSize;
28872 ds.load({params:{start: lastStart, limit: this.pageSize}});
28875 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28881 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28882 * @param {Roo.data.Store} store The data store to unbind
28884 unbind : function(ds){
28885 ds.un("beforeload", this.beforeLoad, this);
28886 ds.un("load", this.onLoad, this);
28887 ds.un("loadexception", this.onLoadError, this);
28888 ds.un("remove", this.updateInfo, this);
28889 ds.un("add", this.updateInfo, this);
28890 this.ds = undefined;
28894 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28895 * @param {Roo.data.Store} store The data store to bind
28897 bind : function(ds){
28898 ds.on("beforeload", this.beforeLoad, this);
28899 ds.on("load", this.onLoad, this);
28900 ds.on("loadexception", this.onLoadError, this);
28901 ds.on("remove", this.updateInfo, this);
28902 ds.on("add", this.updateInfo, this);
28913 * @class Roo.bootstrap.MessageBar
28914 * @extends Roo.bootstrap.Component
28915 * Bootstrap MessageBar class
28916 * @cfg {String} html contents of the MessageBar
28917 * @cfg {String} weight (info | success | warning | danger) default info
28918 * @cfg {String} beforeClass insert the bar before the given class
28919 * @cfg {Boolean} closable (true | false) default false
28920 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28923 * Create a new Element
28924 * @param {Object} config The config object
28927 Roo.bootstrap.MessageBar = function(config){
28928 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28931 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28937 beforeClass: 'bootstrap-sticky-wrap',
28939 getAutoCreate : function(){
28943 cls: 'alert alert-dismissable alert-' + this.weight,
28948 html: this.html || ''
28954 cfg.cls += ' alert-messages-fixed';
28968 onRender : function(ct, position)
28970 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28973 var cfg = Roo.apply({}, this.getAutoCreate());
28977 cfg.cls += ' ' + this.cls;
28980 cfg.style = this.style;
28982 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28984 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28987 this.el.select('>button.close').on('click', this.hide, this);
28993 if (!this.rendered) {
28999 this.fireEvent('show', this);
29005 if (!this.rendered) {
29011 this.fireEvent('hide', this);
29014 update : function()
29016 // var e = this.el.dom.firstChild;
29018 // if(this.closable){
29019 // e = e.nextSibling;
29022 // e.data = this.html || '';
29024 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29040 * @class Roo.bootstrap.Graph
29041 * @extends Roo.bootstrap.Component
29042 * Bootstrap Graph class
29046 @cfg {String} graphtype bar | vbar | pie
29047 @cfg {number} g_x coodinator | centre x (pie)
29048 @cfg {number} g_y coodinator | centre y (pie)
29049 @cfg {number} g_r radius (pie)
29050 @cfg {number} g_height height of the chart (respected by all elements in the set)
29051 @cfg {number} g_width width of the chart (respected by all elements in the set)
29052 @cfg {Object} title The title of the chart
29055 -opts (object) options for the chart
29057 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29058 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29060 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.
29061 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29063 o stretch (boolean)
29065 -opts (object) options for the pie
29068 o startAngle (number)
29069 o endAngle (number)
29073 * Create a new Input
29074 * @param {Object} config The config object
29077 Roo.bootstrap.Graph = function(config){
29078 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29084 * The img click event for the img.
29085 * @param {Roo.EventObject} e
29091 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29102 //g_colors: this.colors,
29109 getAutoCreate : function(){
29120 onRender : function(ct,position){
29123 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29125 if (typeof(Raphael) == 'undefined') {
29126 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29130 this.raphael = Raphael(this.el.dom);
29132 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29133 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29134 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29135 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29137 r.text(160, 10, "Single Series Chart").attr(txtattr);
29138 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29139 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29140 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29142 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29143 r.barchart(330, 10, 300, 220, data1);
29144 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29145 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29148 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29149 // r.barchart(30, 30, 560, 250, xdata, {
29150 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29151 // axis : "0 0 1 1",
29152 // axisxlabels : xdata
29153 // //yvalues : cols,
29156 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29158 // this.load(null,xdata,{
29159 // axis : "0 0 1 1",
29160 // axisxlabels : xdata
29165 load : function(graphtype,xdata,opts)
29167 this.raphael.clear();
29169 graphtype = this.graphtype;
29174 var r = this.raphael,
29175 fin = function () {
29176 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29178 fout = function () {
29179 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29181 pfin = function() {
29182 this.sector.stop();
29183 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29186 this.label[0].stop();
29187 this.label[0].attr({ r: 7.5 });
29188 this.label[1].attr({ "font-weight": 800 });
29191 pfout = function() {
29192 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29195 this.label[0].animate({ r: 5 }, 500, "bounce");
29196 this.label[1].attr({ "font-weight": 400 });
29202 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29205 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29208 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29209 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29211 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29218 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29223 setTitle: function(o)
29228 initEvents: function() {
29231 this.el.on('click', this.onClick, this);
29235 onClick : function(e)
29237 Roo.log('img onclick');
29238 this.fireEvent('click', this, e);
29250 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29253 * @class Roo.bootstrap.dash.NumberBox
29254 * @extends Roo.bootstrap.Component
29255 * Bootstrap NumberBox class
29256 * @cfg {String} headline Box headline
29257 * @cfg {String} content Box content
29258 * @cfg {String} icon Box icon
29259 * @cfg {String} footer Footer text
29260 * @cfg {String} fhref Footer href
29263 * Create a new NumberBox
29264 * @param {Object} config The config object
29268 Roo.bootstrap.dash.NumberBox = function(config){
29269 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29273 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29282 getAutoCreate : function(){
29286 cls : 'small-box ',
29294 cls : 'roo-headline',
29295 html : this.headline
29299 cls : 'roo-content',
29300 html : this.content
29314 cls : 'ion ' + this.icon
29323 cls : 'small-box-footer',
29324 href : this.fhref || '#',
29328 cfg.cn.push(footer);
29335 onRender : function(ct,position){
29336 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29343 setHeadline: function (value)
29345 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29348 setFooter: function (value, href)
29350 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29353 this.el.select('a.small-box-footer',true).first().attr('href', href);
29358 setContent: function (value)
29360 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29363 initEvents: function()
29377 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29380 * @class Roo.bootstrap.dash.TabBox
29381 * @extends Roo.bootstrap.Component
29382 * @children Roo.bootstrap.dash.TabPane
29383 * Bootstrap TabBox class
29384 * @cfg {String} title Title of the TabBox
29385 * @cfg {String} icon Icon of the TabBox
29386 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29387 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29390 * Create a new TabBox
29391 * @param {Object} config The config object
29395 Roo.bootstrap.dash.TabBox = function(config){
29396 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29401 * When a pane is added
29402 * @param {Roo.bootstrap.dash.TabPane} pane
29406 * @event activatepane
29407 * When a pane is activated
29408 * @param {Roo.bootstrap.dash.TabPane} pane
29410 "activatepane" : true
29418 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29423 tabScrollable : false,
29425 getChildContainer : function()
29427 return this.el.select('.tab-content', true).first();
29430 getAutoCreate : function(){
29434 cls: 'pull-left header',
29442 cls: 'fa ' + this.icon
29448 cls: 'nav nav-tabs pull-right',
29454 if(this.tabScrollable){
29461 cls: 'nav nav-tabs pull-right',
29472 cls: 'nav-tabs-custom',
29477 cls: 'tab-content no-padding',
29485 initEvents : function()
29487 //Roo.log('add add pane handler');
29488 this.on('addpane', this.onAddPane, this);
29491 * Updates the box title
29492 * @param {String} html to set the title to.
29494 setTitle : function(value)
29496 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29498 onAddPane : function(pane)
29500 this.panes.push(pane);
29501 //Roo.log('addpane');
29503 // tabs are rendere left to right..
29504 if(!this.showtabs){
29508 var ctr = this.el.select('.nav-tabs', true).first();
29511 var existing = ctr.select('.nav-tab',true);
29512 var qty = existing.getCount();;
29515 var tab = ctr.createChild({
29517 cls : 'nav-tab' + (qty ? '' : ' active'),
29525 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29528 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29530 pane.el.addClass('active');
29535 onTabClick : function(ev,un,ob,pane)
29537 //Roo.log('tab - prev default');
29538 ev.preventDefault();
29541 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29542 pane.tab.addClass('active');
29543 //Roo.log(pane.title);
29544 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29545 // technically we should have a deactivate event.. but maybe add later.
29546 // and it should not de-activate the selected tab...
29547 this.fireEvent('activatepane', pane);
29548 pane.el.addClass('active');
29549 pane.fireEvent('activate');
29554 getActivePane : function()
29557 Roo.each(this.panes, function(p) {
29558 if(p.el.hasClass('active')){
29579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29581 * @class Roo.bootstrap.TabPane
29582 * @extends Roo.bootstrap.Component
29583 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29584 * Bootstrap TabPane class
29585 * @cfg {Boolean} active (false | true) Default false
29586 * @cfg {String} title title of panel
29590 * Create a new TabPane
29591 * @param {Object} config The config object
29594 Roo.bootstrap.dash.TabPane = function(config){
29595 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29601 * When a pane is activated
29602 * @param {Roo.bootstrap.dash.TabPane} pane
29609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29614 // the tabBox that this is attached to.
29617 getAutoCreate : function()
29625 cfg.cls += ' active';
29630 initEvents : function()
29632 //Roo.log('trigger add pane handler');
29633 this.parent().fireEvent('addpane', this)
29637 * Updates the tab title
29638 * @param {String} html to set the title to.
29640 setTitle: function(str)
29646 this.tab.select('a', true).first().dom.innerHTML = str;
29665 * @class Roo.bootstrap.Tooltip
29666 * Bootstrap Tooltip class
29667 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29668 * to determine which dom element triggers the tooltip.
29670 * It needs to add support for additional attributes like tooltip-position
29673 * Create a new Toolti
29674 * @param {Object} config The config object
29677 Roo.bootstrap.Tooltip = function(config){
29678 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29680 this.alignment = Roo.bootstrap.Tooltip.alignment;
29682 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29683 this.alignment = config.alignment;
29688 Roo.apply(Roo.bootstrap.Tooltip, {
29690 * @function init initialize tooltip monitoring.
29694 currentTip : false,
29695 currentRegion : false,
29701 Roo.get(document).on('mouseover', this.enter ,this);
29702 Roo.get(document).on('mouseout', this.leave, this);
29705 this.currentTip = new Roo.bootstrap.Tooltip();
29708 enter : function(ev)
29710 var dom = ev.getTarget();
29712 //Roo.log(['enter',dom]);
29713 var el = Roo.fly(dom);
29714 if (this.currentEl) {
29716 //Roo.log(this.currentEl);
29717 //Roo.log(this.currentEl.contains(dom));
29718 if (this.currentEl == el) {
29721 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29727 if (this.currentTip.el) {
29728 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29732 if(!el || el.dom == document){
29738 if (!el.attr('tooltip')) {
29739 pel = el.findParent("[tooltip]");
29741 bindEl = Roo.get(pel);
29747 // you can not look for children, as if el is the body.. then everythign is the child..
29748 if (!pel && !el.attr('tooltip')) { //
29749 if (!el.select("[tooltip]").elements.length) {
29752 // is the mouse over this child...?
29753 bindEl = el.select("[tooltip]").first();
29754 var xy = ev.getXY();
29755 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29756 //Roo.log("not in region.");
29759 //Roo.log("child element over..");
29762 this.currentEl = el;
29763 this.currentTip.bind(bindEl);
29764 this.currentRegion = Roo.lib.Region.getRegion(dom);
29765 this.currentTip.enter();
29768 leave : function(ev)
29770 var dom = ev.getTarget();
29771 //Roo.log(['leave',dom]);
29772 if (!this.currentEl) {
29777 if (dom != this.currentEl.dom) {
29780 var xy = ev.getXY();
29781 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29784 // only activate leave if mouse cursor is outside... bounding box..
29789 if (this.currentTip) {
29790 this.currentTip.leave();
29792 //Roo.log('clear currentEl');
29793 this.currentEl = false;
29798 'left' : ['r-l', [-2,0], 'right'],
29799 'right' : ['l-r', [2,0], 'left'],
29800 'bottom' : ['t-b', [0,2], 'top'],
29801 'top' : [ 'b-t', [0,-2], 'bottom']
29807 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29812 delay : null, // can be { show : 300 , hide: 500}
29816 hoverState : null, //???
29818 placement : 'bottom',
29822 getAutoCreate : function(){
29829 cls : 'tooltip-arrow arrow'
29832 cls : 'tooltip-inner'
29839 bind : function(el)
29844 initEvents : function()
29846 this.arrowEl = this.el.select('.arrow', true).first();
29847 this.innerEl = this.el.select('.tooltip-inner', true).first();
29850 enter : function () {
29852 if (this.timeout != null) {
29853 clearTimeout(this.timeout);
29856 this.hoverState = 'in';
29857 //Roo.log("enter - show");
29858 if (!this.delay || !this.delay.show) {
29863 this.timeout = setTimeout(function () {
29864 if (_t.hoverState == 'in') {
29867 }, this.delay.show);
29871 clearTimeout(this.timeout);
29873 this.hoverState = 'out';
29874 if (!this.delay || !this.delay.hide) {
29880 this.timeout = setTimeout(function () {
29881 //Roo.log("leave - timeout");
29883 if (_t.hoverState == 'out') {
29885 Roo.bootstrap.Tooltip.currentEl = false;
29890 show : function (msg)
29893 this.render(document.body);
29896 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29898 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29900 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29902 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29903 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29905 var placement = typeof this.placement == 'function' ?
29906 this.placement.call(this, this.el, on_el) :
29909 var autoToken = /\s?auto?\s?/i;
29910 var autoPlace = autoToken.test(placement);
29912 placement = placement.replace(autoToken, '') || 'top';
29916 //this.el.setXY([0,0]);
29918 //this.el.dom.style.display='block';
29920 //this.el.appendTo(on_el);
29922 var p = this.getPosition();
29923 var box = this.el.getBox();
29929 var align = this.alignment[placement];
29931 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29933 if(placement == 'top' || placement == 'bottom'){
29935 placement = 'right';
29938 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29939 placement = 'left';
29942 var scroll = Roo.select('body', true).first().getScroll();
29944 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29948 align = this.alignment[placement];
29950 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29954 var elems = document.getElementsByTagName('div');
29955 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29956 for (var i = 0; i < elems.length; i++) {
29957 var zindex = Number.parseInt(
29958 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29961 if (zindex > highest) {
29968 this.el.dom.style.zIndex = highest;
29970 this.el.alignTo(this.bindEl, align[0],align[1]);
29971 //var arrow = this.el.select('.arrow',true).first();
29972 //arrow.set(align[2],
29974 this.el.addClass(placement);
29975 this.el.addClass("bs-tooltip-"+ placement);
29977 this.el.addClass('in fade show');
29979 this.hoverState = null;
29981 if (this.el.hasClass('fade')) {
29996 //this.el.setXY([0,0]);
29997 this.el.removeClass(['show', 'in']);
30013 * @class Roo.bootstrap.LocationPicker
30014 * @extends Roo.bootstrap.Component
30015 * Bootstrap LocationPicker class
30016 * @cfg {Number} latitude Position when init default 0
30017 * @cfg {Number} longitude Position when init default 0
30018 * @cfg {Number} zoom default 15
30019 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30020 * @cfg {Boolean} mapTypeControl default false
30021 * @cfg {Boolean} disableDoubleClickZoom default false
30022 * @cfg {Boolean} scrollwheel default true
30023 * @cfg {Boolean} streetViewControl default false
30024 * @cfg {Number} radius default 0
30025 * @cfg {String} locationName
30026 * @cfg {Boolean} draggable default true
30027 * @cfg {Boolean} enableAutocomplete default false
30028 * @cfg {Boolean} enableReverseGeocode default true
30029 * @cfg {String} markerTitle
30032 * Create a new LocationPicker
30033 * @param {Object} config The config object
30037 Roo.bootstrap.LocationPicker = function(config){
30039 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30044 * Fires when the picker initialized.
30045 * @param {Roo.bootstrap.LocationPicker} this
30046 * @param {Google Location} location
30050 * @event positionchanged
30051 * Fires when the picker position changed.
30052 * @param {Roo.bootstrap.LocationPicker} this
30053 * @param {Google Location} location
30055 positionchanged : true,
30058 * Fires when the map resize.
30059 * @param {Roo.bootstrap.LocationPicker} this
30064 * Fires when the map show.
30065 * @param {Roo.bootstrap.LocationPicker} this
30070 * Fires when the map hide.
30071 * @param {Roo.bootstrap.LocationPicker} this
30076 * Fires when click the map.
30077 * @param {Roo.bootstrap.LocationPicker} this
30078 * @param {Map event} e
30082 * @event mapRightClick
30083 * Fires when right click the map.
30084 * @param {Roo.bootstrap.LocationPicker} this
30085 * @param {Map event} e
30087 mapRightClick : true,
30089 * @event markerClick
30090 * Fires when click the marker.
30091 * @param {Roo.bootstrap.LocationPicker} this
30092 * @param {Map event} e
30094 markerClick : true,
30096 * @event markerRightClick
30097 * Fires when right click the marker.
30098 * @param {Roo.bootstrap.LocationPicker} this
30099 * @param {Map event} e
30101 markerRightClick : true,
30103 * @event OverlayViewDraw
30104 * Fires when OverlayView Draw
30105 * @param {Roo.bootstrap.LocationPicker} this
30107 OverlayViewDraw : true,
30109 * @event OverlayViewOnAdd
30110 * Fires when OverlayView Draw
30111 * @param {Roo.bootstrap.LocationPicker} this
30113 OverlayViewOnAdd : true,
30115 * @event OverlayViewOnRemove
30116 * Fires when OverlayView Draw
30117 * @param {Roo.bootstrap.LocationPicker} this
30119 OverlayViewOnRemove : true,
30121 * @event OverlayViewShow
30122 * Fires when OverlayView Draw
30123 * @param {Roo.bootstrap.LocationPicker} this
30124 * @param {Pixel} cpx
30126 OverlayViewShow : true,
30128 * @event OverlayViewHide
30129 * Fires when OverlayView Draw
30130 * @param {Roo.bootstrap.LocationPicker} this
30132 OverlayViewHide : true,
30134 * @event loadexception
30135 * Fires when load google lib failed.
30136 * @param {Roo.bootstrap.LocationPicker} this
30138 loadexception : true
30143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30145 gMapContext: false,
30151 mapTypeControl: false,
30152 disableDoubleClickZoom: false,
30154 streetViewControl: false,
30158 enableAutocomplete: false,
30159 enableReverseGeocode: true,
30162 getAutoCreate: function()
30167 cls: 'roo-location-picker'
30173 initEvents: function(ct, position)
30175 if(!this.el.getWidth() || this.isApplied()){
30179 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30184 initial: function()
30186 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30187 this.fireEvent('loadexception', this);
30191 if(!this.mapTypeId){
30192 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30195 this.gMapContext = this.GMapContext();
30197 this.initOverlayView();
30199 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30203 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30204 _this.setPosition(_this.gMapContext.marker.position);
30207 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30208 _this.fireEvent('mapClick', this, event);
30212 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30213 _this.fireEvent('mapRightClick', this, event);
30217 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30218 _this.fireEvent('markerClick', this, event);
30222 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30223 _this.fireEvent('markerRightClick', this, event);
30227 this.setPosition(this.gMapContext.location);
30229 this.fireEvent('initial', this, this.gMapContext.location);
30232 initOverlayView: function()
30236 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30240 _this.fireEvent('OverlayViewDraw', _this);
30245 _this.fireEvent('OverlayViewOnAdd', _this);
30248 onRemove: function()
30250 _this.fireEvent('OverlayViewOnRemove', _this);
30253 show: function(cpx)
30255 _this.fireEvent('OverlayViewShow', _this, cpx);
30260 _this.fireEvent('OverlayViewHide', _this);
30266 fromLatLngToContainerPixel: function(event)
30268 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30271 isApplied: function()
30273 return this.getGmapContext() == false ? false : true;
30276 getGmapContext: function()
30278 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30281 GMapContext: function()
30283 var position = new google.maps.LatLng(this.latitude, this.longitude);
30285 var _map = new google.maps.Map(this.el.dom, {
30288 mapTypeId: this.mapTypeId,
30289 mapTypeControl: this.mapTypeControl,
30290 disableDoubleClickZoom: this.disableDoubleClickZoom,
30291 scrollwheel: this.scrollwheel,
30292 streetViewControl: this.streetViewControl,
30293 locationName: this.locationName,
30294 draggable: this.draggable,
30295 enableAutocomplete: this.enableAutocomplete,
30296 enableReverseGeocode: this.enableReverseGeocode
30299 var _marker = new google.maps.Marker({
30300 position: position,
30302 title: this.markerTitle,
30303 draggable: this.draggable
30310 location: position,
30311 radius: this.radius,
30312 locationName: this.locationName,
30313 addressComponents: {
30314 formatted_address: null,
30315 addressLine1: null,
30316 addressLine2: null,
30318 streetNumber: null,
30322 stateOrProvince: null
30325 domContainer: this.el.dom,
30326 geodecoder: new google.maps.Geocoder()
30330 drawCircle: function(center, radius, options)
30332 if (this.gMapContext.circle != null) {
30333 this.gMapContext.circle.setMap(null);
30337 options = Roo.apply({}, options, {
30338 strokeColor: "#0000FF",
30339 strokeOpacity: .35,
30341 fillColor: "#0000FF",
30345 options.map = this.gMapContext.map;
30346 options.radius = radius;
30347 options.center = center;
30348 this.gMapContext.circle = new google.maps.Circle(options);
30349 return this.gMapContext.circle;
30355 setPosition: function(location)
30357 this.gMapContext.location = location;
30358 this.gMapContext.marker.setPosition(location);
30359 this.gMapContext.map.panTo(location);
30360 this.drawCircle(location, this.gMapContext.radius, {});
30364 if (this.gMapContext.settings.enableReverseGeocode) {
30365 this.gMapContext.geodecoder.geocode({
30366 latLng: this.gMapContext.location
30367 }, function(results, status) {
30369 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30370 _this.gMapContext.locationName = results[0].formatted_address;
30371 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30373 _this.fireEvent('positionchanged', this, location);
30380 this.fireEvent('positionchanged', this, location);
30385 google.maps.event.trigger(this.gMapContext.map, "resize");
30387 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30389 this.fireEvent('resize', this);
30392 setPositionByLatLng: function(latitude, longitude)
30394 this.setPosition(new google.maps.LatLng(latitude, longitude));
30397 getCurrentPosition: function()
30400 latitude: this.gMapContext.location.lat(),
30401 longitude: this.gMapContext.location.lng()
30405 getAddressName: function()
30407 return this.gMapContext.locationName;
30410 getAddressComponents: function()
30412 return this.gMapContext.addressComponents;
30415 address_component_from_google_geocode: function(address_components)
30419 for (var i = 0; i < address_components.length; i++) {
30420 var component = address_components[i];
30421 if (component.types.indexOf("postal_code") >= 0) {
30422 result.postalCode = component.short_name;
30423 } else if (component.types.indexOf("street_number") >= 0) {
30424 result.streetNumber = component.short_name;
30425 } else if (component.types.indexOf("route") >= 0) {
30426 result.streetName = component.short_name;
30427 } else if (component.types.indexOf("neighborhood") >= 0) {
30428 result.city = component.short_name;
30429 } else if (component.types.indexOf("locality") >= 0) {
30430 result.city = component.short_name;
30431 } else if (component.types.indexOf("sublocality") >= 0) {
30432 result.district = component.short_name;
30433 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30434 result.stateOrProvince = component.short_name;
30435 } else if (component.types.indexOf("country") >= 0) {
30436 result.country = component.short_name;
30440 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30441 result.addressLine2 = "";
30445 setZoomLevel: function(zoom)
30447 this.gMapContext.map.setZoom(zoom);
30460 this.fireEvent('show', this);
30471 this.fireEvent('hide', this);
30476 Roo.apply(Roo.bootstrap.LocationPicker, {
30478 OverlayView : function(map, options)
30480 options = options || {};
30487 * @class Roo.bootstrap.Alert
30488 * @extends Roo.bootstrap.Component
30489 * Bootstrap Alert class - shows an alert area box
30491 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30492 Enter a valid email address
30495 * @cfg {String} title The title of alert
30496 * @cfg {String} html The content of alert
30497 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30498 * @cfg {String} fa font-awesomeicon
30499 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30500 * @cfg {Boolean} close true to show a x closer
30504 * Create a new alert
30505 * @param {Object} config The config object
30509 Roo.bootstrap.Alert = function(config){
30510 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30514 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30520 faicon: false, // BC
30524 getAutoCreate : function()
30536 style : this.close ? '' : 'display:none'
30540 cls : 'roo-alert-icon'
30545 cls : 'roo-alert-title',
30550 cls : 'roo-alert-text',
30557 cfg.cn[0].cls += ' fa ' + this.faicon;
30560 cfg.cn[0].cls += ' fa ' + this.fa;
30564 cfg.cls += ' alert-' + this.weight;
30570 initEvents: function()
30572 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30573 this.titleEl = this.el.select('.roo-alert-title',true).first();
30574 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30575 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30576 if (this.seconds > 0) {
30577 this.hide.defer(this.seconds, this);
30581 * Set the Title Message HTML
30582 * @param {String} html
30584 setTitle : function(str)
30586 this.titleEl.dom.innerHTML = str;
30590 * Set the Body Message HTML
30591 * @param {String} html
30593 setHtml : function(str)
30595 this.htmlEl.dom.innerHTML = str;
30598 * Set the Weight of the alert
30599 * @param {String} (success|info|warning|danger) weight
30602 setWeight : function(weight)
30605 this.el.removeClass('alert-' + this.weight);
30608 this.weight = weight;
30610 this.el.addClass('alert-' + this.weight);
30613 * Set the Icon of the alert
30614 * @param {String} see fontawsome names (name without the 'fa-' bit)
30616 setIcon : function(icon)
30619 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30622 this.faicon = icon;
30624 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30649 * @class Roo.bootstrap.UploadCropbox
30650 * @extends Roo.bootstrap.Component
30651 * Bootstrap UploadCropbox class
30652 * @cfg {String} emptyText show when image has been loaded
30653 * @cfg {String} rotateNotify show when image too small to rotate
30654 * @cfg {Number} errorTimeout default 3000
30655 * @cfg {Number} minWidth default 300
30656 * @cfg {Number} minHeight default 300
30657 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30658 * @cfg {Boolean} isDocument (true|false) default false
30659 * @cfg {String} url action url
30660 * @cfg {String} paramName default 'imageUpload'
30661 * @cfg {String} method default POST
30662 * @cfg {Boolean} loadMask (true|false) default true
30663 * @cfg {Boolean} loadingText default 'Loading...'
30666 * Create a new UploadCropbox
30667 * @param {Object} config The config object
30670 Roo.bootstrap.UploadCropbox = function(config){
30671 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30675 * @event beforeselectfile
30676 * Fire before select file
30677 * @param {Roo.bootstrap.UploadCropbox} this
30679 "beforeselectfile" : true,
30682 * Fire after initEvent
30683 * @param {Roo.bootstrap.UploadCropbox} this
30688 * Fire after initEvent
30689 * @param {Roo.bootstrap.UploadCropbox} this
30690 * @param {String} data
30695 * Fire when preparing the file data
30696 * @param {Roo.bootstrap.UploadCropbox} this
30697 * @param {Object} file
30702 * Fire when get exception
30703 * @param {Roo.bootstrap.UploadCropbox} this
30704 * @param {XMLHttpRequest} xhr
30706 "exception" : true,
30708 * @event beforeloadcanvas
30709 * Fire before load the canvas
30710 * @param {Roo.bootstrap.UploadCropbox} this
30711 * @param {String} src
30713 "beforeloadcanvas" : true,
30716 * Fire when trash image
30717 * @param {Roo.bootstrap.UploadCropbox} this
30722 * Fire when download the image
30723 * @param {Roo.bootstrap.UploadCropbox} this
30727 * @event footerbuttonclick
30728 * Fire when footerbuttonclick
30729 * @param {Roo.bootstrap.UploadCropbox} this
30730 * @param {String} type
30732 "footerbuttonclick" : true,
30736 * @param {Roo.bootstrap.UploadCropbox} this
30741 * Fire when rotate the image
30742 * @param {Roo.bootstrap.UploadCropbox} this
30743 * @param {String} pos
30748 * Fire when inspect the file
30749 * @param {Roo.bootstrap.UploadCropbox} this
30750 * @param {Object} file
30755 * Fire when xhr upload the file
30756 * @param {Roo.bootstrap.UploadCropbox} this
30757 * @param {Object} data
30762 * Fire when arrange the file data
30763 * @param {Roo.bootstrap.UploadCropbox} this
30764 * @param {Object} formData
30769 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30772 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30774 emptyText : 'Click to upload image',
30775 rotateNotify : 'Image is too small to rotate',
30776 errorTimeout : 3000,
30790 cropType : 'image/jpeg',
30792 canvasLoaded : false,
30793 isDocument : false,
30795 paramName : 'imageUpload',
30797 loadingText : 'Loading...',
30800 getAutoCreate : function()
30804 cls : 'roo-upload-cropbox',
30808 cls : 'roo-upload-cropbox-selector',
30813 cls : 'roo-upload-cropbox-body',
30814 style : 'cursor:pointer',
30818 cls : 'roo-upload-cropbox-preview'
30822 cls : 'roo-upload-cropbox-thumb'
30826 cls : 'roo-upload-cropbox-empty-notify',
30827 html : this.emptyText
30831 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30832 html : this.rotateNotify
30838 cls : 'roo-upload-cropbox-footer',
30841 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30851 onRender : function(ct, position)
30853 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30855 if (this.buttons.length) {
30857 Roo.each(this.buttons, function(bb) {
30859 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30861 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30867 this.maskEl = this.el;
30871 initEvents : function()
30873 this.urlAPI = (window.createObjectURL && window) ||
30874 (window.URL && URL.revokeObjectURL && URL) ||
30875 (window.webkitURL && webkitURL);
30877 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30878 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30881 this.selectorEl.hide();
30883 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30884 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30886 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30887 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30888 this.thumbEl.hide();
30890 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30891 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30893 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30894 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30895 this.errorEl.hide();
30897 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30898 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30899 this.footerEl.hide();
30901 this.setThumbBoxSize();
30907 this.fireEvent('initial', this);
30914 window.addEventListener("resize", function() { _this.resize(); } );
30916 this.bodyEl.on('click', this.beforeSelectFile, this);
30919 this.bodyEl.on('touchstart', this.onTouchStart, this);
30920 this.bodyEl.on('touchmove', this.onTouchMove, this);
30921 this.bodyEl.on('touchend', this.onTouchEnd, this);
30925 this.bodyEl.on('mousedown', this.onMouseDown, this);
30926 this.bodyEl.on('mousemove', this.onMouseMove, this);
30927 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30928 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30929 Roo.get(document).on('mouseup', this.onMouseUp, this);
30932 this.selectorEl.on('change', this.onFileSelected, this);
30938 this.baseScale = 1;
30940 this.baseRotate = 1;
30941 this.dragable = false;
30942 this.pinching = false;
30945 this.cropData = false;
30946 this.notifyEl.dom.innerHTML = this.emptyText;
30948 this.selectorEl.dom.value = '';
30952 resize : function()
30954 if(this.fireEvent('resize', this) != false){
30955 this.setThumbBoxPosition();
30956 this.setCanvasPosition();
30960 onFooterButtonClick : function(e, el, o, type)
30963 case 'rotate-left' :
30964 this.onRotateLeft(e);
30966 case 'rotate-right' :
30967 this.onRotateRight(e);
30970 this.beforeSelectFile(e);
30985 this.fireEvent('footerbuttonclick', this, type);
30988 beforeSelectFile : function(e)
30990 e.preventDefault();
30992 if(this.fireEvent('beforeselectfile', this) != false){
30993 this.selectorEl.dom.click();
30997 onFileSelected : function(e)
30999 e.preventDefault();
31001 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31005 var file = this.selectorEl.dom.files[0];
31007 if(this.fireEvent('inspect', this, file) != false){
31008 this.prepare(file);
31013 trash : function(e)
31015 this.fireEvent('trash', this);
31018 download : function(e)
31020 this.fireEvent('download', this);
31023 loadCanvas : function(src)
31025 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31029 this.imageEl = document.createElement('img');
31033 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31035 this.imageEl.src = src;
31039 onLoadCanvas : function()
31041 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31042 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31044 this.bodyEl.un('click', this.beforeSelectFile, this);
31046 this.notifyEl.hide();
31047 this.thumbEl.show();
31048 this.footerEl.show();
31050 this.baseRotateLevel();
31052 if(this.isDocument){
31053 this.setThumbBoxSize();
31056 this.setThumbBoxPosition();
31058 this.baseScaleLevel();
31064 this.canvasLoaded = true;
31067 this.maskEl.unmask();
31072 setCanvasPosition : function()
31074 if(!this.canvasEl){
31078 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31079 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31081 this.previewEl.setLeft(pw);
31082 this.previewEl.setTop(ph);
31086 onMouseDown : function(e)
31090 this.dragable = true;
31091 this.pinching = false;
31093 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31094 this.dragable = false;
31098 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31103 onMouseMove : function(e)
31107 if(!this.canvasLoaded){
31111 if (!this.dragable){
31115 var minX = Math.ceil(this.thumbEl.getLeft(true));
31116 var minY = Math.ceil(this.thumbEl.getTop(true));
31118 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31119 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31121 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31122 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31124 x = x - this.mouseX;
31125 y = y - this.mouseY;
31127 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31128 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31130 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31131 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31133 this.previewEl.setLeft(bgX);
31134 this.previewEl.setTop(bgY);
31136 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31137 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31140 onMouseUp : function(e)
31144 this.dragable = false;
31147 onMouseWheel : function(e)
31151 this.startScale = this.scale;
31153 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31155 if(!this.zoomable()){
31156 this.scale = this.startScale;
31165 zoomable : function()
31167 var minScale = this.thumbEl.getWidth() / this.minWidth;
31169 if(this.minWidth < this.minHeight){
31170 minScale = this.thumbEl.getHeight() / this.minHeight;
31173 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31174 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31178 (this.rotate == 0 || this.rotate == 180) &&
31180 width > this.imageEl.OriginWidth ||
31181 height > this.imageEl.OriginHeight ||
31182 (width < this.minWidth && height < this.minHeight)
31190 (this.rotate == 90 || this.rotate == 270) &&
31192 width > this.imageEl.OriginWidth ||
31193 height > this.imageEl.OriginHeight ||
31194 (width < this.minHeight && height < this.minWidth)
31201 !this.isDocument &&
31202 (this.rotate == 0 || this.rotate == 180) &&
31204 width < this.minWidth ||
31205 width > this.imageEl.OriginWidth ||
31206 height < this.minHeight ||
31207 height > this.imageEl.OriginHeight
31214 !this.isDocument &&
31215 (this.rotate == 90 || this.rotate == 270) &&
31217 width < this.minHeight ||
31218 width > this.imageEl.OriginWidth ||
31219 height < this.minWidth ||
31220 height > this.imageEl.OriginHeight
31230 onRotateLeft : function(e)
31232 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31234 var minScale = this.thumbEl.getWidth() / this.minWidth;
31236 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31237 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31239 this.startScale = this.scale;
31241 while (this.getScaleLevel() < minScale){
31243 this.scale = this.scale + 1;
31245 if(!this.zoomable()){
31250 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31251 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31256 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31263 this.scale = this.startScale;
31265 this.onRotateFail();
31270 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31272 if(this.isDocument){
31273 this.setThumbBoxSize();
31274 this.setThumbBoxPosition();
31275 this.setCanvasPosition();
31280 this.fireEvent('rotate', this, 'left');
31284 onRotateRight : function(e)
31286 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31288 var minScale = this.thumbEl.getWidth() / this.minWidth;
31290 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31291 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31293 this.startScale = this.scale;
31295 while (this.getScaleLevel() < minScale){
31297 this.scale = this.scale + 1;
31299 if(!this.zoomable()){
31304 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31305 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31310 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31317 this.scale = this.startScale;
31319 this.onRotateFail();
31324 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31326 if(this.isDocument){
31327 this.setThumbBoxSize();
31328 this.setThumbBoxPosition();
31329 this.setCanvasPosition();
31334 this.fireEvent('rotate', this, 'right');
31337 onRotateFail : function()
31339 this.errorEl.show(true);
31343 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31348 this.previewEl.dom.innerHTML = '';
31350 var canvasEl = document.createElement("canvas");
31352 var contextEl = canvasEl.getContext("2d");
31354 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31355 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31356 var center = this.imageEl.OriginWidth / 2;
31358 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31359 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31360 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31361 center = this.imageEl.OriginHeight / 2;
31364 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31366 contextEl.translate(center, center);
31367 contextEl.rotate(this.rotate * Math.PI / 180);
31369 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31371 this.canvasEl = document.createElement("canvas");
31373 this.contextEl = this.canvasEl.getContext("2d");
31375 switch (this.rotate) {
31378 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31379 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31381 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31386 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31389 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31394 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31399 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31400 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31402 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31403 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);
31407 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);
31412 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31413 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31415 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31416 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31420 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);
31427 this.previewEl.appendChild(this.canvasEl);
31429 this.setCanvasPosition();
31434 if(!this.canvasLoaded){
31438 var imageCanvas = document.createElement("canvas");
31440 var imageContext = imageCanvas.getContext("2d");
31442 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31443 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31445 var center = imageCanvas.width / 2;
31447 imageContext.translate(center, center);
31449 imageContext.rotate(this.rotate * Math.PI / 180);
31451 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31453 var canvas = document.createElement("canvas");
31455 var context = canvas.getContext("2d");
31457 canvas.width = this.minWidth;
31458 canvas.height = this.minHeight;
31460 switch (this.rotate) {
31463 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31464 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31466 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31467 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31469 var targetWidth = this.minWidth - 2 * x;
31470 var targetHeight = this.minHeight - 2 * y;
31474 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31475 scale = targetWidth / width;
31478 if(x > 0 && y == 0){
31479 scale = targetHeight / height;
31482 if(x > 0 && y > 0){
31483 scale = targetWidth / width;
31485 if(width < height){
31486 scale = targetHeight / height;
31490 context.scale(scale, scale);
31492 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31493 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31495 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31496 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31498 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31503 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31504 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31506 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31509 var targetWidth = this.minWidth - 2 * x;
31510 var targetHeight = this.minHeight - 2 * y;
31514 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515 scale = targetWidth / width;
31518 if(x > 0 && y == 0){
31519 scale = targetHeight / height;
31522 if(x > 0 && y > 0){
31523 scale = targetWidth / width;
31525 if(width < height){
31526 scale = targetHeight / height;
31530 context.scale(scale, scale);
31532 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31535 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31538 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31540 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31545 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31546 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31548 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31549 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31551 var targetWidth = this.minWidth - 2 * x;
31552 var targetHeight = this.minHeight - 2 * y;
31556 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31557 scale = targetWidth / width;
31560 if(x > 0 && y == 0){
31561 scale = targetHeight / height;
31564 if(x > 0 && y > 0){
31565 scale = targetWidth / width;
31567 if(width < height){
31568 scale = targetHeight / height;
31572 context.scale(scale, scale);
31574 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31575 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31577 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31578 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31580 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31581 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31583 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31588 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31589 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31591 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31592 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31594 var targetWidth = this.minWidth - 2 * x;
31595 var targetHeight = this.minHeight - 2 * y;
31599 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31600 scale = targetWidth / width;
31603 if(x > 0 && y == 0){
31604 scale = targetHeight / height;
31607 if(x > 0 && y > 0){
31608 scale = targetWidth / width;
31610 if(width < height){
31611 scale = targetHeight / height;
31615 context.scale(scale, scale);
31617 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31618 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31620 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31621 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31623 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31625 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31632 this.cropData = canvas.toDataURL(this.cropType);
31634 if(this.fireEvent('crop', this, this.cropData) !== false){
31635 this.process(this.file, this.cropData);
31642 setThumbBoxSize : function()
31646 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31647 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31648 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31650 this.minWidth = width;
31651 this.minHeight = height;
31653 if(this.rotate == 90 || this.rotate == 270){
31654 this.minWidth = height;
31655 this.minHeight = width;
31660 width = Math.ceil(this.minWidth * height / this.minHeight);
31662 if(this.minWidth > this.minHeight){
31664 height = Math.ceil(this.minHeight * width / this.minWidth);
31667 this.thumbEl.setStyle({
31668 width : width + 'px',
31669 height : height + 'px'
31676 setThumbBoxPosition : function()
31678 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31679 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31681 this.thumbEl.setLeft(x);
31682 this.thumbEl.setTop(y);
31686 baseRotateLevel : function()
31688 this.baseRotate = 1;
31691 typeof(this.exif) != 'undefined' &&
31692 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31693 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31695 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31698 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31702 baseScaleLevel : function()
31706 if(this.isDocument){
31708 if(this.baseRotate == 6 || this.baseRotate == 8){
31710 height = this.thumbEl.getHeight();
31711 this.baseScale = height / this.imageEl.OriginWidth;
31713 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31714 width = this.thumbEl.getWidth();
31715 this.baseScale = width / this.imageEl.OriginHeight;
31721 height = this.thumbEl.getHeight();
31722 this.baseScale = height / this.imageEl.OriginHeight;
31724 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31725 width = this.thumbEl.getWidth();
31726 this.baseScale = width / this.imageEl.OriginWidth;
31732 if(this.baseRotate == 6 || this.baseRotate == 8){
31734 width = this.thumbEl.getHeight();
31735 this.baseScale = width / this.imageEl.OriginHeight;
31737 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31738 height = this.thumbEl.getWidth();
31739 this.baseScale = height / this.imageEl.OriginHeight;
31742 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31743 height = this.thumbEl.getWidth();
31744 this.baseScale = height / this.imageEl.OriginHeight;
31746 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31747 width = this.thumbEl.getHeight();
31748 this.baseScale = width / this.imageEl.OriginWidth;
31755 width = this.thumbEl.getWidth();
31756 this.baseScale = width / this.imageEl.OriginWidth;
31758 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31759 height = this.thumbEl.getHeight();
31760 this.baseScale = height / this.imageEl.OriginHeight;
31763 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31765 height = this.thumbEl.getHeight();
31766 this.baseScale = height / this.imageEl.OriginHeight;
31768 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31769 width = this.thumbEl.getWidth();
31770 this.baseScale = width / this.imageEl.OriginWidth;
31778 getScaleLevel : function()
31780 return this.baseScale * Math.pow(1.1, this.scale);
31783 onTouchStart : function(e)
31785 if(!this.canvasLoaded){
31786 this.beforeSelectFile(e);
31790 var touches = e.browserEvent.touches;
31796 if(touches.length == 1){
31797 this.onMouseDown(e);
31801 if(touches.length != 2){
31807 for(var i = 0, finger; finger = touches[i]; i++){
31808 coords.push(finger.pageX, finger.pageY);
31811 var x = Math.pow(coords[0] - coords[2], 2);
31812 var y = Math.pow(coords[1] - coords[3], 2);
31814 this.startDistance = Math.sqrt(x + y);
31816 this.startScale = this.scale;
31818 this.pinching = true;
31819 this.dragable = false;
31823 onTouchMove : function(e)
31825 if(!this.pinching && !this.dragable){
31829 var touches = e.browserEvent.touches;
31836 this.onMouseMove(e);
31842 for(var i = 0, finger; finger = touches[i]; i++){
31843 coords.push(finger.pageX, finger.pageY);
31846 var x = Math.pow(coords[0] - coords[2], 2);
31847 var y = Math.pow(coords[1] - coords[3], 2);
31849 this.endDistance = Math.sqrt(x + y);
31851 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31853 if(!this.zoomable()){
31854 this.scale = this.startScale;
31862 onTouchEnd : function(e)
31864 this.pinching = false;
31865 this.dragable = false;
31869 process : function(file, crop)
31872 this.maskEl.mask(this.loadingText);
31875 this.xhr = new XMLHttpRequest();
31877 file.xhr = this.xhr;
31879 this.xhr.open(this.method, this.url, true);
31882 "Accept": "application/json",
31883 "Cache-Control": "no-cache",
31884 "X-Requested-With": "XMLHttpRequest"
31887 for (var headerName in headers) {
31888 var headerValue = headers[headerName];
31890 this.xhr.setRequestHeader(headerName, headerValue);
31896 this.xhr.onload = function()
31898 _this.xhrOnLoad(_this.xhr);
31901 this.xhr.onerror = function()
31903 _this.xhrOnError(_this.xhr);
31906 var formData = new FormData();
31908 formData.append('returnHTML', 'NO');
31911 formData.append('crop', crop);
31914 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31915 formData.append(this.paramName, file, file.name);
31918 if(typeof(file.filename) != 'undefined'){
31919 formData.append('filename', file.filename);
31922 if(typeof(file.mimetype) != 'undefined'){
31923 formData.append('mimetype', file.mimetype);
31926 if(this.fireEvent('arrange', this, formData) != false){
31927 this.xhr.send(formData);
31931 xhrOnLoad : function(xhr)
31934 this.maskEl.unmask();
31937 if (xhr.readyState !== 4) {
31938 this.fireEvent('exception', this, xhr);
31942 var response = Roo.decode(xhr.responseText);
31944 if(!response.success){
31945 this.fireEvent('exception', this, xhr);
31949 var response = Roo.decode(xhr.responseText);
31951 this.fireEvent('upload', this, response);
31955 xhrOnError : function()
31958 this.maskEl.unmask();
31961 Roo.log('xhr on error');
31963 var response = Roo.decode(xhr.responseText);
31969 prepare : function(file)
31972 this.maskEl.mask(this.loadingText);
31978 if(typeof(file) === 'string'){
31979 this.loadCanvas(file);
31983 if(!file || !this.urlAPI){
31988 this.cropType = file.type;
31992 if(this.fireEvent('prepare', this, this.file) != false){
31994 var reader = new FileReader();
31996 reader.onload = function (e) {
31997 if (e.target.error) {
31998 Roo.log(e.target.error);
32002 var buffer = e.target.result,
32003 dataView = new DataView(buffer),
32005 maxOffset = dataView.byteLength - 4,
32009 if (dataView.getUint16(0) === 0xffd8) {
32010 while (offset < maxOffset) {
32011 markerBytes = dataView.getUint16(offset);
32013 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32014 markerLength = dataView.getUint16(offset + 2) + 2;
32015 if (offset + markerLength > dataView.byteLength) {
32016 Roo.log('Invalid meta data: Invalid segment size.');
32020 if(markerBytes == 0xffe1){
32021 _this.parseExifData(
32028 offset += markerLength;
32038 var url = _this.urlAPI.createObjectURL(_this.file);
32040 _this.loadCanvas(url);
32045 reader.readAsArrayBuffer(this.file);
32051 parseExifData : function(dataView, offset, length)
32053 var tiffOffset = offset + 10,
32057 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32058 // No Exif data, might be XMP data instead
32062 // Check for the ASCII code for "Exif" (0x45786966):
32063 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32064 // No Exif data, might be XMP data instead
32067 if (tiffOffset + 8 > dataView.byteLength) {
32068 Roo.log('Invalid Exif data: Invalid segment size.');
32071 // Check for the two null bytes:
32072 if (dataView.getUint16(offset + 8) !== 0x0000) {
32073 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32076 // Check the byte alignment:
32077 switch (dataView.getUint16(tiffOffset)) {
32079 littleEndian = true;
32082 littleEndian = false;
32085 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32088 // Check for the TIFF tag marker (0x002A):
32089 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32090 Roo.log('Invalid Exif data: Missing TIFF marker.');
32093 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32094 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32096 this.parseExifTags(
32099 tiffOffset + dirOffset,
32104 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32109 if (dirOffset + 6 > dataView.byteLength) {
32110 Roo.log('Invalid Exif data: Invalid directory offset.');
32113 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32114 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32115 if (dirEndOffset + 4 > dataView.byteLength) {
32116 Roo.log('Invalid Exif data: Invalid directory size.');
32119 for (i = 0; i < tagsNumber; i += 1) {
32123 dirOffset + 2 + 12 * i, // tag offset
32127 // Return the offset to the next directory:
32128 return dataView.getUint32(dirEndOffset, littleEndian);
32131 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32133 var tag = dataView.getUint16(offset, littleEndian);
32135 this.exif[tag] = this.getExifValue(
32139 dataView.getUint16(offset + 2, littleEndian), // tag type
32140 dataView.getUint32(offset + 4, littleEndian), // tag length
32145 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32147 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32156 Roo.log('Invalid Exif data: Invalid tag type.');
32160 tagSize = tagType.size * length;
32161 // Determine if the value is contained in the dataOffset bytes,
32162 // or if the value at the dataOffset is a pointer to the actual data:
32163 dataOffset = tagSize > 4 ?
32164 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32165 if (dataOffset + tagSize > dataView.byteLength) {
32166 Roo.log('Invalid Exif data: Invalid data offset.');
32169 if (length === 1) {
32170 return tagType.getValue(dataView, dataOffset, littleEndian);
32173 for (i = 0; i < length; i += 1) {
32174 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32177 if (tagType.ascii) {
32179 // Concatenate the chars:
32180 for (i = 0; i < values.length; i += 1) {
32182 // Ignore the terminating NULL byte(s):
32183 if (c === '\u0000') {
32195 Roo.apply(Roo.bootstrap.UploadCropbox, {
32197 'Orientation': 0x0112
32201 1: 0, //'top-left',
32203 3: 180, //'bottom-right',
32204 // 4: 'bottom-left',
32206 6: 90, //'right-top',
32207 // 7: 'right-bottom',
32208 8: 270 //'left-bottom'
32212 // byte, 8-bit unsigned int:
32214 getValue: function (dataView, dataOffset) {
32215 return dataView.getUint8(dataOffset);
32219 // ascii, 8-bit byte:
32221 getValue: function (dataView, dataOffset) {
32222 return String.fromCharCode(dataView.getUint8(dataOffset));
32227 // short, 16 bit int:
32229 getValue: function (dataView, dataOffset, littleEndian) {
32230 return dataView.getUint16(dataOffset, littleEndian);
32234 // long, 32 bit int:
32236 getValue: function (dataView, dataOffset, littleEndian) {
32237 return dataView.getUint32(dataOffset, littleEndian);
32241 // rational = two long values, first is numerator, second is denominator:
32243 getValue: function (dataView, dataOffset, littleEndian) {
32244 return dataView.getUint32(dataOffset, littleEndian) /
32245 dataView.getUint32(dataOffset + 4, littleEndian);
32249 // slong, 32 bit signed int:
32251 getValue: function (dataView, dataOffset, littleEndian) {
32252 return dataView.getInt32(dataOffset, littleEndian);
32256 // srational, two slongs, first is numerator, second is denominator:
32258 getValue: function (dataView, dataOffset, littleEndian) {
32259 return dataView.getInt32(dataOffset, littleEndian) /
32260 dataView.getInt32(dataOffset + 4, littleEndian);
32270 cls : 'btn-group roo-upload-cropbox-rotate-left',
32271 action : 'rotate-left',
32275 cls : 'btn btn-default',
32276 html : '<i class="fa fa-undo"></i>'
32282 cls : 'btn-group roo-upload-cropbox-picture',
32283 action : 'picture',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-picture-o"></i>'
32294 cls : 'btn-group roo-upload-cropbox-rotate-right',
32295 action : 'rotate-right',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-repeat"></i>'
32308 cls : 'btn-group roo-upload-cropbox-rotate-left',
32309 action : 'rotate-left',
32313 cls : 'btn btn-default',
32314 html : '<i class="fa fa-undo"></i>'
32320 cls : 'btn-group roo-upload-cropbox-download',
32321 action : 'download',
32325 cls : 'btn btn-default',
32326 html : '<i class="fa fa-download"></i>'
32332 cls : 'btn-group roo-upload-cropbox-crop',
32337 cls : 'btn btn-default',
32338 html : '<i class="fa fa-crop"></i>'
32344 cls : 'btn-group roo-upload-cropbox-trash',
32349 cls : 'btn btn-default',
32350 html : '<i class="fa fa-trash"></i>'
32356 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357 action : 'rotate-right',
32361 cls : 'btn btn-default',
32362 html : '<i class="fa fa-repeat"></i>'
32370 cls : 'btn-group roo-upload-cropbox-rotate-left',
32371 action : 'rotate-left',
32375 cls : 'btn btn-default',
32376 html : '<i class="fa fa-undo"></i>'
32382 cls : 'btn-group roo-upload-cropbox-rotate-right',
32383 action : 'rotate-right',
32387 cls : 'btn btn-default',
32388 html : '<i class="fa fa-repeat"></i>'
32401 * @class Roo.bootstrap.DocumentManager
32402 * @extends Roo.bootstrap.Component
32403 * Bootstrap DocumentManager class
32404 * @cfg {String} paramName default 'imageUpload'
32405 * @cfg {String} toolTipName default 'filename'
32406 * @cfg {String} method default POST
32407 * @cfg {String} url action url
32408 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32409 * @cfg {Boolean} multiple multiple upload default true
32410 * @cfg {Number} thumbSize default 300
32411 * @cfg {String} fieldLabel
32412 * @cfg {Number} labelWidth default 4
32413 * @cfg {String} labelAlign (left|top) default left
32414 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32415 * @cfg {Number} labellg set the width of label (1-12)
32416 * @cfg {Number} labelmd set the width of label (1-12)
32417 * @cfg {Number} labelsm set the width of label (1-12)
32418 * @cfg {Number} labelxs set the width of label (1-12)
32421 * Create a new DocumentManager
32422 * @param {Object} config The config object
32425 Roo.bootstrap.DocumentManager = function(config){
32426 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32429 this.delegates = [];
32434 * Fire when initial the DocumentManager
32435 * @param {Roo.bootstrap.DocumentManager} this
32440 * inspect selected file
32441 * @param {Roo.bootstrap.DocumentManager} this
32442 * @param {File} file
32447 * Fire when xhr load exception
32448 * @param {Roo.bootstrap.DocumentManager} this
32449 * @param {XMLHttpRequest} xhr
32451 "exception" : true,
32453 * @event afterupload
32454 * Fire when xhr load exception
32455 * @param {Roo.bootstrap.DocumentManager} this
32456 * @param {XMLHttpRequest} xhr
32458 "afterupload" : true,
32461 * prepare the form data
32462 * @param {Roo.bootstrap.DocumentManager} this
32463 * @param {Object} formData
32468 * Fire when remove the file
32469 * @param {Roo.bootstrap.DocumentManager} this
32470 * @param {Object} file
32475 * Fire after refresh the file
32476 * @param {Roo.bootstrap.DocumentManager} this
32481 * Fire after click the image
32482 * @param {Roo.bootstrap.DocumentManager} this
32483 * @param {Object} file
32488 * Fire when upload a image and editable set to true
32489 * @param {Roo.bootstrap.DocumentManager} this
32490 * @param {Object} file
32494 * @event beforeselectfile
32495 * Fire before select file
32496 * @param {Roo.bootstrap.DocumentManager} this
32498 "beforeselectfile" : true,
32501 * Fire before process file
32502 * @param {Roo.bootstrap.DocumentManager} this
32503 * @param {Object} file
32507 * @event previewrendered
32508 * Fire when preview rendered
32509 * @param {Roo.bootstrap.DocumentManager} this
32510 * @param {Object} file
32512 "previewrendered" : true,
32515 "previewResize" : true
32520 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32529 paramName : 'imageUpload',
32530 toolTipName : 'filename',
32533 labelAlign : 'left',
32543 getAutoCreate : function()
32545 var managerWidget = {
32547 cls : 'roo-document-manager',
32551 cls : 'roo-document-manager-selector',
32556 cls : 'roo-document-manager-uploader',
32560 cls : 'roo-document-manager-upload-btn',
32561 html : '<i class="fa fa-plus"></i>'
32572 cls : 'column col-md-12',
32577 if(this.fieldLabel.length){
32582 cls : 'column col-md-12',
32583 html : this.fieldLabel
32587 cls : 'column col-md-12',
32592 if(this.labelAlign == 'left'){
32597 html : this.fieldLabel
32606 if(this.labelWidth > 12){
32607 content[0].style = "width: " + this.labelWidth + 'px';
32610 if(this.labelWidth < 13 && this.labelmd == 0){
32611 this.labelmd = this.labelWidth;
32614 if(this.labellg > 0){
32615 content[0].cls += ' col-lg-' + this.labellg;
32616 content[1].cls += ' col-lg-' + (12 - this.labellg);
32619 if(this.labelmd > 0){
32620 content[0].cls += ' col-md-' + this.labelmd;
32621 content[1].cls += ' col-md-' + (12 - this.labelmd);
32624 if(this.labelsm > 0){
32625 content[0].cls += ' col-sm-' + this.labelsm;
32626 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32629 if(this.labelxs > 0){
32630 content[0].cls += ' col-xs-' + this.labelxs;
32631 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32639 cls : 'row clearfix',
32647 initEvents : function()
32649 this.managerEl = this.el.select('.roo-document-manager', true).first();
32650 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32652 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32653 this.selectorEl.hide();
32656 this.selectorEl.attr('multiple', 'multiple');
32659 this.selectorEl.on('change', this.onFileSelected, this);
32661 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32662 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32664 this.uploader.on('click', this.onUploaderClick, this);
32666 this.renderProgressDialog();
32670 window.addEventListener("resize", function() { _this.refresh(); } );
32672 this.fireEvent('initial', this);
32675 renderProgressDialog : function()
32679 this.progressDialog = new Roo.bootstrap.Modal({
32680 cls : 'roo-document-manager-progress-dialog',
32681 allow_close : false,
32692 btnclick : function() {
32693 _this.uploadCancel();
32699 this.progressDialog.render(Roo.get(document.body));
32701 this.progress = new Roo.bootstrap.Progress({
32702 cls : 'roo-document-manager-progress',
32707 this.progress.render(this.progressDialog.getChildContainer());
32709 this.progressBar = new Roo.bootstrap.ProgressBar({
32710 cls : 'roo-document-manager-progress-bar',
32713 aria_valuemax : 12,
32717 this.progressBar.render(this.progress.getChildContainer());
32720 onUploaderClick : function(e)
32722 e.preventDefault();
32724 if(this.fireEvent('beforeselectfile', this) != false){
32725 this.selectorEl.dom.click();
32730 onFileSelected : function(e)
32732 e.preventDefault();
32734 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32738 Roo.each(this.selectorEl.dom.files, function(file){
32739 if(this.fireEvent('inspect', this, file) != false){
32740 this.files.push(file);
32750 this.selectorEl.dom.value = '';
32752 if(!this.files || !this.files.length){
32756 if(this.boxes > 0 && this.files.length > this.boxes){
32757 this.files = this.files.slice(0, this.boxes);
32760 this.uploader.show();
32762 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32763 this.uploader.hide();
32772 Roo.each(this.files, function(file){
32774 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32775 var f = this.renderPreview(file);
32780 if(file.type.indexOf('image') != -1){
32781 this.delegates.push(
32783 _this.process(file);
32784 }).createDelegate(this)
32792 _this.process(file);
32793 }).createDelegate(this)
32798 this.files = files;
32800 this.delegates = this.delegates.concat(docs);
32802 if(!this.delegates.length){
32807 this.progressBar.aria_valuemax = this.delegates.length;
32814 arrange : function()
32816 if(!this.delegates.length){
32817 this.progressDialog.hide();
32822 var delegate = this.delegates.shift();
32824 this.progressDialog.show();
32826 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32828 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32833 refresh : function()
32835 this.uploader.show();
32837 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32838 this.uploader.hide();
32841 Roo.isTouch ? this.closable(false) : this.closable(true);
32843 this.fireEvent('refresh', this);
32846 onRemove : function(e, el, o)
32848 e.preventDefault();
32850 this.fireEvent('remove', this, o);
32854 remove : function(o)
32858 Roo.each(this.files, function(file){
32859 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32868 this.files = files;
32875 Roo.each(this.files, function(file){
32880 file.target.remove();
32889 onClick : function(e, el, o)
32891 e.preventDefault();
32893 this.fireEvent('click', this, o);
32897 closable : function(closable)
32899 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32901 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32913 xhrOnLoad : function(xhr)
32915 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32919 if (xhr.readyState !== 4) {
32921 this.fireEvent('exception', this, xhr);
32925 var response = Roo.decode(xhr.responseText);
32927 if(!response.success){
32929 this.fireEvent('exception', this, xhr);
32933 var file = this.renderPreview(response.data);
32935 this.files.push(file);
32939 this.fireEvent('afterupload', this, xhr);
32943 xhrOnError : function(xhr)
32945 Roo.log('xhr on error');
32947 var response = Roo.decode(xhr.responseText);
32954 process : function(file)
32956 if(this.fireEvent('process', this, file) !== false){
32957 if(this.editable && file.type.indexOf('image') != -1){
32958 this.fireEvent('edit', this, file);
32962 this.uploadStart(file, false);
32969 uploadStart : function(file, crop)
32971 this.xhr = new XMLHttpRequest();
32973 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32978 file.xhr = this.xhr;
32980 this.managerEl.createChild({
32982 cls : 'roo-document-manager-loading',
32986 tooltip : file.name,
32987 cls : 'roo-document-manager-thumb',
32988 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32994 this.xhr.open(this.method, this.url, true);
32997 "Accept": "application/json",
32998 "Cache-Control": "no-cache",
32999 "X-Requested-With": "XMLHttpRequest"
33002 for (var headerName in headers) {
33003 var headerValue = headers[headerName];
33005 this.xhr.setRequestHeader(headerName, headerValue);
33011 this.xhr.onload = function()
33013 _this.xhrOnLoad(_this.xhr);
33016 this.xhr.onerror = function()
33018 _this.xhrOnError(_this.xhr);
33021 var formData = new FormData();
33023 formData.append('returnHTML', 'NO');
33026 formData.append('crop', crop);
33029 formData.append(this.paramName, file, file.name);
33036 if(this.fireEvent('prepare', this, formData, options) != false){
33038 if(options.manually){
33042 this.xhr.send(formData);
33046 this.uploadCancel();
33049 uploadCancel : function()
33055 this.delegates = [];
33057 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33064 renderPreview : function(file)
33066 if(typeof(file.target) != 'undefined' && file.target){
33070 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33072 var previewEl = this.managerEl.createChild({
33074 cls : 'roo-document-manager-preview',
33078 tooltip : file[this.toolTipName],
33079 cls : 'roo-document-manager-thumb',
33080 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33085 html : '<i class="fa fa-times-circle"></i>'
33090 var close = previewEl.select('button.close', true).first();
33092 close.on('click', this.onRemove, this, file);
33094 file.target = previewEl;
33096 var image = previewEl.select('img', true).first();
33100 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33102 image.on('click', this.onClick, this, file);
33104 this.fireEvent('previewrendered', this, file);
33110 onPreviewLoad : function(file, image)
33112 if(typeof(file.target) == 'undefined' || !file.target){
33116 var width = image.dom.naturalWidth || image.dom.width;
33117 var height = image.dom.naturalHeight || image.dom.height;
33119 if(!this.previewResize) {
33123 if(width > height){
33124 file.target.addClass('wide');
33128 file.target.addClass('tall');
33133 uploadFromSource : function(file, crop)
33135 this.xhr = new XMLHttpRequest();
33137 this.managerEl.createChild({
33139 cls : 'roo-document-manager-loading',
33143 tooltip : file.name,
33144 cls : 'roo-document-manager-thumb',
33145 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33151 this.xhr.open(this.method, this.url, true);
33154 "Accept": "application/json",
33155 "Cache-Control": "no-cache",
33156 "X-Requested-With": "XMLHttpRequest"
33159 for (var headerName in headers) {
33160 var headerValue = headers[headerName];
33162 this.xhr.setRequestHeader(headerName, headerValue);
33168 this.xhr.onload = function()
33170 _this.xhrOnLoad(_this.xhr);
33173 this.xhr.onerror = function()
33175 _this.xhrOnError(_this.xhr);
33178 var formData = new FormData();
33180 formData.append('returnHTML', 'NO');
33182 formData.append('crop', crop);
33184 if(typeof(file.filename) != 'undefined'){
33185 formData.append('filename', file.filename);
33188 if(typeof(file.mimetype) != 'undefined'){
33189 formData.append('mimetype', file.mimetype);
33194 if(this.fireEvent('prepare', this, formData) != false){
33195 this.xhr.send(formData);
33205 * @class Roo.bootstrap.DocumentViewer
33206 * @extends Roo.bootstrap.Component
33207 * Bootstrap DocumentViewer class
33208 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33209 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33212 * Create a new DocumentViewer
33213 * @param {Object} config The config object
33216 Roo.bootstrap.DocumentViewer = function(config){
33217 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33222 * Fire after initEvent
33223 * @param {Roo.bootstrap.DocumentViewer} this
33229 * @param {Roo.bootstrap.DocumentViewer} this
33234 * Fire after download button
33235 * @param {Roo.bootstrap.DocumentViewer} this
33240 * Fire after trash button
33241 * @param {Roo.bootstrap.DocumentViewer} this
33248 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33250 showDownload : true,
33254 getAutoCreate : function()
33258 cls : 'roo-document-viewer',
33262 cls : 'roo-document-viewer-body',
33266 cls : 'roo-document-viewer-thumb',
33270 cls : 'roo-document-viewer-image'
33278 cls : 'roo-document-viewer-footer',
33281 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33285 cls : 'btn-group roo-document-viewer-download',
33289 cls : 'btn btn-default',
33290 html : '<i class="fa fa-download"></i>'
33296 cls : 'btn-group roo-document-viewer-trash',
33300 cls : 'btn btn-default',
33301 html : '<i class="fa fa-trash"></i>'
33314 initEvents : function()
33316 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33317 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33319 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33320 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33322 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33323 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33325 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33326 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33328 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33329 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33331 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33332 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33334 this.bodyEl.on('click', this.onClick, this);
33335 this.downloadBtn.on('click', this.onDownload, this);
33336 this.trashBtn.on('click', this.onTrash, this);
33338 this.downloadBtn.hide();
33339 this.trashBtn.hide();
33341 if(this.showDownload){
33342 this.downloadBtn.show();
33345 if(this.showTrash){
33346 this.trashBtn.show();
33349 if(!this.showDownload && !this.showTrash) {
33350 this.footerEl.hide();
33355 initial : function()
33357 this.fireEvent('initial', this);
33361 onClick : function(e)
33363 e.preventDefault();
33365 this.fireEvent('click', this);
33368 onDownload : function(e)
33370 e.preventDefault();
33372 this.fireEvent('download', this);
33375 onTrash : function(e)
33377 e.preventDefault();
33379 this.fireEvent('trash', this);
33391 * @class Roo.bootstrap.form.FieldLabel
33392 * @extends Roo.bootstrap.Component
33393 * Bootstrap FieldLabel class
33394 * @cfg {String} html contents of the element
33395 * @cfg {String} tag tag of the element default label
33396 * @cfg {String} cls class of the element
33397 * @cfg {String} target label target
33398 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33399 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33400 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33401 * @cfg {String} iconTooltip default "This field is required"
33402 * @cfg {String} indicatorpos (left|right) default left
33405 * Create a new FieldLabel
33406 * @param {Object} config The config object
33409 Roo.bootstrap.form.FieldLabel = function(config){
33410 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33415 * Fires after the field has been marked as invalid.
33416 * @param {Roo.form.FieldLabel} this
33417 * @param {String} msg The validation message
33422 * Fires after the field has been validated with no errors.
33423 * @param {Roo.form.FieldLabel} this
33429 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33436 invalidClass : 'has-warning',
33437 validClass : 'has-success',
33438 iconTooltip : 'This field is required',
33439 indicatorpos : 'left',
33441 getAutoCreate : function(){
33444 if (!this.allowBlank) {
33450 cls : 'roo-bootstrap-field-label ' + this.cls,
33455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33456 tooltip : this.iconTooltip
33465 if(this.indicatorpos == 'right'){
33468 cls : 'roo-bootstrap-field-label ' + this.cls,
33477 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33478 tooltip : this.iconTooltip
33487 initEvents: function()
33489 Roo.bootstrap.Element.superclass.initEvents.call(this);
33491 this.indicator = this.indicatorEl();
33493 if(this.indicator){
33494 this.indicator.removeClass('visible');
33495 this.indicator.addClass('invisible');
33498 Roo.bootstrap.form.FieldLabel.register(this);
33501 indicatorEl : function()
33503 var indicator = this.el.select('i.roo-required-indicator',true).first();
33514 * Mark this field as valid
33516 markValid : function()
33518 if(this.indicator){
33519 this.indicator.removeClass('visible');
33520 this.indicator.addClass('invisible');
33522 if (Roo.bootstrap.version == 3) {
33523 this.el.removeClass(this.invalidClass);
33524 this.el.addClass(this.validClass);
33526 this.el.removeClass('is-invalid');
33527 this.el.addClass('is-valid');
33531 this.fireEvent('valid', this);
33535 * Mark this field as invalid
33536 * @param {String} msg The validation message
33538 markInvalid : function(msg)
33540 if(this.indicator){
33541 this.indicator.removeClass('invisible');
33542 this.indicator.addClass('visible');
33544 if (Roo.bootstrap.version == 3) {
33545 this.el.removeClass(this.validClass);
33546 this.el.addClass(this.invalidClass);
33548 this.el.removeClass('is-valid');
33549 this.el.addClass('is-invalid');
33553 this.fireEvent('invalid', this, msg);
33559 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33564 * register a FieldLabel Group
33565 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33567 register : function(label)
33569 if(this.groups.hasOwnProperty(label.target)){
33573 this.groups[label.target] = label;
33577 * fetch a FieldLabel Group based on the target
33578 * @param {string} target
33579 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33581 get: function(target) {
33582 if (typeof(this.groups[target]) == 'undefined') {
33586 return this.groups[target] ;
33595 * page DateSplitField.
33601 * @class Roo.bootstrap.form.DateSplitField
33602 * @extends Roo.bootstrap.Component
33603 * Bootstrap DateSplitField class
33604 * @cfg {string} fieldLabel - the label associated
33605 * @cfg {Number} labelWidth set the width of label (0-12)
33606 * @cfg {String} labelAlign (top|left)
33607 * @cfg {Boolean} dayAllowBlank (true|false) default false
33608 * @cfg {Boolean} monthAllowBlank (true|false) default false
33609 * @cfg {Boolean} yearAllowBlank (true|false) default false
33610 * @cfg {string} dayPlaceholder
33611 * @cfg {string} monthPlaceholder
33612 * @cfg {string} yearPlaceholder
33613 * @cfg {string} dayFormat default 'd'
33614 * @cfg {string} monthFormat default 'm'
33615 * @cfg {string} yearFormat default 'Y'
33616 * @cfg {Number} labellg set the width of label (1-12)
33617 * @cfg {Number} labelmd set the width of label (1-12)
33618 * @cfg {Number} labelsm set the width of label (1-12)
33619 * @cfg {Number} labelxs set the width of label (1-12)
33623 * Create a new DateSplitField
33624 * @param {Object} config The config object
33627 Roo.bootstrap.form.DateSplitField = function(config){
33628 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33634 * getting the data of years
33635 * @param {Roo.bootstrap.form.DateSplitField} this
33636 * @param {Object} years
33641 * getting the data of days
33642 * @param {Roo.bootstrap.form.DateSplitField} this
33643 * @param {Object} days
33648 * Fires after the field has been marked as invalid.
33649 * @param {Roo.form.Field} this
33650 * @param {String} msg The validation message
33655 * Fires after the field has been validated with no errors.
33656 * @param {Roo.form.Field} this
33662 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33665 labelAlign : 'top',
33667 dayAllowBlank : false,
33668 monthAllowBlank : false,
33669 yearAllowBlank : false,
33670 dayPlaceholder : '',
33671 monthPlaceholder : '',
33672 yearPlaceholder : '',
33676 isFormField : true,
33682 getAutoCreate : function()
33686 cls : 'row roo-date-split-field-group',
33691 cls : 'form-hidden-field roo-date-split-field-group-value',
33697 var labelCls = 'col-md-12';
33698 var contentCls = 'col-md-4';
33700 if(this.fieldLabel){
33704 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33708 html : this.fieldLabel
33713 if(this.labelAlign == 'left'){
33715 if(this.labelWidth > 12){
33716 label.style = "width: " + this.labelWidth + 'px';
33719 if(this.labelWidth < 13 && this.labelmd == 0){
33720 this.labelmd = this.labelWidth;
33723 if(this.labellg > 0){
33724 labelCls = ' col-lg-' + this.labellg;
33725 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33728 if(this.labelmd > 0){
33729 labelCls = ' col-md-' + this.labelmd;
33730 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33733 if(this.labelsm > 0){
33734 labelCls = ' col-sm-' + this.labelsm;
33735 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33738 if(this.labelxs > 0){
33739 labelCls = ' col-xs-' + this.labelxs;
33740 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33744 label.cls += ' ' + labelCls;
33746 cfg.cn.push(label);
33749 Roo.each(['day', 'month', 'year'], function(t){
33752 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33759 inputEl: function ()
33761 return this.el.select('.roo-date-split-field-group-value', true).first();
33764 onRender : function(ct, position)
33768 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33770 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33772 this.dayField = new Roo.bootstrap.form.ComboBox({
33773 allowBlank : this.dayAllowBlank,
33774 alwaysQuery : true,
33775 displayField : 'value',
33778 forceSelection : true,
33780 placeholder : this.dayPlaceholder,
33781 selectOnFocus : true,
33782 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33783 triggerAction : 'all',
33785 valueField : 'value',
33786 store : new Roo.data.SimpleStore({
33787 data : (function() {
33789 _this.fireEvent('days', _this, days);
33792 fields : [ 'value' ]
33795 select : function (_self, record, index)
33797 _this.setValue(_this.getValue());
33802 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33804 this.monthField = new Roo.bootstrap.form.MonthField({
33805 after : '<i class=\"fa fa-calendar\"></i>',
33806 allowBlank : this.monthAllowBlank,
33807 placeholder : this.monthPlaceholder,
33810 render : function (_self)
33812 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33813 e.preventDefault();
33817 select : function (_self, oldvalue, newvalue)
33819 _this.setValue(_this.getValue());
33824 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33826 this.yearField = new Roo.bootstrap.form.ComboBox({
33827 allowBlank : this.yearAllowBlank,
33828 alwaysQuery : true,
33829 displayField : 'value',
33832 forceSelection : true,
33834 placeholder : this.yearPlaceholder,
33835 selectOnFocus : true,
33836 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33837 triggerAction : 'all',
33839 valueField : 'value',
33840 store : new Roo.data.SimpleStore({
33841 data : (function() {
33843 _this.fireEvent('years', _this, years);
33846 fields : [ 'value' ]
33849 select : function (_self, record, index)
33851 _this.setValue(_this.getValue());
33856 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33859 setValue : function(v, format)
33861 this.inputEl.dom.value = v;
33863 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33865 var d = Date.parseDate(v, f);
33872 this.setDay(d.format(this.dayFormat));
33873 this.setMonth(d.format(this.monthFormat));
33874 this.setYear(d.format(this.yearFormat));
33881 setDay : function(v)
33883 this.dayField.setValue(v);
33884 this.inputEl.dom.value = this.getValue();
33889 setMonth : function(v)
33891 this.monthField.setValue(v, true);
33892 this.inputEl.dom.value = this.getValue();
33897 setYear : function(v)
33899 this.yearField.setValue(v);
33900 this.inputEl.dom.value = this.getValue();
33905 getDay : function()
33907 return this.dayField.getValue();
33910 getMonth : function()
33912 return this.monthField.getValue();
33915 getYear : function()
33917 return this.yearField.getValue();
33920 getValue : function()
33922 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33924 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33934 this.inputEl.dom.value = '';
33939 validate : function()
33941 var d = this.dayField.validate();
33942 var m = this.monthField.validate();
33943 var y = this.yearField.validate();
33948 (!this.dayAllowBlank && !d) ||
33949 (!this.monthAllowBlank && !m) ||
33950 (!this.yearAllowBlank && !y)
33955 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33964 this.markInvalid();
33969 markValid : function()
33972 var label = this.el.select('label', true).first();
33973 var icon = this.el.select('i.fa-star', true).first();
33979 this.fireEvent('valid', this);
33983 * Mark this field as invalid
33984 * @param {String} msg The validation message
33986 markInvalid : function(msg)
33989 var label = this.el.select('label', true).first();
33990 var icon = this.el.select('i.fa-star', true).first();
33992 if(label && !icon){
33993 this.el.select('.roo-date-split-field-label', true).createChild({
33995 cls : 'text-danger fa fa-lg fa-star',
33996 tooltip : 'This field is required',
33997 style : 'margin-right:5px;'
34001 this.fireEvent('invalid', this, msg);
34004 clearInvalid : function()
34006 var label = this.el.select('label', true).first();
34007 var icon = this.el.select('i.fa-star', true).first();
34013 this.fireEvent('valid', this);
34016 getName: function()
34026 * @class Roo.bootstrap.LayoutMasonry
34027 * @extends Roo.bootstrap.Component
34028 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34029 * Bootstrap Layout Masonry class
34032 * http://masonry.desandro.com
34034 * The idea is to render all the bricks based on vertical width...
34036 * The original code extends 'outlayer' - we might need to use that....
34039 * Create a new Element
34040 * @param {Object} config The config object
34043 Roo.bootstrap.LayoutMasonry = function(config){
34045 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34049 Roo.bootstrap.LayoutMasonry.register(this);
34055 * Fire after layout the items
34056 * @param {Roo.bootstrap.LayoutMasonry} this
34057 * @param {Roo.EventObject} e
34064 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34067 * @cfg {Boolean} isLayoutInstant = no animation?
34069 isLayoutInstant : false, // needed?
34072 * @cfg {Number} boxWidth width of the columns
34077 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34082 * @cfg {Number} padWidth padding below box..
34087 * @cfg {Number} gutter gutter width..
34092 * @cfg {Number} maxCols maximum number of columns
34098 * @cfg {Boolean} isAutoInitial defalut true
34100 isAutoInitial : true,
34105 * @cfg {Boolean} isHorizontal defalut false
34107 isHorizontal : false,
34109 currentSize : null,
34115 bricks: null, //CompositeElement
34119 _isLayoutInited : false,
34121 // isAlternative : false, // only use for vertical layout...
34124 * @cfg {Number} alternativePadWidth padding below box..
34126 alternativePadWidth : 50,
34128 selectedBrick : [],
34130 getAutoCreate : function(){
34132 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34136 cls: 'blog-masonary-wrapper ' + this.cls,
34138 cls : 'mas-boxes masonary'
34145 getChildContainer: function( )
34147 if (this.boxesEl) {
34148 return this.boxesEl;
34151 this.boxesEl = this.el.select('.mas-boxes').first();
34153 return this.boxesEl;
34157 initEvents : function()
34161 if(this.isAutoInitial){
34162 Roo.log('hook children rendered');
34163 this.on('childrenrendered', function() {
34164 Roo.log('children rendered');
34170 initial : function()
34172 this.selectedBrick = [];
34174 this.currentSize = this.el.getBox(true);
34176 Roo.EventManager.onWindowResize(this.resize, this);
34178 if(!this.isAutoInitial){
34186 //this.layout.defer(500,this);
34190 resize : function()
34192 var cs = this.el.getBox(true);
34195 this.currentSize.width == cs.width &&
34196 this.currentSize.x == cs.x &&
34197 this.currentSize.height == cs.height &&
34198 this.currentSize.y == cs.y
34200 Roo.log("no change in with or X or Y");
34204 this.currentSize = cs;
34210 layout : function()
34212 this._resetLayout();
34214 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34216 this.layoutItems( isInstant );
34218 this._isLayoutInited = true;
34220 this.fireEvent('layout', this);
34224 _resetLayout : function()
34226 if(this.isHorizontal){
34227 this.horizontalMeasureColumns();
34231 this.verticalMeasureColumns();
34235 verticalMeasureColumns : function()
34237 this.getContainerWidth();
34239 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34240 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34244 var boxWidth = this.boxWidth + this.padWidth;
34246 if(this.containerWidth < this.boxWidth){
34247 boxWidth = this.containerWidth
34250 var containerWidth = this.containerWidth;
34252 var cols = Math.floor(containerWidth / boxWidth);
34254 this.cols = Math.max( cols, 1 );
34256 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34258 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34260 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34262 this.colWidth = boxWidth + avail - this.padWidth;
34264 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34265 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34268 horizontalMeasureColumns : function()
34270 this.getContainerWidth();
34272 var boxWidth = this.boxWidth;
34274 if(this.containerWidth < boxWidth){
34275 boxWidth = this.containerWidth;
34278 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34280 this.el.setHeight(boxWidth);
34284 getContainerWidth : function()
34286 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34289 layoutItems : function( isInstant )
34291 Roo.log(this.bricks);
34293 var items = Roo.apply([], this.bricks);
34295 if(this.isHorizontal){
34296 this._horizontalLayoutItems( items , isInstant );
34300 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34301 // this._verticalAlternativeLayoutItems( items , isInstant );
34305 this._verticalLayoutItems( items , isInstant );
34309 _verticalLayoutItems : function ( items , isInstant)
34311 if ( !items || !items.length ) {
34316 ['xs', 'xs', 'xs', 'tall'],
34317 ['xs', 'xs', 'tall'],
34318 ['xs', 'xs', 'sm'],
34319 ['xs', 'xs', 'xs'],
34325 ['sm', 'xs', 'xs'],
34329 ['tall', 'xs', 'xs', 'xs'],
34330 ['tall', 'xs', 'xs'],
34342 Roo.each(items, function(item, k){
34344 switch (item.size) {
34345 // these layouts take up a full box,
34356 boxes.push([item]);
34379 var filterPattern = function(box, length)
34387 var pattern = box.slice(0, length);
34391 Roo.each(pattern, function(i){
34392 format.push(i.size);
34395 Roo.each(standard, function(s){
34397 if(String(s) != String(format)){
34406 if(!match && length == 1){
34411 filterPattern(box, length - 1);
34415 queue.push(pattern);
34417 box = box.slice(length, box.length);
34419 filterPattern(box, 4);
34425 Roo.each(boxes, function(box, k){
34431 if(box.length == 1){
34436 filterPattern(box, 4);
34440 this._processVerticalLayoutQueue( queue, isInstant );
34444 // _verticalAlternativeLayoutItems : function( items , isInstant )
34446 // if ( !items || !items.length ) {
34450 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34454 _horizontalLayoutItems : function ( items , isInstant)
34456 if ( !items || !items.length || items.length < 3) {
34462 var eItems = items.slice(0, 3);
34464 items = items.slice(3, items.length);
34467 ['xs', 'xs', 'xs', 'wide'],
34468 ['xs', 'xs', 'wide'],
34469 ['xs', 'xs', 'sm'],
34470 ['xs', 'xs', 'xs'],
34476 ['sm', 'xs', 'xs'],
34480 ['wide', 'xs', 'xs', 'xs'],
34481 ['wide', 'xs', 'xs'],
34494 Roo.each(items, function(item, k){
34496 switch (item.size) {
34507 boxes.push([item]);
34531 var filterPattern = function(box, length)
34539 var pattern = box.slice(0, length);
34543 Roo.each(pattern, function(i){
34544 format.push(i.size);
34547 Roo.each(standard, function(s){
34549 if(String(s) != String(format)){
34558 if(!match && length == 1){
34563 filterPattern(box, length - 1);
34567 queue.push(pattern);
34569 box = box.slice(length, box.length);
34571 filterPattern(box, 4);
34577 Roo.each(boxes, function(box, k){
34583 if(box.length == 1){
34588 filterPattern(box, 4);
34595 var pos = this.el.getBox(true);
34599 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34601 var hit_end = false;
34603 Roo.each(queue, function(box){
34607 Roo.each(box, function(b){
34609 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34619 Roo.each(box, function(b){
34621 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34624 mx = Math.max(mx, b.x);
34628 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34632 Roo.each(box, function(b){
34634 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34648 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34651 /** Sets position of item in DOM
34652 * @param {Element} item
34653 * @param {Number} x - horizontal position
34654 * @param {Number} y - vertical position
34655 * @param {Boolean} isInstant - disables transitions
34657 _processVerticalLayoutQueue : function( queue, isInstant )
34659 var pos = this.el.getBox(true);
34664 for (var i = 0; i < this.cols; i++){
34668 Roo.each(queue, function(box, k){
34670 var col = k % this.cols;
34672 Roo.each(box, function(b,kk){
34674 b.el.position('absolute');
34676 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34677 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34679 if(b.size == 'md-left' || b.size == 'md-right'){
34680 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34681 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34684 b.el.setWidth(width);
34685 b.el.setHeight(height);
34687 b.el.select('iframe',true).setSize(width,height);
34691 for (var i = 0; i < this.cols; i++){
34693 if(maxY[i] < maxY[col]){
34698 col = Math.min(col, i);
34702 x = pos.x + col * (this.colWidth + this.padWidth);
34706 var positions = [];
34708 switch (box.length){
34710 positions = this.getVerticalOneBoxColPositions(x, y, box);
34713 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34716 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34719 positions = this.getVerticalFourBoxColPositions(x, y, box);
34725 Roo.each(box, function(b,kk){
34727 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34729 var sz = b.el.getSize();
34731 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34739 for (var i = 0; i < this.cols; i++){
34740 mY = Math.max(mY, maxY[i]);
34743 this.el.setHeight(mY - pos.y);
34747 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34749 // var pos = this.el.getBox(true);
34752 // var maxX = pos.right;
34754 // var maxHeight = 0;
34756 // Roo.each(items, function(item, k){
34760 // item.el.position('absolute');
34762 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34764 // item.el.setWidth(width);
34766 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34768 // item.el.setHeight(height);
34771 // item.el.setXY([x, y], isInstant ? false : true);
34773 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34776 // y = y + height + this.alternativePadWidth;
34778 // maxHeight = maxHeight + height + this.alternativePadWidth;
34782 // this.el.setHeight(maxHeight);
34786 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34788 var pos = this.el.getBox(true);
34793 var maxX = pos.right;
34795 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34797 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34799 Roo.each(queue, function(box, k){
34801 Roo.each(box, function(b, kk){
34803 b.el.position('absolute');
34805 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34806 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34808 if(b.size == 'md-left' || b.size == 'md-right'){
34809 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34810 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34813 b.el.setWidth(width);
34814 b.el.setHeight(height);
34822 var positions = [];
34824 switch (box.length){
34826 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34829 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34832 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34835 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34841 Roo.each(box, function(b,kk){
34843 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34845 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34853 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34855 Roo.each(eItems, function(b,k){
34857 b.size = (k == 0) ? 'sm' : 'xs';
34858 b.x = (k == 0) ? 2 : 1;
34859 b.y = (k == 0) ? 2 : 1;
34861 b.el.position('absolute');
34863 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34865 b.el.setWidth(width);
34867 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34869 b.el.setHeight(height);
34873 var positions = [];
34876 x : maxX - this.unitWidth * 2 - this.gutter,
34881 x : maxX - this.unitWidth,
34882 y : minY + (this.unitWidth + this.gutter) * 2
34886 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34890 Roo.each(eItems, function(b,k){
34892 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34898 getVerticalOneBoxColPositions : function(x, y, box)
34902 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34904 if(box[0].size == 'md-left'){
34908 if(box[0].size == 'md-right'){
34913 x : x + (this.unitWidth + this.gutter) * rand,
34920 getVerticalTwoBoxColPositions : function(x, y, box)
34924 if(box[0].size == 'xs'){
34928 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34932 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34946 x : x + (this.unitWidth + this.gutter) * 2,
34947 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34954 getVerticalThreeBoxColPositions : function(x, y, box)
34958 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34966 x : x + (this.unitWidth + this.gutter) * 1,
34971 x : x + (this.unitWidth + this.gutter) * 2,
34979 if(box[0].size == 'xs' && box[1].size == 'xs'){
34988 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34992 x : x + (this.unitWidth + this.gutter) * 1,
35006 x : x + (this.unitWidth + this.gutter) * 2,
35011 x : x + (this.unitWidth + this.gutter) * 2,
35012 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35019 getVerticalFourBoxColPositions : function(x, y, box)
35023 if(box[0].size == 'xs'){
35032 y : y + (this.unitHeight + this.gutter) * 1
35037 y : y + (this.unitHeight + this.gutter) * 2
35041 x : x + (this.unitWidth + this.gutter) * 1,
35055 x : x + (this.unitWidth + this.gutter) * 2,
35060 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35061 y : y + (this.unitHeight + this.gutter) * 1
35065 x : x + (this.unitWidth + this.gutter) * 2,
35066 y : y + (this.unitWidth + this.gutter) * 2
35073 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35077 if(box[0].size == 'md-left'){
35079 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35086 if(box[0].size == 'md-right'){
35088 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35089 y : minY + (this.unitWidth + this.gutter) * 1
35095 var rand = Math.floor(Math.random() * (4 - box[0].y));
35098 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35099 y : minY + (this.unitWidth + this.gutter) * rand
35106 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35110 if(box[0].size == 'xs'){
35113 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35118 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35119 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35127 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35132 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35133 y : minY + (this.unitWidth + this.gutter) * 2
35140 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35144 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35152 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153 y : minY + (this.unitWidth + this.gutter) * 1
35157 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158 y : minY + (this.unitWidth + this.gutter) * 2
35165 if(box[0].size == 'xs' && box[1].size == 'xs'){
35168 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35173 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35178 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35179 y : minY + (this.unitWidth + this.gutter) * 1
35187 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35192 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35193 y : minY + (this.unitWidth + this.gutter) * 2
35197 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35198 y : minY + (this.unitWidth + this.gutter) * 2
35205 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35209 if(box[0].size == 'xs'){
35212 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35217 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35222 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),
35227 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35228 y : minY + (this.unitWidth + this.gutter) * 1
35236 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35241 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35242 y : minY + (this.unitWidth + this.gutter) * 2
35246 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35247 y : minY + (this.unitWidth + this.gutter) * 2
35251 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),
35252 y : minY + (this.unitWidth + this.gutter) * 2
35260 * remove a Masonry Brick
35261 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35263 removeBrick : function(brick_id)
35269 for (var i = 0; i<this.bricks.length; i++) {
35270 if (this.bricks[i].id == brick_id) {
35271 this.bricks.splice(i,1);
35272 this.el.dom.removeChild(Roo.get(brick_id).dom);
35279 * adds a Masonry Brick
35280 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35282 addBrick : function(cfg)
35284 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35285 //this.register(cn);
35286 cn.parentId = this.id;
35287 cn.render(this.el);
35292 * register a Masonry Brick
35293 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35296 register : function(brick)
35298 this.bricks.push(brick);
35299 brick.masonryId = this.id;
35303 * clear all the Masonry Brick
35305 clearAll : function()
35308 //this.getChildContainer().dom.innerHTML = "";
35309 this.el.dom.innerHTML = '';
35312 getSelected : function()
35314 if (!this.selectedBrick) {
35318 return this.selectedBrick;
35322 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35326 * register a Masonry Layout
35327 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35330 register : function(layout)
35332 this.groups[layout.id] = layout;
35335 * fetch a Masonry Layout based on the masonry layout ID
35336 * @param {string} the masonry layout to add
35337 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35340 get: function(layout_id) {
35341 if (typeof(this.groups[layout_id]) == 'undefined') {
35344 return this.groups[layout_id] ;
35356 * http://masonry.desandro.com
35358 * The idea is to render all the bricks based on vertical width...
35360 * The original code extends 'outlayer' - we might need to use that....
35366 * @class Roo.bootstrap.LayoutMasonryAuto
35367 * @extends Roo.bootstrap.Component
35368 * Bootstrap Layout Masonry class
35371 * Create a new Element
35372 * @param {Object} config The config object
35375 Roo.bootstrap.LayoutMasonryAuto = function(config){
35376 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35379 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35382 * @cfg {Boolean} isFitWidth - resize the width..
35384 isFitWidth : false, // options..
35386 * @cfg {Boolean} isOriginLeft = left align?
35388 isOriginLeft : true,
35390 * @cfg {Boolean} isOriginTop = top align?
35392 isOriginTop : false,
35394 * @cfg {Boolean} isLayoutInstant = no animation?
35396 isLayoutInstant : false, // needed?
35398 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35400 isResizingContainer : true,
35402 * @cfg {Number} columnWidth width of the columns
35408 * @cfg {Number} maxCols maximum number of columns
35413 * @cfg {Number} padHeight padding below box..
35419 * @cfg {Boolean} isAutoInitial defalut true
35422 isAutoInitial : true,
35428 initialColumnWidth : 0,
35429 currentSize : null,
35431 colYs : null, // array.
35438 bricks: null, //CompositeElement
35439 cols : 0, // array?
35440 // element : null, // wrapped now this.el
35441 _isLayoutInited : null,
35444 getAutoCreate : function(){
35448 cls: 'blog-masonary-wrapper ' + this.cls,
35450 cls : 'mas-boxes masonary'
35457 getChildContainer: function( )
35459 if (this.boxesEl) {
35460 return this.boxesEl;
35463 this.boxesEl = this.el.select('.mas-boxes').first();
35465 return this.boxesEl;
35469 initEvents : function()
35473 if(this.isAutoInitial){
35474 Roo.log('hook children rendered');
35475 this.on('childrenrendered', function() {
35476 Roo.log('children rendered');
35483 initial : function()
35485 this.reloadItems();
35487 this.currentSize = this.el.getBox(true);
35489 /// was window resize... - let's see if this works..
35490 Roo.EventManager.onWindowResize(this.resize, this);
35492 if(!this.isAutoInitial){
35497 this.layout.defer(500,this);
35500 reloadItems: function()
35502 this.bricks = this.el.select('.masonry-brick', true);
35504 this.bricks.each(function(b) {
35505 //Roo.log(b.getSize());
35506 if (!b.attr('originalwidth')) {
35507 b.attr('originalwidth', b.getSize().width);
35512 Roo.log(this.bricks.elements.length);
35515 resize : function()
35518 var cs = this.el.getBox(true);
35520 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35521 Roo.log("no change in with or X");
35524 this.currentSize = cs;
35528 layout : function()
35531 this._resetLayout();
35532 //this._manageStamps();
35534 // don't animate first layout
35535 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35536 this.layoutItems( isInstant );
35538 // flag for initalized
35539 this._isLayoutInited = true;
35542 layoutItems : function( isInstant )
35544 //var items = this._getItemsForLayout( this.items );
35545 // original code supports filtering layout items.. we just ignore it..
35547 this._layoutItems( this.bricks , isInstant );
35549 this._postLayout();
35551 _layoutItems : function ( items , isInstant)
35553 //this.fireEvent( 'layout', this, items );
35556 if ( !items || !items.elements.length ) {
35557 // no items, emit event with empty array
35562 items.each(function(item) {
35563 Roo.log("layout item");
35565 // get x/y object from method
35566 var position = this._getItemLayoutPosition( item );
35568 position.item = item;
35569 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35570 queue.push( position );
35573 this._processLayoutQueue( queue );
35575 /** Sets position of item in DOM
35576 * @param {Element} item
35577 * @param {Number} x - horizontal position
35578 * @param {Number} y - vertical position
35579 * @param {Boolean} isInstant - disables transitions
35581 _processLayoutQueue : function( queue )
35583 for ( var i=0, len = queue.length; i < len; i++ ) {
35584 var obj = queue[i];
35585 obj.item.position('absolute');
35586 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35592 * Any logic you want to do after each layout,
35593 * i.e. size the container
35595 _postLayout : function()
35597 this.resizeContainer();
35600 resizeContainer : function()
35602 if ( !this.isResizingContainer ) {
35605 var size = this._getContainerSize();
35607 this.el.setSize(size.width,size.height);
35608 this.boxesEl.setSize(size.width,size.height);
35614 _resetLayout : function()
35616 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35617 this.colWidth = this.el.getWidth();
35618 //this.gutter = this.el.getWidth();
35620 this.measureColumns();
35626 this.colYs.push( 0 );
35632 measureColumns : function()
35634 this.getContainerWidth();
35635 // if columnWidth is 0, default to outerWidth of first item
35636 if ( !this.columnWidth ) {
35637 var firstItem = this.bricks.first();
35638 Roo.log(firstItem);
35639 this.columnWidth = this.containerWidth;
35640 if (firstItem && firstItem.attr('originalwidth') ) {
35641 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35643 // columnWidth fall back to item of first element
35644 Roo.log("set column width?");
35645 this.initialColumnWidth = this.columnWidth ;
35647 // if first elem has no width, default to size of container
35652 if (this.initialColumnWidth) {
35653 this.columnWidth = this.initialColumnWidth;
35658 // column width is fixed at the top - however if container width get's smaller we should
35661 // this bit calcs how man columns..
35663 var columnWidth = this.columnWidth += this.gutter;
35665 // calculate columns
35666 var containerWidth = this.containerWidth + this.gutter;
35668 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35669 // fix rounding errors, typically with gutters
35670 var excess = columnWidth - containerWidth % columnWidth;
35673 // if overshoot is less than a pixel, round up, otherwise floor it
35674 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35675 cols = Math[ mathMethod ]( cols );
35676 this.cols = Math.max( cols, 1 );
35677 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35679 // padding positioning..
35680 var totalColWidth = this.cols * this.columnWidth;
35681 var padavail = this.containerWidth - totalColWidth;
35682 // so for 2 columns - we need 3 'pads'
35684 var padNeeded = (1+this.cols) * this.padWidth;
35686 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35688 this.columnWidth += padExtra
35689 //this.padWidth = Math.floor(padavail / ( this.cols));
35691 // adjust colum width so that padding is fixed??
35693 // we have 3 columns ... total = width * 3
35694 // we have X left over... that should be used by
35696 //if (this.expandC) {
35704 getContainerWidth : function()
35706 /* // container is parent if fit width
35707 var container = this.isFitWidth ? this.element.parentNode : this.element;
35708 // check that this.size and size are there
35709 // IE8 triggers resize on body size change, so they might not be
35711 var size = getSize( container ); //FIXME
35712 this.containerWidth = size && size.innerWidth; //FIXME
35715 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35719 _getItemLayoutPosition : function( item ) // what is item?
35721 // we resize the item to our columnWidth..
35723 item.setWidth(this.columnWidth);
35724 item.autoBoxAdjust = false;
35726 var sz = item.getSize();
35728 // how many columns does this brick span
35729 var remainder = this.containerWidth % this.columnWidth;
35731 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35732 // round if off by 1 pixel, otherwise use ceil
35733 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35734 colSpan = Math.min( colSpan, this.cols );
35736 // normally this should be '1' as we dont' currently allow multi width columns..
35738 var colGroup = this._getColGroup( colSpan );
35739 // get the minimum Y value from the columns
35740 var minimumY = Math.min.apply( Math, colGroup );
35741 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35743 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35745 // position the brick
35747 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35748 y: this.currentSize.y + minimumY + this.padHeight
35752 // apply setHeight to necessary columns
35753 var setHeight = minimumY + sz.height + this.padHeight;
35754 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35756 var setSpan = this.cols + 1 - colGroup.length;
35757 for ( var i = 0; i < setSpan; i++ ) {
35758 this.colYs[ shortColIndex + i ] = setHeight ;
35765 * @param {Number} colSpan - number of columns the element spans
35766 * @returns {Array} colGroup
35768 _getColGroup : function( colSpan )
35770 if ( colSpan < 2 ) {
35771 // if brick spans only one column, use all the column Ys
35776 // how many different places could this brick fit horizontally
35777 var groupCount = this.cols + 1 - colSpan;
35778 // for each group potential horizontal position
35779 for ( var i = 0; i < groupCount; i++ ) {
35780 // make an array of colY values for that one group
35781 var groupColYs = this.colYs.slice( i, i + colSpan );
35782 // and get the max value of the array
35783 colGroup[i] = Math.max.apply( Math, groupColYs );
35788 _manageStamp : function( stamp )
35790 var stampSize = stamp.getSize();
35791 var offset = stamp.getBox();
35792 // get the columns that this stamp affects
35793 var firstX = this.isOriginLeft ? offset.x : offset.right;
35794 var lastX = firstX + stampSize.width;
35795 var firstCol = Math.floor( firstX / this.columnWidth );
35796 firstCol = Math.max( 0, firstCol );
35798 var lastCol = Math.floor( lastX / this.columnWidth );
35799 // lastCol should not go over if multiple of columnWidth #425
35800 lastCol -= lastX % this.columnWidth ? 0 : 1;
35801 lastCol = Math.min( this.cols - 1, lastCol );
35803 // set colYs to bottom of the stamp
35804 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35807 for ( var i = firstCol; i <= lastCol; i++ ) {
35808 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35813 _getContainerSize : function()
35815 this.maxY = Math.max.apply( Math, this.colYs );
35820 if ( this.isFitWidth ) {
35821 size.width = this._getContainerFitWidth();
35827 _getContainerFitWidth : function()
35829 var unusedCols = 0;
35830 // count unused columns
35833 if ( this.colYs[i] !== 0 ) {
35838 // fit container to columns that have been used
35839 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35842 needsResizeLayout : function()
35844 var previousWidth = this.containerWidth;
35845 this.getContainerWidth();
35846 return previousWidth !== this.containerWidth;
35861 * @class Roo.bootstrap.MasonryBrick
35862 * @extends Roo.bootstrap.Component
35863 * Bootstrap MasonryBrick class
35866 * Create a new MasonryBrick
35867 * @param {Object} config The config object
35870 Roo.bootstrap.MasonryBrick = function(config){
35872 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35874 Roo.bootstrap.MasonryBrick.register(this);
35880 * When a MasonryBrick is clcik
35881 * @param {Roo.bootstrap.MasonryBrick} this
35882 * @param {Roo.EventObject} e
35888 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35891 * @cfg {String} title
35895 * @cfg {String} html
35899 * @cfg {String} bgimage
35903 * @cfg {String} videourl
35907 * @cfg {String} cls
35911 * @cfg {String} href
35915 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35920 * @cfg {String} placetitle (center|bottom)
35925 * @cfg {Boolean} isFitContainer defalut true
35927 isFitContainer : true,
35930 * @cfg {Boolean} preventDefault defalut false
35932 preventDefault : false,
35935 * @cfg {Boolean} inverse defalut false
35937 maskInverse : false,
35939 getAutoCreate : function()
35941 if(!this.isFitContainer){
35942 return this.getSplitAutoCreate();
35945 var cls = 'masonry-brick masonry-brick-full';
35947 if(this.href.length){
35948 cls += ' masonry-brick-link';
35951 if(this.bgimage.length){
35952 cls += ' masonry-brick-image';
35955 if(this.maskInverse){
35956 cls += ' mask-inverse';
35959 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35960 cls += ' enable-mask';
35964 cls += ' masonry-' + this.size + '-brick';
35967 if(this.placetitle.length){
35969 switch (this.placetitle) {
35971 cls += ' masonry-center-title';
35974 cls += ' masonry-bottom-title';
35981 if(!this.html.length && !this.bgimage.length){
35982 cls += ' masonry-center-title';
35985 if(!this.html.length && this.bgimage.length){
35986 cls += ' masonry-bottom-title';
35991 cls += ' ' + this.cls;
35995 tag: (this.href.length) ? 'a' : 'div',
36000 cls: 'masonry-brick-mask'
36004 cls: 'masonry-brick-paragraph',
36010 if(this.href.length){
36011 cfg.href = this.href;
36014 var cn = cfg.cn[1].cn;
36016 if(this.title.length){
36019 cls: 'masonry-brick-title',
36024 if(this.html.length){
36027 cls: 'masonry-brick-text',
36032 if (!this.title.length && !this.html.length) {
36033 cfg.cn[1].cls += ' hide';
36036 if(this.bgimage.length){
36039 cls: 'masonry-brick-image-view',
36044 if(this.videourl.length){
36045 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36046 // youtube support only?
36049 cls: 'masonry-brick-image-view',
36052 allowfullscreen : true
36060 getSplitAutoCreate : function()
36062 var cls = 'masonry-brick masonry-brick-split';
36064 if(this.href.length){
36065 cls += ' masonry-brick-link';
36068 if(this.bgimage.length){
36069 cls += ' masonry-brick-image';
36073 cls += ' masonry-' + this.size + '-brick';
36076 switch (this.placetitle) {
36078 cls += ' masonry-center-title';
36081 cls += ' masonry-bottom-title';
36084 if(!this.bgimage.length){
36085 cls += ' masonry-center-title';
36088 if(this.bgimage.length){
36089 cls += ' masonry-bottom-title';
36095 cls += ' ' + this.cls;
36099 tag: (this.href.length) ? 'a' : 'div',
36104 cls: 'masonry-brick-split-head',
36108 cls: 'masonry-brick-paragraph',
36115 cls: 'masonry-brick-split-body',
36121 if(this.href.length){
36122 cfg.href = this.href;
36125 if(this.title.length){
36126 cfg.cn[0].cn[0].cn.push({
36128 cls: 'masonry-brick-title',
36133 if(this.html.length){
36134 cfg.cn[1].cn.push({
36136 cls: 'masonry-brick-text',
36141 if(this.bgimage.length){
36142 cfg.cn[0].cn.push({
36144 cls: 'masonry-brick-image-view',
36149 if(this.videourl.length){
36150 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36151 // youtube support only?
36152 cfg.cn[0].cn.cn.push({
36154 cls: 'masonry-brick-image-view',
36157 allowfullscreen : true
36164 initEvents: function()
36166 switch (this.size) {
36199 this.el.on('touchstart', this.onTouchStart, this);
36200 this.el.on('touchmove', this.onTouchMove, this);
36201 this.el.on('touchend', this.onTouchEnd, this);
36202 this.el.on('contextmenu', this.onContextMenu, this);
36204 this.el.on('mouseenter' ,this.enter, this);
36205 this.el.on('mouseleave', this.leave, this);
36206 this.el.on('click', this.onClick, this);
36209 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36210 this.parent().bricks.push(this);
36215 onClick: function(e, el)
36217 var time = this.endTimer - this.startTimer;
36218 // Roo.log(e.preventDefault());
36221 e.preventDefault();
36226 if(!this.preventDefault){
36230 e.preventDefault();
36232 if (this.activeClass != '') {
36233 this.selectBrick();
36236 this.fireEvent('click', this, e);
36239 enter: function(e, el)
36241 e.preventDefault();
36243 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36247 if(this.bgimage.length && this.html.length){
36248 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36252 leave: function(e, el)
36254 e.preventDefault();
36256 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36260 if(this.bgimage.length && this.html.length){
36261 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36265 onTouchStart: function(e, el)
36267 // e.preventDefault();
36269 this.touchmoved = false;
36271 if(!this.isFitContainer){
36275 if(!this.bgimage.length || !this.html.length){
36279 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36281 this.timer = new Date().getTime();
36285 onTouchMove: function(e, el)
36287 this.touchmoved = true;
36290 onContextMenu : function(e,el)
36292 e.preventDefault();
36293 e.stopPropagation();
36297 onTouchEnd: function(e, el)
36299 // e.preventDefault();
36301 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36308 if(!this.bgimage.length || !this.html.length){
36310 if(this.href.length){
36311 window.location.href = this.href;
36317 if(!this.isFitContainer){
36321 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36323 window.location.href = this.href;
36326 //selection on single brick only
36327 selectBrick : function() {
36329 if (!this.parentId) {
36333 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36334 var index = m.selectedBrick.indexOf(this.id);
36337 m.selectedBrick.splice(index,1);
36338 this.el.removeClass(this.activeClass);
36342 for(var i = 0; i < m.selectedBrick.length; i++) {
36343 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36344 b.el.removeClass(b.activeClass);
36347 m.selectedBrick = [];
36349 m.selectedBrick.push(this.id);
36350 this.el.addClass(this.activeClass);
36354 isSelected : function(){
36355 return this.el.hasClass(this.activeClass);
36360 Roo.apply(Roo.bootstrap.MasonryBrick, {
36363 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36365 * register a Masonry Brick
36366 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36369 register : function(brick)
36371 //this.groups[brick.id] = brick;
36372 this.groups.add(brick.id, brick);
36375 * fetch a masonry brick based on the masonry brick ID
36376 * @param {string} the masonry brick to add
36377 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36380 get: function(brick_id)
36382 // if (typeof(this.groups[brick_id]) == 'undefined') {
36385 // return this.groups[brick_id] ;
36387 if(this.groups.key(brick_id)) {
36388 return this.groups.key(brick_id);
36406 * @class Roo.bootstrap.Brick
36407 * @extends Roo.bootstrap.Component
36408 * Bootstrap Brick class
36411 * Create a new Brick
36412 * @param {Object} config The config object
36415 Roo.bootstrap.Brick = function(config){
36416 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36422 * When a Brick is click
36423 * @param {Roo.bootstrap.Brick} this
36424 * @param {Roo.EventObject} e
36430 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36433 * @cfg {String} title
36437 * @cfg {String} html
36441 * @cfg {String} bgimage
36445 * @cfg {String} cls
36449 * @cfg {String} href
36453 * @cfg {String} video
36457 * @cfg {Boolean} square
36461 getAutoCreate : function()
36463 var cls = 'roo-brick';
36465 if(this.href.length){
36466 cls += ' roo-brick-link';
36469 if(this.bgimage.length){
36470 cls += ' roo-brick-image';
36473 if(!this.html.length && !this.bgimage.length){
36474 cls += ' roo-brick-center-title';
36477 if(!this.html.length && this.bgimage.length){
36478 cls += ' roo-brick-bottom-title';
36482 cls += ' ' + this.cls;
36486 tag: (this.href.length) ? 'a' : 'div',
36491 cls: 'roo-brick-paragraph',
36497 if(this.href.length){
36498 cfg.href = this.href;
36501 var cn = cfg.cn[0].cn;
36503 if(this.title.length){
36506 cls: 'roo-brick-title',
36511 if(this.html.length){
36514 cls: 'roo-brick-text',
36521 if(this.bgimage.length){
36524 cls: 'roo-brick-image-view',
36532 initEvents: function()
36534 if(this.title.length || this.html.length){
36535 this.el.on('mouseenter' ,this.enter, this);
36536 this.el.on('mouseleave', this.leave, this);
36539 Roo.EventManager.onWindowResize(this.resize, this);
36541 if(this.bgimage.length){
36542 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36543 this.imageEl.on('load', this.onImageLoad, this);
36550 onImageLoad : function()
36555 resize : function()
36557 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36559 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36561 if(this.bgimage.length){
36562 var image = this.el.select('.roo-brick-image-view', true).first();
36564 image.setWidth(paragraph.getWidth());
36567 image.setHeight(paragraph.getWidth());
36570 this.el.setHeight(image.getHeight());
36571 paragraph.setHeight(image.getHeight());
36577 enter: function(e, el)
36579 e.preventDefault();
36581 if(this.bgimage.length){
36582 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36583 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36587 leave: function(e, el)
36589 e.preventDefault();
36591 if(this.bgimage.length){
36592 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36593 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36608 * @class Roo.bootstrap.form.NumberField
36609 * @extends Roo.bootstrap.form.Input
36610 * Bootstrap NumberField class
36616 * Create a new NumberField
36617 * @param {Object} config The config object
36620 Roo.bootstrap.form.NumberField = function(config){
36621 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36624 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36627 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36629 allowDecimals : true,
36631 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36633 decimalSeparator : ".",
36635 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36637 decimalPrecision : 2,
36639 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36641 allowNegative : true,
36644 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36648 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36650 minValue : Number.NEGATIVE_INFINITY,
36652 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36654 maxValue : Number.MAX_VALUE,
36656 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36658 minText : "The minimum value for this field is {0}",
36660 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36662 maxText : "The maximum value for this field is {0}",
36664 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36665 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36667 nanText : "{0} is not a valid number",
36669 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36671 thousandsDelimiter : false,
36673 * @cfg {String} valueAlign alignment of value
36675 valueAlign : "left",
36677 getAutoCreate : function()
36679 var hiddenInput = {
36683 cls: 'hidden-number-input'
36687 hiddenInput.name = this.name;
36692 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36694 this.name = hiddenInput.name;
36696 if(cfg.cn.length > 0) {
36697 cfg.cn.push(hiddenInput);
36704 initEvents : function()
36706 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36708 var allowed = "0123456789";
36710 if(this.allowDecimals){
36711 allowed += this.decimalSeparator;
36714 if(this.allowNegative){
36718 if(this.thousandsDelimiter) {
36722 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36724 var keyPress = function(e){
36726 var k = e.getKey();
36728 var c = e.getCharCode();
36731 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36732 allowed.indexOf(String.fromCharCode(c)) === -1
36738 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36742 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36747 this.el.on("keypress", keyPress, this);
36750 validateValue : function(value)
36753 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36757 var num = this.parseValue(value);
36760 this.markInvalid(String.format(this.nanText, value));
36764 if(num < this.minValue){
36765 this.markInvalid(String.format(this.minText, this.minValue));
36769 if(num > this.maxValue){
36770 this.markInvalid(String.format(this.maxText, this.maxValue));
36777 getValue : function()
36779 var v = this.hiddenEl().getValue();
36781 return this.fixPrecision(this.parseValue(v));
36784 parseValue : function(value)
36786 if(this.thousandsDelimiter) {
36788 r = new RegExp(",", "g");
36789 value = value.replace(r, "");
36792 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36793 return isNaN(value) ? '' : value;
36796 fixPrecision : function(value)
36798 if(this.thousandsDelimiter) {
36800 r = new RegExp(",", "g");
36801 value = value.replace(r, "");
36804 var nan = isNaN(value);
36806 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36807 return nan ? '' : value;
36809 return parseFloat(value).toFixed(this.decimalPrecision);
36812 setValue : function(v)
36814 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36820 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36822 this.inputEl().dom.value = (v == '') ? '' :
36823 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36825 if(!this.allowZero && v === '0') {
36826 this.hiddenEl().dom.value = '';
36827 this.inputEl().dom.value = '';
36834 decimalPrecisionFcn : function(v)
36836 return Math.floor(v);
36839 beforeBlur : function()
36841 var v = this.parseValue(this.getRawValue());
36843 if(v || v === 0 || v === ''){
36848 hiddenEl : function()
36850 return this.el.select('input.hidden-number-input',true).first();
36862 * @class Roo.bootstrap.DocumentSlider
36863 * @extends Roo.bootstrap.Component
36864 * Bootstrap DocumentSlider class
36867 * Create a new DocumentViewer
36868 * @param {Object} config The config object
36871 Roo.bootstrap.DocumentSlider = function(config){
36872 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36879 * Fire after initEvent
36880 * @param {Roo.bootstrap.DocumentSlider} this
36885 * Fire after update
36886 * @param {Roo.bootstrap.DocumentSlider} this
36892 * @param {Roo.bootstrap.DocumentSlider} this
36898 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36904 getAutoCreate : function()
36908 cls : 'roo-document-slider',
36912 cls : 'roo-document-slider-header',
36916 cls : 'roo-document-slider-header-title'
36922 cls : 'roo-document-slider-body',
36926 cls : 'roo-document-slider-prev',
36930 cls : 'fa fa-chevron-left'
36936 cls : 'roo-document-slider-thumb',
36940 cls : 'roo-document-slider-image'
36946 cls : 'roo-document-slider-next',
36950 cls : 'fa fa-chevron-right'
36962 initEvents : function()
36964 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36965 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36967 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36968 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36970 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36971 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36973 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36974 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36976 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36977 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36979 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36980 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36982 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36983 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36985 this.thumbEl.on('click', this.onClick, this);
36987 this.prevIndicator.on('click', this.prev, this);
36989 this.nextIndicator.on('click', this.next, this);
36993 initial : function()
36995 if(this.files.length){
36996 this.indicator = 1;
37000 this.fireEvent('initial', this);
37003 update : function()
37005 this.imageEl.attr('src', this.files[this.indicator - 1]);
37007 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37009 this.prevIndicator.show();
37011 if(this.indicator == 1){
37012 this.prevIndicator.hide();
37015 this.nextIndicator.show();
37017 if(this.indicator == this.files.length){
37018 this.nextIndicator.hide();
37021 this.thumbEl.scrollTo('top');
37023 this.fireEvent('update', this);
37026 onClick : function(e)
37028 e.preventDefault();
37030 this.fireEvent('click', this);
37035 e.preventDefault();
37037 this.indicator = Math.max(1, this.indicator - 1);
37044 e.preventDefault();
37046 this.indicator = Math.min(this.files.length, this.indicator + 1);
37060 * @class Roo.bootstrap.form.RadioSet
37061 * @extends Roo.bootstrap.form.Input
37062 * @children Roo.bootstrap.form.Radio
37063 * Bootstrap RadioSet class
37064 * @cfg {String} indicatorpos (left|right) default left
37065 * @cfg {Boolean} inline (true|false) inline the element (default true)
37066 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37068 * Create a new RadioSet
37069 * @param {Object} config The config object
37072 Roo.bootstrap.form.RadioSet = function(config){
37074 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37078 Roo.bootstrap.form.RadioSet.register(this);
37083 * Fires when the element is checked or unchecked.
37084 * @param {Roo.bootstrap.form.RadioSet} this This radio
37085 * @param {Roo.bootstrap.form.Radio} item The checked item
37090 * Fires when the element is click.
37091 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37092 * @param {Roo.bootstrap.form.Radio} item The checked item
37093 * @param {Roo.EventObject} e The event object
37100 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37108 indicatorpos : 'left',
37110 getAutoCreate : function()
37114 cls : 'roo-radio-set-label',
37118 html : this.fieldLabel
37122 if (Roo.bootstrap.version == 3) {
37125 if(this.indicatorpos == 'left'){
37128 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37129 tooltip : 'This field is required'
37134 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37135 tooltip : 'This field is required'
37141 cls : 'roo-radio-set-items'
37144 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37146 if (align === 'left' && this.fieldLabel.length) {
37149 cls : "roo-radio-set-right",
37155 if(this.labelWidth > 12){
37156 label.style = "width: " + this.labelWidth + 'px';
37159 if(this.labelWidth < 13 && this.labelmd == 0){
37160 this.labelmd = this.labelWidth;
37163 if(this.labellg > 0){
37164 label.cls += ' col-lg-' + this.labellg;
37165 items.cls += ' col-lg-' + (12 - this.labellg);
37168 if(this.labelmd > 0){
37169 label.cls += ' col-md-' + this.labelmd;
37170 items.cls += ' col-md-' + (12 - this.labelmd);
37173 if(this.labelsm > 0){
37174 label.cls += ' col-sm-' + this.labelsm;
37175 items.cls += ' col-sm-' + (12 - this.labelsm);
37178 if(this.labelxs > 0){
37179 label.cls += ' col-xs-' + this.labelxs;
37180 items.cls += ' col-xs-' + (12 - this.labelxs);
37186 cls : 'roo-radio-set',
37190 cls : 'roo-radio-set-input',
37193 value : this.value ? this.value : ''
37200 if(this.weight.length){
37201 cfg.cls += ' roo-radio-' + this.weight;
37205 cfg.cls += ' roo-radio-set-inline';
37209 ['xs','sm','md','lg'].map(function(size){
37210 if (settings[size]) {
37211 cfg.cls += ' col-' + size + '-' + settings[size];
37219 initEvents : function()
37221 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37222 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37224 if(!this.fieldLabel.length){
37225 this.labelEl.hide();
37228 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37229 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37231 this.indicator = this.indicatorEl();
37233 if(this.indicator){
37234 this.indicator.addClass('invisible');
37237 this.originalValue = this.getValue();
37241 inputEl: function ()
37243 return this.el.select('.roo-radio-set-input', true).first();
37246 getChildContainer : function()
37248 return this.itemsEl;
37251 register : function(item)
37253 this.radioes.push(item);
37257 validate : function()
37259 if(this.getVisibilityEl().hasClass('hidden')){
37265 Roo.each(this.radioes, function(i){
37274 if(this.allowBlank) {
37278 if(this.disabled || valid){
37283 this.markInvalid();
37288 markValid : function()
37290 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37291 this.indicatorEl().removeClass('visible');
37292 this.indicatorEl().addClass('invisible');
37296 if (Roo.bootstrap.version == 3) {
37297 this.el.removeClass([this.invalidClass, this.validClass]);
37298 this.el.addClass(this.validClass);
37300 this.el.removeClass(['is-invalid','is-valid']);
37301 this.el.addClass(['is-valid']);
37303 this.fireEvent('valid', this);
37306 markInvalid : function(msg)
37308 if(this.allowBlank || this.disabled){
37312 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37313 this.indicatorEl().removeClass('invisible');
37314 this.indicatorEl().addClass('visible');
37316 if (Roo.bootstrap.version == 3) {
37317 this.el.removeClass([this.invalidClass, this.validClass]);
37318 this.el.addClass(this.invalidClass);
37320 this.el.removeClass(['is-invalid','is-valid']);
37321 this.el.addClass(['is-invalid']);
37324 this.fireEvent('invalid', this, msg);
37328 setValue : function(v, suppressEvent)
37330 if(this.value === v){
37337 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37340 Roo.each(this.radioes, function(i){
37342 i.el.removeClass('checked');
37345 Roo.each(this.radioes, function(i){
37347 if(i.value === v || i.value.toString() === v.toString()){
37349 i.el.addClass('checked');
37351 if(suppressEvent !== true){
37352 this.fireEvent('check', this, i);
37363 clearInvalid : function(){
37365 if(!this.el || this.preventMark){
37369 this.el.removeClass([this.invalidClass]);
37371 this.fireEvent('valid', this);
37376 Roo.apply(Roo.bootstrap.form.RadioSet, {
37380 register : function(set)
37382 this.groups[set.name] = set;
37385 get: function(name)
37387 if (typeof(this.groups[name]) == 'undefined') {
37391 return this.groups[name] ;
37397 * Ext JS Library 1.1.1
37398 * Copyright(c) 2006-2007, Ext JS, LLC.
37400 * Originally Released Under LGPL - original licence link has changed is not relivant.
37403 * <script type="text/javascript">
37408 * @class Roo.bootstrap.SplitBar
37409 * @extends Roo.util.Observable
37410 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37414 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37415 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37416 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37417 split.minSize = 100;
37418 split.maxSize = 600;
37419 split.animate = true;
37420 split.on('moved', splitterMoved);
37423 * Create a new SplitBar
37424 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37425 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37426 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37428 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37429 position of the SplitBar).
37431 Roo.bootstrap.SplitBar = function(cfg){
37436 // dragElement : elm
37437 // resizingElement: el,
37439 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37440 // placement : Roo.bootstrap.SplitBar.LEFT ,
37441 // existingProxy ???
37444 this.el = Roo.get(cfg.dragElement, true);
37445 this.el.dom.unselectable = "on";
37447 this.resizingEl = Roo.get(cfg.resizingElement, true);
37451 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37452 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37455 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37458 * The minimum size of the resizing element. (Defaults to 0)
37464 * The maximum size of the resizing element. (Defaults to 2000)
37467 this.maxSize = 2000;
37470 * Whether to animate the transition to the new size
37473 this.animate = false;
37476 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37479 this.useShim = false;
37484 if(!cfg.existingProxy){
37486 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37488 this.proxy = Roo.get(cfg.existingProxy).dom;
37491 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37494 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37497 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37500 this.dragSpecs = {};
37503 * @private The adapter to use to positon and resize elements
37505 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37506 this.adapter.init(this);
37508 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37510 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37511 this.el.addClass("roo-splitbar-h");
37514 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37515 this.el.addClass("roo-splitbar-v");
37521 * Fires when the splitter is moved (alias for {@link #event-moved})
37522 * @param {Roo.bootstrap.SplitBar} this
37523 * @param {Number} newSize the new width or height
37528 * Fires when the splitter is moved
37529 * @param {Roo.bootstrap.SplitBar} this
37530 * @param {Number} newSize the new width or height
37534 * @event beforeresize
37535 * Fires before the splitter is dragged
37536 * @param {Roo.bootstrap.SplitBar} this
37538 "beforeresize" : true,
37540 "beforeapply" : true
37543 Roo.util.Observable.call(this);
37546 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37547 onStartProxyDrag : function(x, y){
37548 this.fireEvent("beforeresize", this);
37550 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37552 o.enableDisplayMode("block");
37553 // all splitbars share the same overlay
37554 Roo.bootstrap.SplitBar.prototype.overlay = o;
37556 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37557 this.overlay.show();
37558 Roo.get(this.proxy).setDisplayed("block");
37559 var size = this.adapter.getElementSize(this);
37560 this.activeMinSize = this.getMinimumSize();;
37561 this.activeMaxSize = this.getMaximumSize();;
37562 var c1 = size - this.activeMinSize;
37563 var c2 = Math.max(this.activeMaxSize - size, 0);
37564 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37565 this.dd.resetConstraints();
37566 this.dd.setXConstraint(
37567 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37568 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37570 this.dd.setYConstraint(0, 0);
37572 this.dd.resetConstraints();
37573 this.dd.setXConstraint(0, 0);
37574 this.dd.setYConstraint(
37575 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37576 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37579 this.dragSpecs.startSize = size;
37580 this.dragSpecs.startPoint = [x, y];
37581 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37585 * @private Called after the drag operation by the DDProxy
37587 onEndProxyDrag : function(e){
37588 Roo.get(this.proxy).setDisplayed(false);
37589 var endPoint = Roo.lib.Event.getXY(e);
37591 this.overlay.hide();
37594 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37595 newSize = this.dragSpecs.startSize +
37596 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37597 endPoint[0] - this.dragSpecs.startPoint[0] :
37598 this.dragSpecs.startPoint[0] - endPoint[0]
37601 newSize = this.dragSpecs.startSize +
37602 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37603 endPoint[1] - this.dragSpecs.startPoint[1] :
37604 this.dragSpecs.startPoint[1] - endPoint[1]
37607 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37608 if(newSize != this.dragSpecs.startSize){
37609 if(this.fireEvent('beforeapply', this, newSize) !== false){
37610 this.adapter.setElementSize(this, newSize);
37611 this.fireEvent("moved", this, newSize);
37612 this.fireEvent("resize", this, newSize);
37618 * Get the adapter this SplitBar uses
37619 * @return The adapter object
37621 getAdapter : function(){
37622 return this.adapter;
37626 * Set the adapter this SplitBar uses
37627 * @param {Object} adapter A SplitBar adapter object
37629 setAdapter : function(adapter){
37630 this.adapter = adapter;
37631 this.adapter.init(this);
37635 * Gets the minimum size for the resizing element
37636 * @return {Number} The minimum size
37638 getMinimumSize : function(){
37639 return this.minSize;
37643 * Sets the minimum size for the resizing element
37644 * @param {Number} minSize The minimum size
37646 setMinimumSize : function(minSize){
37647 this.minSize = minSize;
37651 * Gets the maximum size for the resizing element
37652 * @return {Number} The maximum size
37654 getMaximumSize : function(){
37655 return this.maxSize;
37659 * Sets the maximum size for the resizing element
37660 * @param {Number} maxSize The maximum size
37662 setMaximumSize : function(maxSize){
37663 this.maxSize = maxSize;
37667 * Sets the initialize size for the resizing element
37668 * @param {Number} size The initial size
37670 setCurrentSize : function(size){
37671 var oldAnimate = this.animate;
37672 this.animate = false;
37673 this.adapter.setElementSize(this, size);
37674 this.animate = oldAnimate;
37678 * Destroy this splitbar.
37679 * @param {Boolean} removeEl True to remove the element
37681 destroy : function(removeEl){
37683 this.shim.remove();
37686 this.proxy.parentNode.removeChild(this.proxy);
37694 * @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.
37696 Roo.bootstrap.SplitBar.createProxy = function(dir){
37697 var proxy = new Roo.Element(document.createElement("div"));
37698 proxy.unselectable();
37699 var cls = 'roo-splitbar-proxy';
37700 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37701 document.body.appendChild(proxy.dom);
37706 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37707 * Default Adapter. It assumes the splitter and resizing element are not positioned
37708 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37710 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37713 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37714 // do nothing for now
37715 init : function(s){
37719 * Called before drag operations to get the current size of the resizing element.
37720 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37722 getElementSize : function(s){
37723 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37724 return s.resizingEl.getWidth();
37726 return s.resizingEl.getHeight();
37731 * Called after drag operations to set the size of the resizing element.
37732 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37733 * @param {Number} newSize The new size to set
37734 * @param {Function} onComplete A function to be invoked when resizing is complete
37736 setElementSize : function(s, newSize, onComplete){
37737 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37739 s.resizingEl.setWidth(newSize);
37741 onComplete(s, newSize);
37744 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37749 s.resizingEl.setHeight(newSize);
37751 onComplete(s, newSize);
37754 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37761 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37762 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37763 * Adapter that moves the splitter element to align with the resized sizing element.
37764 * Used with an absolute positioned SplitBar.
37765 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37766 * document.body, make sure you assign an id to the body element.
37768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37769 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37770 this.container = Roo.get(container);
37773 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37774 init : function(s){
37775 this.basic.init(s);
37778 getElementSize : function(s){
37779 return this.basic.getElementSize(s);
37782 setElementSize : function(s, newSize, onComplete){
37783 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37786 moveSplitter : function(s){
37787 var yes = Roo.bootstrap.SplitBar;
37788 switch(s.placement){
37790 s.el.setX(s.resizingEl.getRight());
37793 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37796 s.el.setY(s.resizingEl.getBottom());
37799 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37806 * Orientation constant - Create a vertical SplitBar
37810 Roo.bootstrap.SplitBar.VERTICAL = 1;
37813 * Orientation constant - Create a horizontal SplitBar
37817 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37820 * Placement constant - The resizing element is to the left of the splitter element
37824 Roo.bootstrap.SplitBar.LEFT = 1;
37827 * Placement constant - The resizing element is to the right of the splitter element
37831 Roo.bootstrap.SplitBar.RIGHT = 2;
37834 * Placement constant - The resizing element is positioned above the splitter element
37838 Roo.bootstrap.SplitBar.TOP = 3;
37841 * Placement constant - The resizing element is positioned under splitter element
37845 Roo.bootstrap.SplitBar.BOTTOM = 4;
37848 * Ext JS Library 1.1.1
37849 * Copyright(c) 2006-2007, Ext JS, LLC.
37851 * Originally Released Under LGPL - original licence link has changed is not relivant.
37854 * <script type="text/javascript">
37858 * @class Roo.bootstrap.layout.Manager
37859 * @extends Roo.bootstrap.Component
37861 * Base class for layout managers.
37863 Roo.bootstrap.layout.Manager = function(config)
37865 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37871 /** false to disable window resize monitoring @type Boolean */
37872 this.monitorWindowResize = true;
37877 * Fires when a layout is performed.
37878 * @param {Roo.LayoutManager} this
37882 * @event regionresized
37883 * Fires when the user resizes a region.
37884 * @param {Roo.LayoutRegion} region The resized region
37885 * @param {Number} newSize The new size (width for east/west, height for north/south)
37887 "regionresized" : true,
37889 * @event regioncollapsed
37890 * Fires when a region is collapsed.
37891 * @param {Roo.LayoutRegion} region The collapsed region
37893 "regioncollapsed" : true,
37895 * @event regionexpanded
37896 * Fires when a region is expanded.
37897 * @param {Roo.LayoutRegion} region The expanded region
37899 "regionexpanded" : true
37901 this.updating = false;
37904 this.el = Roo.get(config.el);
37910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37915 monitorWindowResize : true,
37921 onRender : function(ct, position)
37924 this.el = Roo.get(ct);
37927 //this.fireEvent('render',this);
37931 initEvents: function()
37935 // ie scrollbar fix
37936 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37937 document.body.scroll = "no";
37938 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37939 this.el.position('relative');
37941 this.id = this.el.id;
37942 this.el.addClass("roo-layout-container");
37943 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37944 if(this.el.dom != document.body ) {
37945 this.el.on('resize', this.layout,this);
37946 this.el.on('show', this.layout,this);
37952 * Returns true if this layout is currently being updated
37953 * @return {Boolean}
37955 isUpdating : function(){
37956 return this.updating;
37960 * Suspend the LayoutManager from doing auto-layouts while
37961 * making multiple add or remove calls
37963 beginUpdate : function(){
37964 this.updating = true;
37968 * Restore auto-layouts and optionally disable the manager from performing a layout
37969 * @param {Boolean} noLayout true to disable a layout update
37971 endUpdate : function(noLayout){
37972 this.updating = false;
37978 layout: function(){
37982 onRegionResized : function(region, newSize){
37983 this.fireEvent("regionresized", region, newSize);
37987 onRegionCollapsed : function(region){
37988 this.fireEvent("regioncollapsed", region);
37991 onRegionExpanded : function(region){
37992 this.fireEvent("regionexpanded", region);
37996 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37997 * performs box-model adjustments.
37998 * @return {Object} The size as an object {width: (the width), height: (the height)}
38000 getViewSize : function()
38003 if(this.el.dom != document.body){
38004 size = this.el.getSize();
38006 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38008 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38009 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38014 * Returns the Element this layout is bound to.
38015 * @return {Roo.Element}
38017 getEl : function(){
38022 * Returns the specified region.
38023 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38024 * @return {Roo.LayoutRegion}
38026 getRegion : function(target){
38027 return this.regions[target.toLowerCase()];
38030 onWindowResize : function(){
38031 if(this.monitorWindowResize){
38038 * Ext JS Library 1.1.1
38039 * Copyright(c) 2006-2007, Ext JS, LLC.
38041 * Originally Released Under LGPL - original licence link has changed is not relivant.
38044 * <script type="text/javascript">
38047 * @class Roo.bootstrap.layout.Border
38048 * @extends Roo.bootstrap.layout.Manager
38049 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38050 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38051 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38052 * please see: examples/bootstrap/nested.html<br><br>
38054 <b>The container the layout is rendered into can be either the body element or any other element.
38055 If it is not the body element, the container needs to either be an absolute positioned element,
38056 or you will need to add "position:relative" to the css of the container. You will also need to specify
38057 the container size if it is not the body element.</b>
38060 * Create a new Border
38061 * @param {Object} config Configuration options
38063 Roo.bootstrap.layout.Border = function(config){
38064 config = config || {};
38065 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38069 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38070 if(config[region]){
38071 config[region].region = region;
38072 this.addRegion(config[region]);
38078 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38080 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38083 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38086 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38089 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38092 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38095 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38101 parent : false, // this might point to a 'nest' or a ???
38104 * Creates and adds a new region if it doesn't already exist.
38105 * @param {String} target The target region key (north, south, east, west or center).
38106 * @param {Object} config The regions config object
38107 * @return {BorderLayoutRegion} The new region
38109 addRegion : function(config)
38111 if(!this.regions[config.region]){
38112 var r = this.factory(config);
38113 this.bindRegion(r);
38115 return this.regions[config.region];
38119 bindRegion : function(r){
38120 this.regions[r.config.region] = r;
38122 r.on("visibilitychange", this.layout, this);
38123 r.on("paneladded", this.layout, this);
38124 r.on("panelremoved", this.layout, this);
38125 r.on("invalidated", this.layout, this);
38126 r.on("resized", this.onRegionResized, this);
38127 r.on("collapsed", this.onRegionCollapsed, this);
38128 r.on("expanded", this.onRegionExpanded, this);
38132 * Performs a layout update.
38134 layout : function()
38136 if(this.updating) {
38140 // render all the rebions if they have not been done alreayd?
38141 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38142 if(this.regions[region] && !this.regions[region].bodyEl){
38143 this.regions[region].onRender(this.el)
38147 var size = this.getViewSize();
38148 var w = size.width;
38149 var h = size.height;
38154 //var x = 0, y = 0;
38156 var rs = this.regions;
38157 var north = rs["north"];
38158 var south = rs["south"];
38159 var west = rs["west"];
38160 var east = rs["east"];
38161 var center = rs["center"];
38162 //if(this.hideOnLayout){ // not supported anymore
38163 //c.el.setStyle("display", "none");
38165 if(north && north.isVisible()){
38166 var b = north.getBox();
38167 var m = north.getMargins();
38168 b.width = w - (m.left+m.right);
38171 centerY = b.height + b.y + m.bottom;
38172 centerH -= centerY;
38173 north.updateBox(this.safeBox(b));
38175 if(south && south.isVisible()){
38176 var b = south.getBox();
38177 var m = south.getMargins();
38178 b.width = w - (m.left+m.right);
38180 var totalHeight = (b.height + m.top + m.bottom);
38181 b.y = h - totalHeight + m.top;
38182 centerH -= totalHeight;
38183 south.updateBox(this.safeBox(b));
38185 if(west && west.isVisible()){
38186 var b = west.getBox();
38187 var m = west.getMargins();
38188 b.height = centerH - (m.top+m.bottom);
38190 b.y = centerY + m.top;
38191 var totalWidth = (b.width + m.left + m.right);
38192 centerX += totalWidth;
38193 centerW -= totalWidth;
38194 west.updateBox(this.safeBox(b));
38196 if(east && east.isVisible()){
38197 var b = east.getBox();
38198 var m = east.getMargins();
38199 b.height = centerH - (m.top+m.bottom);
38200 var totalWidth = (b.width + m.left + m.right);
38201 b.x = w - totalWidth + m.left;
38202 b.y = centerY + m.top;
38203 centerW -= totalWidth;
38204 east.updateBox(this.safeBox(b));
38207 var m = center.getMargins();
38209 x: centerX + m.left,
38210 y: centerY + m.top,
38211 width: centerW - (m.left+m.right),
38212 height: centerH - (m.top+m.bottom)
38214 //if(this.hideOnLayout){
38215 //center.el.setStyle("display", "block");
38217 center.updateBox(this.safeBox(centerBox));
38220 this.fireEvent("layout", this);
38224 safeBox : function(box){
38225 box.width = Math.max(0, box.width);
38226 box.height = Math.max(0, box.height);
38231 * Adds a ContentPanel (or subclass) to this layout.
38232 * @param {String} target The target region key (north, south, east, west or center).
38233 * @param {Roo.ContentPanel} panel The panel to add
38234 * @return {Roo.ContentPanel} The added panel
38236 add : function(target, panel){
38238 target = target.toLowerCase();
38239 return this.regions[target].add(panel);
38243 * Remove a ContentPanel (or subclass) to this layout.
38244 * @param {String} target The target region key (north, south, east, west or center).
38245 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38246 * @return {Roo.ContentPanel} The removed panel
38248 remove : function(target, panel){
38249 target = target.toLowerCase();
38250 return this.regions[target].remove(panel);
38254 * Searches all regions for a panel with the specified id
38255 * @param {String} panelId
38256 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38258 findPanel : function(panelId){
38259 var rs = this.regions;
38260 for(var target in rs){
38261 if(typeof rs[target] != "function"){
38262 var p = rs[target].getPanel(panelId);
38272 * Searches all regions for a panel with the specified id and activates (shows) it.
38273 * @param {String/ContentPanel} panelId The panels id or the panel itself
38274 * @return {Roo.ContentPanel} The shown panel or null
38276 showPanel : function(panelId) {
38277 var rs = this.regions;
38278 for(var target in rs){
38279 var r = rs[target];
38280 if(typeof r != "function"){
38281 if(r.hasPanel(panelId)){
38282 return r.showPanel(panelId);
38290 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38291 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38294 restoreState : function(provider){
38296 provider = Roo.state.Manager;
38298 var sm = new Roo.LayoutStateManager();
38299 sm.init(this, provider);
38305 * Adds a xtype elements to the layout.
38309 xtype : 'ContentPanel',
38316 xtype : 'NestedLayoutPanel',
38322 items : [ ... list of content panels or nested layout panels.. ]
38326 * @param {Object} cfg Xtype definition of item to add.
38328 addxtype : function(cfg)
38330 // basically accepts a pannel...
38331 // can accept a layout region..!?!?
38332 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38335 // theory? children can only be panels??
38337 //if (!cfg.xtype.match(/Panel$/)) {
38342 if (typeof(cfg.region) == 'undefined') {
38343 Roo.log("Failed to add Panel, region was not set");
38347 var region = cfg.region;
38353 xitems = cfg.items;
38358 if ( region == 'center') {
38359 Roo.log("Center: " + cfg.title);
38365 case 'Content': // ContentPanel (el, cfg)
38366 case 'Scroll': // ContentPanel (el, cfg)
38368 cfg.autoCreate = cfg.autoCreate || true;
38369 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38371 // var el = this.el.createChild();
38372 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38375 this.add(region, ret);
38379 case 'TreePanel': // our new panel!
38380 cfg.el = this.el.createChild();
38381 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38382 this.add(region, ret);
38387 // create a new Layout (which is a Border Layout...
38389 var clayout = cfg.layout;
38390 clayout.el = this.el.createChild();
38391 clayout.items = clayout.items || [];
38395 // replace this exitems with the clayout ones..
38396 xitems = clayout.items;
38398 // force background off if it's in center...
38399 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38400 cfg.background = false;
38402 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38405 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38406 //console.log('adding nested layout panel ' + cfg.toSource());
38407 this.add(region, ret);
38408 nb = {}; /// find first...
38413 // needs grid and region
38415 //var el = this.getRegion(region).el.createChild();
38417 *var el = this.el.createChild();
38418 // create the grid first...
38419 cfg.grid.container = el;
38420 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38423 if (region == 'center' && this.active ) {
38424 cfg.background = false;
38427 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38429 this.add(region, ret);
38431 if (cfg.background) {
38432 // render grid on panel activation (if panel background)
38433 ret.on('activate', function(gp) {
38434 if (!gp.grid.rendered) {
38435 // gp.grid.render(el);
38439 // cfg.grid.render(el);
38445 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38446 // it was the old xcomponent building that caused this before.
38447 // espeically if border is the top element in the tree.
38457 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38459 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38460 this.add(region, ret);
38464 throw "Can not add '" + cfg.xtype + "' to Border";
38470 this.beginUpdate();
38474 Roo.each(xitems, function(i) {
38475 region = nb && i.region ? i.region : false;
38477 var add = ret.addxtype(i);
38480 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38481 if (!i.background) {
38482 abn[region] = nb[region] ;
38489 // make the last non-background panel active..
38490 //if (nb) { Roo.log(abn); }
38493 for(var r in abn) {
38494 region = this.getRegion(r);
38496 // tried using nb[r], but it does not work..
38498 region.showPanel(abn[r]);
38509 factory : function(cfg)
38512 var validRegions = Roo.bootstrap.layout.Border.regions;
38514 var target = cfg.region;
38517 var r = Roo.bootstrap.layout;
38521 return new r.North(cfg);
38523 return new r.South(cfg);
38525 return new r.East(cfg);
38527 return new r.West(cfg);
38529 return new r.Center(cfg);
38531 throw 'Layout region "'+target+'" not supported.';
38538 * Ext JS Library 1.1.1
38539 * Copyright(c) 2006-2007, Ext JS, LLC.
38541 * Originally Released Under LGPL - original licence link has changed is not relivant.
38544 * <script type="text/javascript">
38548 * @class Roo.bootstrap.layout.Basic
38549 * @extends Roo.util.Observable
38550 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38551 * and does not have a titlebar, tabs or any other features. All it does is size and position
38552 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38553 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38554 * @cfg {string} region the region that it inhabits..
38555 * @cfg {bool} skipConfig skip config?
38559 Roo.bootstrap.layout.Basic = function(config){
38561 this.mgr = config.mgr;
38563 this.position = config.region;
38565 var skipConfig = config.skipConfig;
38569 * @scope Roo.BasicLayoutRegion
38573 * @event beforeremove
38574 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38575 * @param {Roo.LayoutRegion} this
38576 * @param {Roo.ContentPanel} panel The panel
38577 * @param {Object} e The cancel event object
38579 "beforeremove" : true,
38581 * @event invalidated
38582 * Fires when the layout for this region is changed.
38583 * @param {Roo.LayoutRegion} this
38585 "invalidated" : true,
38587 * @event visibilitychange
38588 * Fires when this region is shown or hidden
38589 * @param {Roo.LayoutRegion} this
38590 * @param {Boolean} visibility true or false
38592 "visibilitychange" : true,
38594 * @event paneladded
38595 * Fires when a panel is added.
38596 * @param {Roo.LayoutRegion} this
38597 * @param {Roo.ContentPanel} panel The panel
38599 "paneladded" : true,
38601 * @event panelremoved
38602 * Fires when a panel is removed.
38603 * @param {Roo.LayoutRegion} this
38604 * @param {Roo.ContentPanel} panel The panel
38606 "panelremoved" : true,
38608 * @event beforecollapse
38609 * Fires when this region before collapse.
38610 * @param {Roo.LayoutRegion} this
38612 "beforecollapse" : true,
38615 * Fires when this region is collapsed.
38616 * @param {Roo.LayoutRegion} this
38618 "collapsed" : true,
38621 * Fires when this region is expanded.
38622 * @param {Roo.LayoutRegion} this
38627 * Fires when this region is slid into view.
38628 * @param {Roo.LayoutRegion} this
38630 "slideshow" : true,
38633 * Fires when this region slides out of view.
38634 * @param {Roo.LayoutRegion} this
38636 "slidehide" : true,
38638 * @event panelactivated
38639 * Fires when a panel is activated.
38640 * @param {Roo.LayoutRegion} this
38641 * @param {Roo.ContentPanel} panel The activated panel
38643 "panelactivated" : true,
38646 * Fires when the user resizes this region.
38647 * @param {Roo.LayoutRegion} this
38648 * @param {Number} newSize The new size (width for east/west, height for north/south)
38652 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38653 this.panels = new Roo.util.MixedCollection();
38654 this.panels.getKey = this.getPanelId.createDelegate(this);
38656 this.activePanel = null;
38657 // ensure listeners are added...
38659 if (config.listeners || config.events) {
38660 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38661 listeners : config.listeners || {},
38662 events : config.events || {}
38666 if(skipConfig !== true){
38667 this.applyConfig(config);
38671 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38673 getPanelId : function(p){
38677 applyConfig : function(config){
38678 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38679 this.config = config;
38684 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38685 * the width, for horizontal (north, south) the height.
38686 * @param {Number} newSize The new width or height
38688 resizeTo : function(newSize){
38689 var el = this.el ? this.el :
38690 (this.activePanel ? this.activePanel.getEl() : null);
38692 switch(this.position){
38695 el.setWidth(newSize);
38696 this.fireEvent("resized", this, newSize);
38700 el.setHeight(newSize);
38701 this.fireEvent("resized", this, newSize);
38707 getBox : function(){
38708 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38711 getMargins : function(){
38712 return this.margins;
38715 updateBox : function(box){
38717 var el = this.activePanel.getEl();
38718 el.dom.style.left = box.x + "px";
38719 el.dom.style.top = box.y + "px";
38720 this.activePanel.setSize(box.width, box.height);
38724 * Returns the container element for this region.
38725 * @return {Roo.Element}
38727 getEl : function(){
38728 return this.activePanel;
38732 * Returns true if this region is currently visible.
38733 * @return {Boolean}
38735 isVisible : function(){
38736 return this.activePanel ? true : false;
38739 setActivePanel : function(panel){
38740 panel = this.getPanel(panel);
38741 if(this.activePanel && this.activePanel != panel){
38742 this.activePanel.setActiveState(false);
38743 this.activePanel.getEl().setLeftTop(-10000,-10000);
38745 this.activePanel = panel;
38746 panel.setActiveState(true);
38748 panel.setSize(this.box.width, this.box.height);
38750 this.fireEvent("panelactivated", this, panel);
38751 this.fireEvent("invalidated");
38755 * Show the specified panel.
38756 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38757 * @return {Roo.ContentPanel} The shown panel or null
38759 showPanel : function(panel){
38760 panel = this.getPanel(panel);
38762 this.setActivePanel(panel);
38768 * Get the active panel for this region.
38769 * @return {Roo.ContentPanel} The active panel or null
38771 getActivePanel : function(){
38772 return this.activePanel;
38776 * Add the passed ContentPanel(s)
38777 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38778 * @return {Roo.ContentPanel} The panel added (if only one was added)
38780 add : function(panel){
38781 if(arguments.length > 1){
38782 for(var i = 0, len = arguments.length; i < len; i++) {
38783 this.add(arguments[i]);
38787 if(this.hasPanel(panel)){
38788 this.showPanel(panel);
38791 var el = panel.getEl();
38792 if(el.dom.parentNode != this.mgr.el.dom){
38793 this.mgr.el.dom.appendChild(el.dom);
38795 if(panel.setRegion){
38796 panel.setRegion(this);
38798 this.panels.add(panel);
38799 el.setStyle("position", "absolute");
38800 if(!panel.background){
38801 this.setActivePanel(panel);
38802 if(this.config.initialSize && this.panels.getCount()==1){
38803 this.resizeTo(this.config.initialSize);
38806 this.fireEvent("paneladded", this, panel);
38811 * Returns true if the panel is in this region.
38812 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38813 * @return {Boolean}
38815 hasPanel : function(panel){
38816 if(typeof panel == "object"){ // must be panel obj
38817 panel = panel.getId();
38819 return this.getPanel(panel) ? true : false;
38823 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38824 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38825 * @param {Boolean} preservePanel Overrides the config preservePanel option
38826 * @return {Roo.ContentPanel} The panel that was removed
38828 remove : function(panel, preservePanel){
38829 panel = this.getPanel(panel);
38834 this.fireEvent("beforeremove", this, panel, e);
38835 if(e.cancel === true){
38838 var panelId = panel.getId();
38839 this.panels.removeKey(panelId);
38844 * Returns the panel specified or null if it's not in this region.
38845 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38846 * @return {Roo.ContentPanel}
38848 getPanel : function(id){
38849 if(typeof id == "object"){ // must be panel obj
38852 return this.panels.get(id);
38856 * Returns this regions position (north/south/east/west/center).
38859 getPosition: function(){
38860 return this.position;
38864 * Ext JS Library 1.1.1
38865 * Copyright(c) 2006-2007, Ext JS, LLC.
38867 * Originally Released Under LGPL - original licence link has changed is not relivant.
38870 * <script type="text/javascript">
38874 * @class Roo.bootstrap.layout.Region
38875 * @extends Roo.bootstrap.layout.Basic
38876 * This class represents a region in a layout manager.
38878 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38879 * @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})
38880 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38881 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38882 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38883 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38884 * @cfg {String} title The title for the region (overrides panel titles)
38885 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38886 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38887 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38888 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38889 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38890 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38891 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38892 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38893 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38894 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38896 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38897 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38898 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38899 * @cfg {Number} width For East/West panels
38900 * @cfg {Number} height For North/South panels
38901 * @cfg {Boolean} split To show the splitter
38902 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38904 * @cfg {string} cls Extra CSS classes to add to region
38906 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38907 * @cfg {string} region the region that it inhabits..
38910 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38911 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38913 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38914 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38915 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38917 Roo.bootstrap.layout.Region = function(config)
38919 this.applyConfig(config);
38921 var mgr = config.mgr;
38922 var pos = config.region;
38923 config.skipConfig = true;
38924 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38927 this.onRender(mgr.el);
38930 this.visible = true;
38931 this.collapsed = false;
38932 this.unrendered_panels = [];
38935 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38937 position: '', // set by wrapper (eg. north/south etc..)
38938 unrendered_panels : null, // unrendered panels.
38940 tabPosition : false,
38942 mgr: false, // points to 'Border'
38945 createBody : function(){
38946 /** This region's body element
38947 * @type Roo.Element */
38948 this.bodyEl = this.el.createChild({
38950 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38954 onRender: function(ctr, pos)
38956 var dh = Roo.DomHelper;
38957 /** This region's container element
38958 * @type Roo.Element */
38959 this.el = dh.append(ctr.dom, {
38961 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38963 /** This region's title element
38964 * @type Roo.Element */
38966 this.titleEl = dh.append(this.el.dom, {
38968 unselectable: "on",
38969 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38971 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38972 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38976 this.titleEl.enableDisplayMode();
38977 /** This region's title text element
38978 * @type HTMLElement */
38979 this.titleTextEl = this.titleEl.dom.firstChild;
38980 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38982 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38983 this.closeBtn.enableDisplayMode();
38984 this.closeBtn.on("click", this.closeClicked, this);
38985 this.closeBtn.hide();
38987 this.createBody(this.config);
38988 if(this.config.hideWhenEmpty){
38990 this.on("paneladded", this.validateVisibility, this);
38991 this.on("panelremoved", this.validateVisibility, this);
38993 if(this.autoScroll){
38994 this.bodyEl.setStyle("overflow", "auto");
38996 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38998 //if(c.titlebar !== false){
38999 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39000 this.titleEl.hide();
39002 this.titleEl.show();
39003 if(this.config.title){
39004 this.titleTextEl.innerHTML = this.config.title;
39008 if(this.config.collapsed){
39009 this.collapse(true);
39011 if(this.config.hidden){
39015 if (this.unrendered_panels && this.unrendered_panels.length) {
39016 for (var i =0;i< this.unrendered_panels.length; i++) {
39017 this.add(this.unrendered_panels[i]);
39019 this.unrendered_panels = null;
39025 applyConfig : function(c)
39028 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39029 var dh = Roo.DomHelper;
39030 if(c.titlebar !== false){
39031 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39032 this.collapseBtn.on("click", this.collapse, this);
39033 this.collapseBtn.enableDisplayMode();
39035 if(c.showPin === true || this.showPin){
39036 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39037 this.stickBtn.enableDisplayMode();
39038 this.stickBtn.on("click", this.expand, this);
39039 this.stickBtn.hide();
39044 /** This region's collapsed element
39045 * @type Roo.Element */
39048 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39049 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39052 if(c.floatable !== false){
39053 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39054 this.collapsedEl.on("click", this.collapseClick, this);
39057 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39058 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39059 id: "message", unselectable: "on", style:{"float":"left"}});
39060 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39062 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39063 this.expandBtn.on("click", this.expand, this);
39067 if(this.collapseBtn){
39068 this.collapseBtn.setVisible(c.collapsible == true);
39071 this.cmargins = c.cmargins || this.cmargins ||
39072 (this.position == "west" || this.position == "east" ?
39073 {top: 0, left: 2, right:2, bottom: 0} :
39074 {top: 2, left: 0, right:0, bottom: 2});
39076 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39079 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39081 this.autoScroll = c.autoScroll || false;
39086 this.duration = c.duration || .30;
39087 this.slideDuration = c.slideDuration || .45;
39092 * Returns true if this region is currently visible.
39093 * @return {Boolean}
39095 isVisible : function(){
39096 return this.visible;
39100 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39101 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39103 //setCollapsedTitle : function(title){
39104 // title = title || " ";
39105 // if(this.collapsedTitleTextEl){
39106 // this.collapsedTitleTextEl.innerHTML = title;
39110 getBox : function(){
39112 // if(!this.collapsed){
39113 b = this.el.getBox(false, true);
39115 // b = this.collapsedEl.getBox(false, true);
39120 getMargins : function(){
39121 return this.margins;
39122 //return this.collapsed ? this.cmargins : this.margins;
39125 highlight : function(){
39126 this.el.addClass("x-layout-panel-dragover");
39129 unhighlight : function(){
39130 this.el.removeClass("x-layout-panel-dragover");
39133 updateBox : function(box)
39135 if (!this.bodyEl) {
39136 return; // not rendered yet..
39140 if(!this.collapsed){
39141 this.el.dom.style.left = box.x + "px";
39142 this.el.dom.style.top = box.y + "px";
39143 this.updateBody(box.width, box.height);
39145 this.collapsedEl.dom.style.left = box.x + "px";
39146 this.collapsedEl.dom.style.top = box.y + "px";
39147 this.collapsedEl.setSize(box.width, box.height);
39150 this.tabs.autoSizeTabs();
39154 updateBody : function(w, h)
39157 this.el.setWidth(w);
39158 w -= this.el.getBorderWidth("rl");
39159 if(this.config.adjustments){
39160 w += this.config.adjustments[0];
39163 if(h !== null && h > 0){
39164 this.el.setHeight(h);
39165 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39166 h -= this.el.getBorderWidth("tb");
39167 if(this.config.adjustments){
39168 h += this.config.adjustments[1];
39170 this.bodyEl.setHeight(h);
39172 h = this.tabs.syncHeight(h);
39175 if(this.panelSize){
39176 w = w !== null ? w : this.panelSize.width;
39177 h = h !== null ? h : this.panelSize.height;
39179 if(this.activePanel){
39180 var el = this.activePanel.getEl();
39181 w = w !== null ? w : el.getWidth();
39182 h = h !== null ? h : el.getHeight();
39183 this.panelSize = {width: w, height: h};
39184 this.activePanel.setSize(w, h);
39186 if(Roo.isIE && this.tabs){
39187 this.tabs.el.repaint();
39192 * Returns the container element for this region.
39193 * @return {Roo.Element}
39195 getEl : function(){
39200 * Hides this region.
39203 //if(!this.collapsed){
39204 this.el.dom.style.left = "-2000px";
39207 // this.collapsedEl.dom.style.left = "-2000px";
39208 // this.collapsedEl.hide();
39210 this.visible = false;
39211 this.fireEvent("visibilitychange", this, false);
39215 * Shows this region if it was previously hidden.
39218 //if(!this.collapsed){
39221 // this.collapsedEl.show();
39223 this.visible = true;
39224 this.fireEvent("visibilitychange", this, true);
39227 closeClicked : function(){
39228 if(this.activePanel){
39229 this.remove(this.activePanel);
39233 collapseClick : function(e){
39235 e.stopPropagation();
39238 e.stopPropagation();
39244 * Collapses this region.
39245 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39248 collapse : function(skipAnim, skipCheck = false){
39249 if(this.collapsed) {
39253 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39255 this.collapsed = true;
39257 this.split.el.hide();
39259 if(this.config.animate && skipAnim !== true){
39260 this.fireEvent("invalidated", this);
39261 this.animateCollapse();
39263 this.el.setLocation(-20000,-20000);
39265 this.collapsedEl.show();
39266 this.fireEvent("collapsed", this);
39267 this.fireEvent("invalidated", this);
39273 animateCollapse : function(){
39278 * Expands this region if it was previously collapsed.
39279 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39280 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39283 expand : function(e, skipAnim){
39285 e.stopPropagation();
39287 if(!this.collapsed || this.el.hasActiveFx()) {
39291 this.afterSlideIn();
39294 this.collapsed = false;
39295 if(this.config.animate && skipAnim !== true){
39296 this.animateExpand();
39300 this.split.el.show();
39302 this.collapsedEl.setLocation(-2000,-2000);
39303 this.collapsedEl.hide();
39304 this.fireEvent("invalidated", this);
39305 this.fireEvent("expanded", this);
39309 animateExpand : function(){
39313 initTabs : function()
39315 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39317 var ts = new Roo.bootstrap.panel.Tabs({
39318 el: this.bodyEl.dom,
39320 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39321 disableTooltips: this.config.disableTabTips,
39322 toolbar : this.config.toolbar
39325 if(this.config.hideTabs){
39326 ts.stripWrap.setDisplayed(false);
39329 ts.resizeTabs = this.config.resizeTabs === true;
39330 ts.minTabWidth = this.config.minTabWidth || 40;
39331 ts.maxTabWidth = this.config.maxTabWidth || 250;
39332 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39333 ts.monitorResize = false;
39334 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39335 ts.bodyEl.addClass('roo-layout-tabs-body');
39336 this.panels.each(this.initPanelAsTab, this);
39339 initPanelAsTab : function(panel){
39340 var ti = this.tabs.addTab(
39344 this.config.closeOnTab && panel.isClosable(),
39347 if(panel.tabTip !== undefined){
39348 ti.setTooltip(panel.tabTip);
39350 ti.on("activate", function(){
39351 this.setActivePanel(panel);
39354 if(this.config.closeOnTab){
39355 ti.on("beforeclose", function(t, e){
39357 this.remove(panel);
39361 panel.tabItem = ti;
39366 updatePanelTitle : function(panel, title)
39368 if(this.activePanel == panel){
39369 this.updateTitle(title);
39372 var ti = this.tabs.getTab(panel.getEl().id);
39374 if(panel.tabTip !== undefined){
39375 ti.setTooltip(panel.tabTip);
39380 updateTitle : function(title){
39381 if(this.titleTextEl && !this.config.title){
39382 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39386 setActivePanel : function(panel)
39388 panel = this.getPanel(panel);
39389 if(this.activePanel && this.activePanel != panel){
39390 if(this.activePanel.setActiveState(false) === false){
39394 this.activePanel = panel;
39395 panel.setActiveState(true);
39396 if(this.panelSize){
39397 panel.setSize(this.panelSize.width, this.panelSize.height);
39400 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39402 this.updateTitle(panel.getTitle());
39404 this.fireEvent("invalidated", this);
39406 this.fireEvent("panelactivated", this, panel);
39410 * Shows the specified panel.
39411 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39412 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39414 showPanel : function(panel)
39416 panel = this.getPanel(panel);
39419 var tab = this.tabs.getTab(panel.getEl().id);
39420 if(tab.isHidden()){
39421 this.tabs.unhideTab(tab.id);
39425 this.setActivePanel(panel);
39432 * Get the active panel for this region.
39433 * @return {Roo.ContentPanel} The active panel or null
39435 getActivePanel : function(){
39436 return this.activePanel;
39439 validateVisibility : function(){
39440 if(this.panels.getCount() < 1){
39441 this.updateTitle(" ");
39442 this.closeBtn.hide();
39445 if(!this.isVisible()){
39452 * Adds the passed ContentPanel(s) to this region.
39453 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39454 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39456 add : function(panel)
39458 if(arguments.length > 1){
39459 for(var i = 0, len = arguments.length; i < len; i++) {
39460 this.add(arguments[i]);
39465 // if we have not been rendered yet, then we can not really do much of this..
39466 if (!this.bodyEl) {
39467 this.unrendered_panels.push(panel);
39474 if(this.hasPanel(panel)){
39475 this.showPanel(panel);
39478 panel.setRegion(this);
39479 this.panels.add(panel);
39480 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39481 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39482 // and hide them... ???
39483 this.bodyEl.dom.appendChild(panel.getEl().dom);
39484 if(panel.background !== true){
39485 this.setActivePanel(panel);
39487 this.fireEvent("paneladded", this, panel);
39494 this.initPanelAsTab(panel);
39498 if(panel.background !== true){
39499 this.tabs.activate(panel.getEl().id);
39501 this.fireEvent("paneladded", this, panel);
39506 * Hides the tab for the specified panel.
39507 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39509 hidePanel : function(panel){
39510 if(this.tabs && (panel = this.getPanel(panel))){
39511 this.tabs.hideTab(panel.getEl().id);
39516 * Unhides the tab for a previously hidden panel.
39517 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39519 unhidePanel : function(panel){
39520 if(this.tabs && (panel = this.getPanel(panel))){
39521 this.tabs.unhideTab(panel.getEl().id);
39525 clearPanels : function(){
39526 while(this.panels.getCount() > 0){
39527 this.remove(this.panels.first());
39532 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39533 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39534 * @param {Boolean} preservePanel Overrides the config preservePanel option
39535 * @return {Roo.ContentPanel} The panel that was removed
39537 remove : function(panel, preservePanel)
39539 panel = this.getPanel(panel);
39544 this.fireEvent("beforeremove", this, panel, e);
39545 if(e.cancel === true){
39548 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39549 var panelId = panel.getId();
39550 this.panels.removeKey(panelId);
39552 document.body.appendChild(panel.getEl().dom);
39555 this.tabs.removeTab(panel.getEl().id);
39556 }else if (!preservePanel){
39557 this.bodyEl.dom.removeChild(panel.getEl().dom);
39559 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39560 var p = this.panels.first();
39561 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39562 tempEl.appendChild(p.getEl().dom);
39563 this.bodyEl.update("");
39564 this.bodyEl.dom.appendChild(p.getEl().dom);
39566 this.updateTitle(p.getTitle());
39568 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39569 this.setActivePanel(p);
39571 panel.setRegion(null);
39572 if(this.activePanel == panel){
39573 this.activePanel = null;
39575 if(this.config.autoDestroy !== false && preservePanel !== true){
39576 try{panel.destroy();}catch(e){}
39578 this.fireEvent("panelremoved", this, panel);
39583 * Returns the TabPanel component used by this region
39584 * @return {Roo.TabPanel}
39586 getTabs : function(){
39590 createTool : function(parentEl, className){
39591 var btn = Roo.DomHelper.append(parentEl, {
39593 cls: "x-layout-tools-button",
39596 cls: "roo-layout-tools-button-inner " + className,
39600 btn.addClassOnOver("roo-layout-tools-button-over");
39605 * Ext JS Library 1.1.1
39606 * Copyright(c) 2006-2007, Ext JS, LLC.
39608 * Originally Released Under LGPL - original licence link has changed is not relivant.
39611 * <script type="text/javascript">
39617 * @class Roo.SplitLayoutRegion
39618 * @extends Roo.LayoutRegion
39619 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39621 Roo.bootstrap.layout.Split = function(config){
39622 this.cursor = config.cursor;
39623 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39626 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39628 splitTip : "Drag to resize.",
39629 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39630 useSplitTips : false,
39632 applyConfig : function(config){
39633 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39636 onRender : function(ctr,pos) {
39638 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39639 if(!this.config.split){
39644 var splitEl = Roo.DomHelper.append(ctr.dom, {
39646 id: this.el.id + "-split",
39647 cls: "roo-layout-split roo-layout-split-"+this.position,
39650 /** The SplitBar for this region
39651 * @type Roo.SplitBar */
39652 // does not exist yet...
39653 Roo.log([this.position, this.orientation]);
39655 this.split = new Roo.bootstrap.SplitBar({
39656 dragElement : splitEl,
39657 resizingElement: this.el,
39658 orientation : this.orientation
39661 this.split.on("moved", this.onSplitMove, this);
39662 this.split.useShim = this.config.useShim === true;
39663 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39664 if(this.useSplitTips){
39665 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39667 //if(config.collapsible){
39668 // this.split.el.on("dblclick", this.collapse, this);
39671 if(typeof this.config.minSize != "undefined"){
39672 this.split.minSize = this.config.minSize;
39674 if(typeof this.config.maxSize != "undefined"){
39675 this.split.maxSize = this.config.maxSize;
39677 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39678 this.hideSplitter();
39683 getHMaxSize : function(){
39684 var cmax = this.config.maxSize || 10000;
39685 var center = this.mgr.getRegion("center");
39686 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39689 getVMaxSize : function(){
39690 var cmax = this.config.maxSize || 10000;
39691 var center = this.mgr.getRegion("center");
39692 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39695 onSplitMove : function(split, newSize){
39696 this.fireEvent("resized", this, newSize);
39700 * Returns the {@link Roo.SplitBar} for this region.
39701 * @return {Roo.SplitBar}
39703 getSplitBar : function(){
39708 this.hideSplitter();
39709 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39712 hideSplitter : function(){
39714 this.split.el.setLocation(-2000,-2000);
39715 this.split.el.hide();
39721 this.split.el.show();
39723 Roo.bootstrap.layout.Split.superclass.show.call(this);
39726 beforeSlide: function(){
39727 if(Roo.isGecko){// firefox overflow auto bug workaround
39728 this.bodyEl.clip();
39730 this.tabs.bodyEl.clip();
39732 if(this.activePanel){
39733 this.activePanel.getEl().clip();
39735 if(this.activePanel.beforeSlide){
39736 this.activePanel.beforeSlide();
39742 afterSlide : function(){
39743 if(Roo.isGecko){// firefox overflow auto bug workaround
39744 this.bodyEl.unclip();
39746 this.tabs.bodyEl.unclip();
39748 if(this.activePanel){
39749 this.activePanel.getEl().unclip();
39750 if(this.activePanel.afterSlide){
39751 this.activePanel.afterSlide();
39757 initAutoHide : function(){
39758 if(this.autoHide !== false){
39759 if(!this.autoHideHd){
39760 var st = new Roo.util.DelayedTask(this.slideIn, this);
39761 this.autoHideHd = {
39762 "mouseout": function(e){
39763 if(!e.within(this.el, true)){
39767 "mouseover" : function(e){
39773 this.el.on(this.autoHideHd);
39777 clearAutoHide : function(){
39778 if(this.autoHide !== false){
39779 this.el.un("mouseout", this.autoHideHd.mouseout);
39780 this.el.un("mouseover", this.autoHideHd.mouseover);
39784 clearMonitor : function(){
39785 Roo.get(document).un("click", this.slideInIf, this);
39788 // these names are backwards but not changed for compat
39789 slideOut : function(){
39790 if(this.isSlid || this.el.hasActiveFx()){
39793 this.isSlid = true;
39794 if(this.collapseBtn){
39795 this.collapseBtn.hide();
39797 this.closeBtnState = this.closeBtn.getStyle('display');
39798 this.closeBtn.hide();
39800 this.stickBtn.show();
39803 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39804 this.beforeSlide();
39805 this.el.setStyle("z-index", 10001);
39806 this.el.slideIn(this.getSlideAnchor(), {
39807 callback: function(){
39809 this.initAutoHide();
39810 Roo.get(document).on("click", this.slideInIf, this);
39811 this.fireEvent("slideshow", this);
39818 afterSlideIn : function(){
39819 this.clearAutoHide();
39820 this.isSlid = false;
39821 this.clearMonitor();
39822 this.el.setStyle("z-index", "");
39823 if(this.collapseBtn){
39824 this.collapseBtn.show();
39826 this.closeBtn.setStyle('display', this.closeBtnState);
39828 this.stickBtn.hide();
39830 this.fireEvent("slidehide", this);
39833 slideIn : function(cb){
39834 if(!this.isSlid || this.el.hasActiveFx()){
39838 this.isSlid = false;
39839 this.beforeSlide();
39840 this.el.slideOut(this.getSlideAnchor(), {
39841 callback: function(){
39842 this.el.setLeftTop(-10000, -10000);
39844 this.afterSlideIn();
39852 slideInIf : function(e){
39853 if(!e.within(this.el)){
39858 animateCollapse : function(){
39859 this.beforeSlide();
39860 this.el.setStyle("z-index", 20000);
39861 var anchor = this.getSlideAnchor();
39862 this.el.slideOut(anchor, {
39863 callback : function(){
39864 this.el.setStyle("z-index", "");
39865 this.collapsedEl.slideIn(anchor, {duration:.3});
39867 this.el.setLocation(-10000,-10000);
39869 this.fireEvent("collapsed", this);
39876 animateExpand : function(){
39877 this.beforeSlide();
39878 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39879 this.el.setStyle("z-index", 20000);
39880 this.collapsedEl.hide({
39883 this.el.slideIn(this.getSlideAnchor(), {
39884 callback : function(){
39885 this.el.setStyle("z-index", "");
39888 this.split.el.show();
39890 this.fireEvent("invalidated", this);
39891 this.fireEvent("expanded", this);
39919 getAnchor : function(){
39920 return this.anchors[this.position];
39923 getCollapseAnchor : function(){
39924 return this.canchors[this.position];
39927 getSlideAnchor : function(){
39928 return this.sanchors[this.position];
39931 getAlignAdj : function(){
39932 var cm = this.cmargins;
39933 switch(this.position){
39949 getExpandAdj : function(){
39950 var c = this.collapsedEl, cm = this.cmargins;
39951 switch(this.position){
39953 return [-(cm.right+c.getWidth()+cm.left), 0];
39956 return [cm.right+c.getWidth()+cm.left, 0];
39959 return [0, -(cm.top+cm.bottom+c.getHeight())];
39962 return [0, cm.top+cm.bottom+c.getHeight()];
39968 * Ext JS Library 1.1.1
39969 * Copyright(c) 2006-2007, Ext JS, LLC.
39971 * Originally Released Under LGPL - original licence link has changed is not relivant.
39974 * <script type="text/javascript">
39977 * These classes are private internal classes
39979 Roo.bootstrap.layout.Center = function(config){
39980 config.region = "center";
39981 Roo.bootstrap.layout.Region.call(this, config);
39982 this.visible = true;
39983 this.minWidth = config.minWidth || 20;
39984 this.minHeight = config.minHeight || 20;
39987 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39989 // center panel can't be hidden
39993 // center panel can't be hidden
39996 getMinWidth: function(){
39997 return this.minWidth;
40000 getMinHeight: function(){
40001 return this.minHeight;
40015 Roo.bootstrap.layout.North = function(config)
40017 config.region = 'north';
40018 config.cursor = 'n-resize';
40020 Roo.bootstrap.layout.Split.call(this, config);
40024 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40025 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40026 this.split.el.addClass("roo-layout-split-v");
40028 //var size = config.initialSize || config.height;
40029 //if(this.el && typeof size != "undefined"){
40030 // this.el.setHeight(size);
40033 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40035 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40038 onRender : function(ctr, pos)
40040 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40041 var size = this.config.initialSize || this.config.height;
40042 if(this.el && typeof size != "undefined"){
40043 this.el.setHeight(size);
40048 getBox : function(){
40049 if(this.collapsed){
40050 return this.collapsedEl.getBox();
40052 var box = this.el.getBox();
40054 box.height += this.split.el.getHeight();
40059 updateBox : function(box){
40060 if(this.split && !this.collapsed){
40061 box.height -= this.split.el.getHeight();
40062 this.split.el.setLeft(box.x);
40063 this.split.el.setTop(box.y+box.height);
40064 this.split.el.setWidth(box.width);
40066 if(this.collapsed){
40067 this.updateBody(box.width, null);
40069 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40077 Roo.bootstrap.layout.South = function(config){
40078 config.region = 'south';
40079 config.cursor = 's-resize';
40080 Roo.bootstrap.layout.Split.call(this, config);
40082 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40083 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40084 this.split.el.addClass("roo-layout-split-v");
40089 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40090 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40092 onRender : function(ctr, pos)
40094 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40095 var size = this.config.initialSize || this.config.height;
40096 if(this.el && typeof size != "undefined"){
40097 this.el.setHeight(size);
40102 getBox : function(){
40103 if(this.collapsed){
40104 return this.collapsedEl.getBox();
40106 var box = this.el.getBox();
40108 var sh = this.split.el.getHeight();
40115 updateBox : function(box){
40116 if(this.split && !this.collapsed){
40117 var sh = this.split.el.getHeight();
40120 this.split.el.setLeft(box.x);
40121 this.split.el.setTop(box.y-sh);
40122 this.split.el.setWidth(box.width);
40124 if(this.collapsed){
40125 this.updateBody(box.width, null);
40127 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40131 Roo.bootstrap.layout.East = function(config){
40132 config.region = "east";
40133 config.cursor = "e-resize";
40134 Roo.bootstrap.layout.Split.call(this, config);
40136 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40137 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40138 this.split.el.addClass("roo-layout-split-h");
40142 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40143 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40145 onRender : function(ctr, pos)
40147 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40148 var size = this.config.initialSize || this.config.width;
40149 if(this.el && typeof size != "undefined"){
40150 this.el.setWidth(size);
40155 getBox : function(){
40156 if(this.collapsed){
40157 return this.collapsedEl.getBox();
40159 var box = this.el.getBox();
40161 var sw = this.split.el.getWidth();
40168 updateBox : function(box){
40169 if(this.split && !this.collapsed){
40170 var sw = this.split.el.getWidth();
40172 this.split.el.setLeft(box.x);
40173 this.split.el.setTop(box.y);
40174 this.split.el.setHeight(box.height);
40177 if(this.collapsed){
40178 this.updateBody(null, box.height);
40180 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40184 Roo.bootstrap.layout.West = function(config){
40185 config.region = "west";
40186 config.cursor = "w-resize";
40188 Roo.bootstrap.layout.Split.call(this, config);
40190 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40191 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40192 this.split.el.addClass("roo-layout-split-h");
40196 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40197 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40199 onRender: function(ctr, pos)
40201 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40202 var size = this.config.initialSize || this.config.width;
40203 if(typeof size != "undefined"){
40204 this.el.setWidth(size);
40208 getBox : function(){
40209 if(this.collapsed){
40210 return this.collapsedEl.getBox();
40212 var box = this.el.getBox();
40213 if (box.width == 0) {
40214 box.width = this.config.width; // kludge?
40217 box.width += this.split.el.getWidth();
40222 updateBox : function(box){
40223 if(this.split && !this.collapsed){
40224 var sw = this.split.el.getWidth();
40226 this.split.el.setLeft(box.x+box.width);
40227 this.split.el.setTop(box.y);
40228 this.split.el.setHeight(box.height);
40230 if(this.collapsed){
40231 this.updateBody(null, box.height);
40233 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40237 * Ext JS Library 1.1.1
40238 * Copyright(c) 2006-2007, Ext JS, LLC.
40240 * Originally Released Under LGPL - original licence link has changed is not relivant.
40243 * <script type="text/javascript">
40246 * @class Roo.bootstrap.paenl.Content
40247 * @extends Roo.util.Observable
40248 * @children Roo.bootstrap.Component
40249 * @parent builder Roo.bootstrap.layout.Border
40250 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40251 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40252 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40253 * @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
40254 * @cfg {Boolean} closable True if the panel can be closed/removed
40255 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40256 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40257 * @cfg {Toolbar} toolbar A toolbar for this panel
40258 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40259 * @cfg {String} title The title for this panel
40260 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40261 * @cfg {String} url Calls {@link #setUrl} with this value
40262 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40263 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40264 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40265 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40266 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40267 * @cfg {Boolean} badges render the badges
40268 * @cfg {String} cls extra classes to use
40269 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40272 * Create a new ContentPanel.
40273 * @param {String/Object} config A string to set only the title or a config object
40276 Roo.bootstrap.panel.Content = function( config){
40278 this.tpl = config.tpl || false;
40280 var el = config.el;
40281 var content = config.content;
40283 if(config.autoCreate){ // xtype is available if this is called from factory
40286 this.el = Roo.get(el);
40287 if(!this.el && config && config.autoCreate){
40288 if(typeof config.autoCreate == "object"){
40289 if(!config.autoCreate.id){
40290 config.autoCreate.id = config.id||el;
40292 this.el = Roo.DomHelper.append(document.body,
40293 config.autoCreate, true);
40297 cls: (config.cls || '') +
40298 (config.background ? ' bg-' + config.background : '') +
40299 " roo-layout-inactive-content",
40302 if (config.iframe) {
40306 style : 'border: 0px',
40307 src : 'about:blank'
40313 elcfg.html = config.html;
40317 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40318 if (config.iframe) {
40319 this.iframeEl = this.el.select('iframe',true).first();
40324 this.closable = false;
40325 this.loaded = false;
40326 this.active = false;
40329 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40331 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40333 this.wrapEl = this.el; //this.el.wrap();
40335 if (config.toolbar.items) {
40336 ti = config.toolbar.items ;
40337 delete config.toolbar.items ;
40341 this.toolbar.render(this.wrapEl, 'before');
40342 for(var i =0;i < ti.length;i++) {
40343 // Roo.log(['add child', items[i]]);
40344 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40346 this.toolbar.items = nitems;
40347 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40348 delete config.toolbar;
40352 // xtype created footer. - not sure if will work as we normally have to render first..
40353 if (this.footer && !this.footer.el && this.footer.xtype) {
40354 if (!this.wrapEl) {
40355 this.wrapEl = this.el.wrap();
40358 this.footer.container = this.wrapEl.createChild();
40360 this.footer = Roo.factory(this.footer, Roo);
40365 if(typeof config == "string"){
40366 this.title = config;
40368 Roo.apply(this, config);
40372 this.resizeEl = Roo.get(this.resizeEl, true);
40374 this.resizeEl = this.el;
40376 // handle view.xtype
40384 * Fires when this panel is activated.
40385 * @param {Roo.ContentPanel} this
40389 * @event deactivate
40390 * Fires when this panel is activated.
40391 * @param {Roo.ContentPanel} this
40393 "deactivate" : true,
40397 * Fires when this panel is resized if fitToFrame is true.
40398 * @param {Roo.ContentPanel} this
40399 * @param {Number} width The width after any component adjustments
40400 * @param {Number} height The height after any component adjustments
40406 * Fires when this tab is created
40407 * @param {Roo.ContentPanel} this
40413 * Fires when this content is scrolled
40414 * @param {Roo.ContentPanel} this
40415 * @param {Event} scrollEvent
40426 if(this.autoScroll && !this.iframe){
40427 this.resizeEl.setStyle("overflow", "auto");
40428 this.resizeEl.on('scroll', this.onScroll, this);
40430 // fix randome scrolling
40431 //this.el.on('scroll', function() {
40432 // Roo.log('fix random scolling');
40433 // this.scrollTo('top',0);
40436 content = content || this.content;
40438 this.setContent(content);
40440 if(config && config.url){
40441 this.setUrl(this.url, this.params, this.loadOnce);
40446 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40448 if (this.view && typeof(this.view.xtype) != 'undefined') {
40449 this.view.el = this.el.appendChild(document.createElement("div"));
40450 this.view = Roo.factory(this.view);
40451 this.view.render && this.view.render(false, '');
40455 this.fireEvent('render', this);
40458 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40468 /* Resize Element - use this to work out scroll etc. */
40471 setRegion : function(region){
40472 this.region = region;
40473 this.setActiveClass(region && !this.background);
40477 setActiveClass: function(state)
40480 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40481 this.el.setStyle('position','relative');
40483 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40484 this.el.setStyle('position', 'absolute');
40489 * Returns the toolbar for this Panel if one was configured.
40490 * @return {Roo.Toolbar}
40492 getToolbar : function(){
40493 return this.toolbar;
40496 setActiveState : function(active)
40498 this.active = active;
40499 this.setActiveClass(active);
40501 if(this.fireEvent("deactivate", this) === false){
40506 this.fireEvent("activate", this);
40510 * Updates this panel's element (not for iframe)
40511 * @param {String} content The new content
40512 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40514 setContent : function(content, loadScripts){
40519 this.el.update(content, loadScripts);
40522 ignoreResize : function(w, h)
40524 return false; // always resize?
40525 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40528 this.lastSize = {width: w, height: h};
40533 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40534 * @return {Roo.UpdateManager} The UpdateManager
40536 getUpdateManager : function(){
40540 return this.el.getUpdateManager();
40543 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40544 * Does not work with IFRAME contents
40545 * @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:
40548 url: "your-url.php",
40549 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40550 callback: yourFunction,
40551 scope: yourObject, //(optional scope)
40554 text: "Loading...",
40560 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40561 * 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.
40562 * @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}
40563 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40564 * @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.
40565 * @return {Roo.ContentPanel} this
40573 var um = this.el.getUpdateManager();
40574 um.update.apply(um, arguments);
40580 * 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.
40581 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40582 * @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)
40583 * @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)
40584 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40586 setUrl : function(url, params, loadOnce){
40588 this.iframeEl.dom.src = url;
40592 if(this.refreshDelegate){
40593 this.removeListener("activate", this.refreshDelegate);
40595 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40596 this.on("activate", this.refreshDelegate);
40597 return this.el.getUpdateManager();
40600 _handleRefresh : function(url, params, loadOnce){
40601 if(!loadOnce || !this.loaded){
40602 var updater = this.el.getUpdateManager();
40603 updater.update(url, params, this._setLoaded.createDelegate(this));
40607 _setLoaded : function(){
40608 this.loaded = true;
40612 * Returns this panel's id
40615 getId : function(){
40620 * Returns this panel's element - used by regiosn to add.
40621 * @return {Roo.Element}
40623 getEl : function(){
40624 return this.wrapEl || this.el;
40629 adjustForComponents : function(width, height)
40631 //Roo.log('adjustForComponents ');
40632 if(this.resizeEl != this.el){
40633 width -= this.el.getFrameWidth('lr');
40634 height -= this.el.getFrameWidth('tb');
40637 var te = this.toolbar.getEl();
40638 te.setWidth(width);
40639 height -= te.getHeight();
40642 var te = this.footer.getEl();
40643 te.setWidth(width);
40644 height -= te.getHeight();
40648 if(this.adjustments){
40649 width += this.adjustments[0];
40650 height += this.adjustments[1];
40652 return {"width": width, "height": height};
40655 setSize : function(width, height){
40656 if(this.fitToFrame && !this.ignoreResize(width, height)){
40657 if(this.fitContainer && this.resizeEl != this.el){
40658 this.el.setSize(width, height);
40660 var size = this.adjustForComponents(width, height);
40662 this.iframeEl.setSize(width,height);
40665 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40666 this.fireEvent('resize', this, size.width, size.height);
40673 * Returns this panel's title
40676 getTitle : function(){
40678 if (typeof(this.title) != 'object') {
40683 for (var k in this.title) {
40684 if (!this.title.hasOwnProperty(k)) {
40688 if (k.indexOf('-') >= 0) {
40689 var s = k.split('-');
40690 for (var i = 0; i<s.length; i++) {
40691 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40694 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40701 * Set this panel's title
40702 * @param {String} title
40704 setTitle : function(title){
40705 this.title = title;
40707 this.region.updatePanelTitle(this, title);
40712 * Returns true is this panel was configured to be closable
40713 * @return {Boolean}
40715 isClosable : function(){
40716 return this.closable;
40719 beforeSlide : function(){
40721 this.resizeEl.clip();
40724 afterSlide : function(){
40726 this.resizeEl.unclip();
40730 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40731 * Will fail silently if the {@link #setUrl} method has not been called.
40732 * This does not activate the panel, just updates its content.
40734 refresh : function(){
40735 if(this.refreshDelegate){
40736 this.loaded = false;
40737 this.refreshDelegate();
40742 * Destroys this panel
40744 destroy : function(){
40745 this.el.removeAllListeners();
40746 var tempEl = document.createElement("span");
40747 tempEl.appendChild(this.el.dom);
40748 tempEl.innerHTML = "";
40754 * form - if the content panel contains a form - this is a reference to it.
40755 * @type {Roo.form.Form}
40759 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40760 * This contains a reference to it.
40766 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40776 * @param {Object} cfg Xtype definition of item to add.
40780 getChildContainer: function () {
40781 return this.getEl();
40785 onScroll : function(e)
40787 this.fireEvent('scroll', this, e);
40792 var ret = new Roo.factory(cfg);
40797 if (cfg.xtype.match(/^Form$/)) {
40800 //if (this.footer) {
40801 // el = this.footer.container.insertSibling(false, 'before');
40803 el = this.el.createChild();
40806 this.form = new Roo.form.Form(cfg);
40809 if ( this.form.allItems.length) {
40810 this.form.render(el.dom);
40814 // should only have one of theses..
40815 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40816 // views.. should not be just added - used named prop 'view''
40818 cfg.el = this.el.appendChild(document.createElement("div"));
40821 var ret = new Roo.factory(cfg);
40823 ret.render && ret.render(false, ''); // render blank..
40833 * @class Roo.bootstrap.panel.Grid
40834 * @extends Roo.bootstrap.panel.Content
40836 * Create a new GridPanel.
40837 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40838 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40839 * @param {Object} config A the config object
40845 Roo.bootstrap.panel.Grid = function(config)
40849 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40850 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40852 config.el = this.wrapper;
40853 //this.el = this.wrapper;
40855 if (config.container) {
40856 // ctor'ed from a Border/panel.grid
40859 this.wrapper.setStyle("overflow", "hidden");
40860 this.wrapper.addClass('roo-grid-container');
40865 if(config.toolbar){
40866 var tool_el = this.wrapper.createChild();
40867 this.toolbar = Roo.factory(config.toolbar);
40869 if (config.toolbar.items) {
40870 ti = config.toolbar.items ;
40871 delete config.toolbar.items ;
40875 this.toolbar.render(tool_el);
40876 for(var i =0;i < ti.length;i++) {
40877 // Roo.log(['add child', items[i]]);
40878 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40880 this.toolbar.items = nitems;
40882 delete config.toolbar;
40885 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40886 config.grid.scrollBody = true;;
40887 config.grid.monitorWindowResize = false; // turn off autosizing
40888 config.grid.autoHeight = false;
40889 config.grid.autoWidth = false;
40891 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40893 if (config.background) {
40894 // render grid on panel activation (if panel background)
40895 this.on('activate', function(gp) {
40896 if (!gp.grid.rendered) {
40897 gp.grid.render(this.wrapper);
40898 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40903 this.grid.render(this.wrapper);
40904 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40907 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40908 // ??? needed ??? config.el = this.wrapper;
40913 // xtype created footer. - not sure if will work as we normally have to render first..
40914 if (this.footer && !this.footer.el && this.footer.xtype) {
40916 var ctr = this.grid.getView().getFooterPanel(true);
40917 this.footer.dataSource = this.grid.dataSource;
40918 this.footer = Roo.factory(this.footer, Roo);
40919 this.footer.render(ctr);
40929 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40932 is_resizing : false,
40934 getId : function(){
40935 return this.grid.id;
40939 * Returns the grid for this panel
40940 * @return {Roo.bootstrap.Table}
40942 getGrid : function(){
40946 setSize : function(width, height)
40948 if (this.is_resizing) {
40952 this.is_resizing = true;
40953 if(!this.ignoreResize(width, height)){
40954 var grid = this.grid;
40955 var size = this.adjustForComponents(width, height);
40956 // tfoot is not a footer?
40959 var gridel = grid.getGridEl();
40960 gridel.setSize(size.width, size.height);
40962 var tbd = grid.getGridEl().select('tbody', true).first();
40963 var thd = grid.getGridEl().select('thead',true).first();
40964 var tbf= grid.getGridEl().select('tfoot', true).first();
40967 size.height -= tbf.getHeight();
40970 size.height -= thd.getHeight();
40973 tbd.setSize(size.width, size.height );
40974 // this is for the account management tab -seems to work there.
40975 var thd = grid.getGridEl().select('thead',true).first();
40977 // tbd.setSize(size.width, size.height - thd.getHeight());
40982 this.is_resizing = false;
40987 beforeSlide : function(){
40988 this.grid.getView().scroller.clip();
40991 afterSlide : function(){
40992 this.grid.getView().scroller.unclip();
40995 destroy : function(){
40996 this.grid.destroy();
40998 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41003 * @class Roo.bootstrap.panel.Nest
41004 * @extends Roo.bootstrap.panel.Content
41006 * Create a new Panel, that can contain a layout.Border.
41009 * @param {String/Object} config A string to set only the title or a config object
41011 Roo.bootstrap.panel.Nest = function(config)
41013 // construct with only one argument..
41014 /* FIXME - implement nicer consturctors
41015 if (layout.layout) {
41017 layout = config.layout;
41018 delete config.layout;
41020 if (layout.xtype && !layout.getEl) {
41021 // then layout needs constructing..
41022 layout = Roo.factory(layout, Roo);
41026 config.el = config.layout.getEl();
41028 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41030 config.layout.monitorWindowResize = false; // turn off autosizing
41031 this.layout = config.layout;
41032 this.layout.getEl().addClass("roo-layout-nested-layout");
41033 this.layout.parent = this;
41040 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41042 * @cfg {Roo.BorderLayout} layout The layout for this panel
41046 setSize : function(width, height){
41047 if(!this.ignoreResize(width, height)){
41048 var size = this.adjustForComponents(width, height);
41049 var el = this.layout.getEl();
41050 if (size.height < 1) {
41051 el.setWidth(size.width);
41053 el.setSize(size.width, size.height);
41055 var touch = el.dom.offsetWidth;
41056 this.layout.layout();
41057 // ie requires a double layout on the first pass
41058 if(Roo.isIE && !this.initialized){
41059 this.initialized = true;
41060 this.layout.layout();
41065 // activate all subpanels if not currently active..
41067 setActiveState : function(active){
41068 this.active = active;
41069 this.setActiveClass(active);
41072 this.fireEvent("deactivate", this);
41076 this.fireEvent("activate", this);
41077 // not sure if this should happen before or after..
41078 if (!this.layout) {
41079 return; // should not happen..
41082 for (var r in this.layout.regions) {
41083 reg = this.layout.getRegion(r);
41084 if (reg.getActivePanel()) {
41085 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41086 reg.setActivePanel(reg.getActivePanel());
41089 if (!reg.panels.length) {
41092 reg.showPanel(reg.getPanel(0));
41101 * Returns the nested BorderLayout for this panel
41102 * @return {Roo.BorderLayout}
41104 getLayout : function(){
41105 return this.layout;
41109 * Adds a xtype elements to the layout of the nested panel
41113 xtype : 'ContentPanel',
41120 xtype : 'NestedLayoutPanel',
41126 items : [ ... list of content panels or nested layout panels.. ]
41130 * @param {Object} cfg Xtype definition of item to add.
41132 addxtype : function(cfg) {
41133 return this.layout.addxtype(cfg);
41138 * Ext JS Library 1.1.1
41139 * Copyright(c) 2006-2007, Ext JS, LLC.
41141 * Originally Released Under LGPL - original licence link has changed is not relivant.
41144 * <script type="text/javascript">
41147 * @class Roo.TabPanel
41148 * @extends Roo.util.Observable
41149 * A lightweight tab container.
41153 // basic tabs 1, built from existing content
41154 var tabs = new Roo.TabPanel("tabs1");
41155 tabs.addTab("script", "View Script");
41156 tabs.addTab("markup", "View Markup");
41157 tabs.activate("script");
41159 // more advanced tabs, built from javascript
41160 var jtabs = new Roo.TabPanel("jtabs");
41161 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41163 // set up the UpdateManager
41164 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41165 var updater = tab2.getUpdateManager();
41166 updater.setDefaultUrl("ajax1.htm");
41167 tab2.on('activate', updater.refresh, updater, true);
41169 // Use setUrl for Ajax loading
41170 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41171 tab3.setUrl("ajax2.htm", null, true);
41174 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41177 jtabs.activate("jtabs-1");
41180 * Create a new TabPanel.
41181 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41182 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41184 Roo.bootstrap.panel.Tabs = function(config){
41186 * The container element for this TabPanel.
41187 * @type Roo.Element
41189 this.el = Roo.get(config.el);
41192 if(typeof config == "boolean"){
41193 this.tabPosition = config ? "bottom" : "top";
41195 Roo.apply(this, config);
41199 if(this.tabPosition == "bottom"){
41200 // if tabs are at the bottom = create the body first.
41201 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41202 this.el.addClass("roo-tabs-bottom");
41204 // next create the tabs holders
41206 if (this.tabPosition == "west"){
41208 var reg = this.region; // fake it..
41210 if (!reg.mgr.parent) {
41213 reg = reg.mgr.parent.region;
41215 Roo.log("got nest?");
41217 if (reg.mgr.getRegion('west')) {
41218 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41219 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41220 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41221 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41222 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41230 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41231 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41232 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41233 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41238 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41241 // finally - if tabs are at the top, then create the body last..
41242 if(this.tabPosition != "bottom"){
41243 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41244 * @type Roo.Element
41246 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41247 this.el.addClass("roo-tabs-top");
41251 this.bodyEl.setStyle("position", "relative");
41253 this.active = null;
41254 this.activateDelegate = this.activate.createDelegate(this);
41259 * Fires when the active tab changes
41260 * @param {Roo.TabPanel} this
41261 * @param {Roo.TabPanelItem} activePanel The new active tab
41265 * @event beforetabchange
41266 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41267 * @param {Roo.TabPanel} this
41268 * @param {Object} e Set cancel to true on this object to cancel the tab change
41269 * @param {Roo.TabPanelItem} tab The tab being changed to
41271 "beforetabchange" : true
41274 Roo.EventManager.onWindowResize(this.onResize, this);
41275 this.cpad = this.el.getPadding("lr");
41276 this.hiddenCount = 0;
41279 // toolbar on the tabbar support...
41280 if (this.toolbar) {
41281 alert("no toolbar support yet");
41282 this.toolbar = false;
41284 var tcfg = this.toolbar;
41285 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41286 this.toolbar = new Roo.Toolbar(tcfg);
41287 if (Roo.isSafari) {
41288 var tbl = tcfg.container.child('table', true);
41289 tbl.setAttribute('width', '100%');
41297 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41300 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41302 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41304 tabPosition : "top",
41306 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41308 currentTabWidth : 0,
41310 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41314 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41318 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41320 preferredTabWidth : 175,
41322 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41324 resizeTabs : false,
41326 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41328 monitorResize : true,
41330 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41332 toolbar : false, // set by caller..
41334 region : false, /// set by caller
41336 disableTooltips : true, // not used yet...
41339 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41340 * @param {String} id The id of the div to use <b>or create</b>
41341 * @param {String} text The text for the tab
41342 * @param {String} content (optional) Content to put in the TabPanelItem body
41343 * @param {Boolean} closable (optional) True to create a close icon on the tab
41344 * @return {Roo.TabPanelItem} The created TabPanelItem
41346 addTab : function(id, text, content, closable, tpl)
41348 var item = new Roo.bootstrap.panel.TabItem({
41352 closable : closable,
41355 this.addTabItem(item);
41357 item.setContent(content);
41363 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41364 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41365 * @return {Roo.TabPanelItem}
41367 getTab : function(id){
41368 return this.items[id];
41372 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41373 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41375 hideTab : function(id){
41376 var t = this.items[id];
41379 this.hiddenCount++;
41380 this.autoSizeTabs();
41385 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41386 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41388 unhideTab : function(id){
41389 var t = this.items[id];
41391 t.setHidden(false);
41392 this.hiddenCount--;
41393 this.autoSizeTabs();
41398 * Adds an existing {@link Roo.TabPanelItem}.
41399 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41401 addTabItem : function(item)
41403 this.items[item.id] = item;
41404 this.items.push(item);
41405 this.autoSizeTabs();
41406 // if(this.resizeTabs){
41407 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41408 // this.autoSizeTabs();
41410 // item.autoSize();
41415 * Removes a {@link Roo.TabPanelItem}.
41416 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41418 removeTab : function(id){
41419 var items = this.items;
41420 var tab = items[id];
41421 if(!tab) { return; }
41422 var index = items.indexOf(tab);
41423 if(this.active == tab && items.length > 1){
41424 var newTab = this.getNextAvailable(index);
41429 this.stripEl.dom.removeChild(tab.pnode.dom);
41430 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41431 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41433 items.splice(index, 1);
41434 delete this.items[tab.id];
41435 tab.fireEvent("close", tab);
41436 tab.purgeListeners();
41437 this.autoSizeTabs();
41440 getNextAvailable : function(start){
41441 var items = this.items;
41443 // look for a next tab that will slide over to
41444 // replace the one being removed
41445 while(index < items.length){
41446 var item = items[++index];
41447 if(item && !item.isHidden()){
41451 // if one isn't found select the previous tab (on the left)
41454 var item = items[--index];
41455 if(item && !item.isHidden()){
41463 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41464 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41466 disableTab : function(id){
41467 var tab = this.items[id];
41468 if(tab && this.active != tab){
41474 * Enables a {@link Roo.TabPanelItem} that is disabled.
41475 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41477 enableTab : function(id){
41478 var tab = this.items[id];
41483 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41484 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41485 * @return {Roo.TabPanelItem} The TabPanelItem.
41487 activate : function(id)
41489 //Roo.log('activite:' + id);
41491 var tab = this.items[id];
41495 if(tab == this.active || tab.disabled){
41499 this.fireEvent("beforetabchange", this, e, tab);
41500 if(e.cancel !== true && !tab.disabled){
41502 this.active.hide();
41504 this.active = this.items[id];
41505 this.active.show();
41506 this.fireEvent("tabchange", this, this.active);
41512 * Gets the active {@link Roo.TabPanelItem}.
41513 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41515 getActiveTab : function(){
41516 return this.active;
41520 * Updates the tab body element to fit the height of the container element
41521 * for overflow scrolling
41522 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41524 syncHeight : function(targetHeight){
41525 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41526 var bm = this.bodyEl.getMargins();
41527 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41528 this.bodyEl.setHeight(newHeight);
41532 onResize : function(){
41533 if(this.monitorResize){
41534 this.autoSizeTabs();
41539 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41541 beginUpdate : function(){
41542 this.updating = true;
41546 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41548 endUpdate : function(){
41549 this.updating = false;
41550 this.autoSizeTabs();
41554 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41556 autoSizeTabs : function()
41558 var count = this.items.length;
41559 var vcount = count - this.hiddenCount;
41562 this.stripEl.hide();
41564 this.stripEl.show();
41567 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41572 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41573 var availWidth = Math.floor(w / vcount);
41574 var b = this.stripBody;
41575 if(b.getWidth() > w){
41576 var tabs = this.items;
41577 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41578 if(availWidth < this.minTabWidth){
41579 /*if(!this.sleft){ // incomplete scrolling code
41580 this.createScrollButtons();
41583 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41586 if(this.currentTabWidth < this.preferredTabWidth){
41587 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41593 * Returns the number of tabs in this TabPanel.
41596 getCount : function(){
41597 return this.items.length;
41601 * Resizes all the tabs to the passed width
41602 * @param {Number} The new width
41604 setTabWidth : function(width){
41605 this.currentTabWidth = width;
41606 for(var i = 0, len = this.items.length; i < len; i++) {
41607 if(!this.items[i].isHidden()) {
41608 this.items[i].setWidth(width);
41614 * Destroys this TabPanel
41615 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41617 destroy : function(removeEl){
41618 Roo.EventManager.removeResizeListener(this.onResize, this);
41619 for(var i = 0, len = this.items.length; i < len; i++){
41620 this.items[i].purgeListeners();
41622 if(removeEl === true){
41623 this.el.update("");
41628 createStrip : function(container)
41630 var strip = document.createElement("nav");
41631 strip.className = Roo.bootstrap.version == 4 ?
41632 "navbar-light bg-light" :
41633 "navbar navbar-default"; //"x-tabs-wrap";
41634 container.appendChild(strip);
41638 createStripList : function(strip)
41640 // div wrapper for retard IE
41641 // returns the "tr" element.
41642 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41643 //'<div class="x-tabs-strip-wrap">'+
41644 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41645 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41646 return strip.firstChild; //.firstChild.firstChild.firstChild;
41648 createBody : function(container)
41650 var body = document.createElement("div");
41651 Roo.id(body, "tab-body");
41652 //Roo.fly(body).addClass("x-tabs-body");
41653 Roo.fly(body).addClass("tab-content");
41654 container.appendChild(body);
41657 createItemBody :function(bodyEl, id){
41658 var body = Roo.getDom(id);
41660 body = document.createElement("div");
41663 //Roo.fly(body).addClass("x-tabs-item-body");
41664 Roo.fly(body).addClass("tab-pane");
41665 bodyEl.insertBefore(body, bodyEl.firstChild);
41669 createStripElements : function(stripEl, text, closable, tpl)
41671 var td = document.createElement("li"); // was td..
41672 td.className = 'nav-item';
41674 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41677 stripEl.appendChild(td);
41679 td.className = "x-tabs-closable";
41680 if(!this.closeTpl){
41681 this.closeTpl = new Roo.Template(
41682 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41683 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41684 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41687 var el = this.closeTpl.overwrite(td, {"text": text});
41688 var close = el.getElementsByTagName("div")[0];
41689 var inner = el.getElementsByTagName("em")[0];
41690 return {"el": el, "close": close, "inner": inner};
41693 // not sure what this is..
41694 // if(!this.tabTpl){
41695 //this.tabTpl = new Roo.Template(
41696 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41697 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41699 // this.tabTpl = new Roo.Template(
41700 // '<a href="#">' +
41701 // '<span unselectable="on"' +
41702 // (this.disableTooltips ? '' : ' title="{text}"') +
41703 // ' >{text}</span></a>'
41709 var template = tpl || this.tabTpl || false;
41712 template = new Roo.Template(
41713 Roo.bootstrap.version == 4 ?
41715 '<a class="nav-link" href="#" unselectable="on"' +
41716 (this.disableTooltips ? '' : ' title="{text}"') +
41719 '<a class="nav-link" href="#">' +
41720 '<span unselectable="on"' +
41721 (this.disableTooltips ? '' : ' title="{text}"') +
41722 ' >{text}</span></a>'
41727 switch (typeof(template)) {
41731 template = new Roo.Template(template);
41737 var el = template.overwrite(td, {"text": text});
41739 var inner = el.getElementsByTagName("span")[0];
41741 return {"el": el, "inner": inner};
41749 * @class Roo.TabPanelItem
41750 * @extends Roo.util.Observable
41751 * Represents an individual item (tab plus body) in a TabPanel.
41752 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41753 * @param {String} id The id of this TabPanelItem
41754 * @param {String} text The text for the tab of this TabPanelItem
41755 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41757 Roo.bootstrap.panel.TabItem = function(config){
41759 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41760 * @type Roo.TabPanel
41762 this.tabPanel = config.panel;
41764 * The id for this TabPanelItem
41767 this.id = config.id;
41769 this.disabled = false;
41771 this.text = config.text;
41773 this.loaded = false;
41774 this.closable = config.closable;
41777 * The body element for this TabPanelItem.
41778 * @type Roo.Element
41780 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41781 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41782 this.bodyEl.setStyle("display", "block");
41783 this.bodyEl.setStyle("zoom", "1");
41784 //this.hideAction();
41786 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41788 this.el = Roo.get(els.el);
41789 this.inner = Roo.get(els.inner, true);
41790 this.textEl = Roo.bootstrap.version == 4 ?
41791 this.el : Roo.get(this.el.dom.firstChild, true);
41793 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41794 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41797 // this.el.on("mousedown", this.onTabMouseDown, this);
41798 this.el.on("click", this.onTabClick, this);
41800 if(config.closable){
41801 var c = Roo.get(els.close, true);
41802 c.dom.title = this.closeText;
41803 c.addClassOnOver("close-over");
41804 c.on("click", this.closeClick, this);
41810 * Fires when this tab becomes the active tab.
41811 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41812 * @param {Roo.TabPanelItem} this
41816 * @event beforeclose
41817 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41818 * @param {Roo.TabPanelItem} this
41819 * @param {Object} e Set cancel to true on this object to cancel the close.
41821 "beforeclose": true,
41824 * Fires when this tab is closed.
41825 * @param {Roo.TabPanelItem} this
41829 * @event deactivate
41830 * Fires when this tab is no longer the active tab.
41831 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41832 * @param {Roo.TabPanelItem} this
41834 "deactivate" : true
41836 this.hidden = false;
41838 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41841 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41843 purgeListeners : function(){
41844 Roo.util.Observable.prototype.purgeListeners.call(this);
41845 this.el.removeAllListeners();
41848 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41851 this.status_node.addClass("active");
41854 this.tabPanel.stripWrap.repaint();
41856 this.fireEvent("activate", this.tabPanel, this);
41860 * Returns true if this tab is the active tab.
41861 * @return {Boolean}
41863 isActive : function(){
41864 return this.tabPanel.getActiveTab() == this;
41868 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41871 this.status_node.removeClass("active");
41873 this.fireEvent("deactivate", this.tabPanel, this);
41876 hideAction : function(){
41877 this.bodyEl.hide();
41878 this.bodyEl.setStyle("position", "absolute");
41879 this.bodyEl.setLeft("-20000px");
41880 this.bodyEl.setTop("-20000px");
41883 showAction : function(){
41884 this.bodyEl.setStyle("position", "relative");
41885 this.bodyEl.setTop("");
41886 this.bodyEl.setLeft("");
41887 this.bodyEl.show();
41891 * Set the tooltip for the tab.
41892 * @param {String} tooltip The tab's tooltip
41894 setTooltip : function(text){
41895 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41896 this.textEl.dom.qtip = text;
41897 this.textEl.dom.removeAttribute('title');
41899 this.textEl.dom.title = text;
41903 onTabClick : function(e){
41904 e.preventDefault();
41905 this.tabPanel.activate(this.id);
41908 onTabMouseDown : function(e){
41909 e.preventDefault();
41910 this.tabPanel.activate(this.id);
41913 getWidth : function(){
41914 return this.inner.getWidth();
41917 setWidth : function(width){
41918 var iwidth = width - this.linode.getPadding("lr");
41919 this.inner.setWidth(iwidth);
41920 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41921 this.linode.setWidth(width);
41925 * Show or hide the tab
41926 * @param {Boolean} hidden True to hide or false to show.
41928 setHidden : function(hidden){
41929 this.hidden = hidden;
41930 this.linode.setStyle("display", hidden ? "none" : "");
41934 * Returns true if this tab is "hidden"
41935 * @return {Boolean}
41937 isHidden : function(){
41938 return this.hidden;
41942 * Returns the text for this tab
41945 getText : function(){
41949 autoSize : function(){
41950 //this.el.beginMeasure();
41951 this.textEl.setWidth(1);
41953 * #2804 [new] Tabs in Roojs
41954 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41956 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41957 //this.el.endMeasure();
41961 * Sets the text for the tab (Note: this also sets the tooltip text)
41962 * @param {String} text The tab's text and tooltip
41964 setText : function(text){
41966 this.textEl.update(text);
41967 this.setTooltip(text);
41968 //if(!this.tabPanel.resizeTabs){
41969 // this.autoSize();
41973 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41975 activate : function(){
41976 this.tabPanel.activate(this.id);
41980 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41982 disable : function(){
41983 if(this.tabPanel.active != this){
41984 this.disabled = true;
41985 this.status_node.addClass("disabled");
41990 * Enables this TabPanelItem if it was previously disabled.
41992 enable : function(){
41993 this.disabled = false;
41994 this.status_node.removeClass("disabled");
41998 * Sets the content for this TabPanelItem.
41999 * @param {String} content The content
42000 * @param {Boolean} loadScripts true to look for and load scripts
42002 setContent : function(content, loadScripts){
42003 this.bodyEl.update(content, loadScripts);
42007 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42008 * @return {Roo.UpdateManager} The UpdateManager
42010 getUpdateManager : function(){
42011 return this.bodyEl.getUpdateManager();
42015 * Set a URL to be used to load the content for this TabPanelItem.
42016 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42017 * @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)
42018 * @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)
42019 * @return {Roo.UpdateManager} The UpdateManager
42021 setUrl : function(url, params, loadOnce){
42022 if(this.refreshDelegate){
42023 this.un('activate', this.refreshDelegate);
42025 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42026 this.on("activate", this.refreshDelegate);
42027 return this.bodyEl.getUpdateManager();
42031 _handleRefresh : function(url, params, loadOnce){
42032 if(!loadOnce || !this.loaded){
42033 var updater = this.bodyEl.getUpdateManager();
42034 updater.update(url, params, this._setLoaded.createDelegate(this));
42039 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42040 * Will fail silently if the setUrl method has not been called.
42041 * This does not activate the panel, just updates its content.
42043 refresh : function(){
42044 if(this.refreshDelegate){
42045 this.loaded = false;
42046 this.refreshDelegate();
42051 _setLoaded : function(){
42052 this.loaded = true;
42056 closeClick : function(e){
42059 this.fireEvent("beforeclose", this, o);
42060 if(o.cancel !== true){
42061 this.tabPanel.removeTab(this.id);
42065 * The text displayed in the tooltip for the close icon.
42068 closeText : "Close this tab"
42071 * This script refer to:
42072 * Title: International Telephone Input
42073 * Author: Jack O'Connor
42074 * Code version: v12.1.12
42075 * Availability: https://github.com/jackocnr/intl-tel-input.git
42078 Roo.bootstrap.form.PhoneInputData = function() {
42081 "Afghanistan (افغانستان)",
42086 "Albania (Shqipëri)",
42091 "Algeria (الجزائر)",
42116 "Antigua and Barbuda",
42126 "Armenia (Հայաստան)",
42142 "Austria (Österreich)",
42147 "Azerbaijan (Azərbaycan)",
42157 "Bahrain (البحرين)",
42162 "Bangladesh (বাংলাদেশ)",
42172 "Belarus (Беларусь)",
42177 "Belgium (België)",
42207 "Bosnia and Herzegovina (Босна и Херцеговина)",
42222 "British Indian Ocean Territory",
42227 "British Virgin Islands",
42237 "Bulgaria (България)",
42247 "Burundi (Uburundi)",
42252 "Cambodia (កម្ពុជា)",
42257 "Cameroon (Cameroun)",
42266 ["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"]
42269 "Cape Verde (Kabu Verdi)",
42274 "Caribbean Netherlands",
42285 "Central African Republic (République centrafricaine)",
42305 "Christmas Island",
42311 "Cocos (Keeling) Islands",
42322 "Comoros (جزر القمر)",
42327 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42332 "Congo (Republic) (Congo-Brazzaville)",
42352 "Croatia (Hrvatska)",
42373 "Czech Republic (Česká republika)",
42378 "Denmark (Danmark)",
42393 "Dominican Republic (República Dominicana)",
42397 ["809", "829", "849"]
42415 "Equatorial Guinea (Guinea Ecuatorial)",
42435 "Falkland Islands (Islas Malvinas)",
42440 "Faroe Islands (Føroyar)",
42461 "French Guiana (Guyane française)",
42466 "French Polynesia (Polynésie française)",
42481 "Georgia (საქართველო)",
42486 "Germany (Deutschland)",
42506 "Greenland (Kalaallit Nunaat)",
42543 "Guinea-Bissau (Guiné Bissau)",
42568 "Hungary (Magyarország)",
42573 "Iceland (Ísland)",
42593 "Iraq (العراق)",
42609 "Israel (ישראל)",
42636 "Jordan (الأردن)",
42641 "Kazakhstan (Казахстан)",
42662 "Kuwait (الكويت)",
42667 "Kyrgyzstan (Кыргызстан)",
42677 "Latvia (Latvija)",
42682 "Lebanon (لبنان)",
42697 "Libya (ليبيا)",
42707 "Lithuania (Lietuva)",
42722 "Macedonia (FYROM) (Македонија)",
42727 "Madagascar (Madagasikara)",
42757 "Marshall Islands",
42767 "Mauritania (موريتانيا)",
42772 "Mauritius (Moris)",
42793 "Moldova (Republica Moldova)",
42803 "Mongolia (Монгол)",
42808 "Montenegro (Crna Gora)",
42818 "Morocco (المغرب)",
42824 "Mozambique (Moçambique)",
42829 "Myanmar (Burma) (မြန်မာ)",
42834 "Namibia (Namibië)",
42849 "Netherlands (Nederland)",
42854 "New Caledonia (Nouvelle-Calédonie)",
42889 "North Korea (조선 민주주의 인민 공화국)",
42894 "Northern Mariana Islands",
42910 "Pakistan (پاکستان)",
42920 "Palestine (فلسطين)",
42930 "Papua New Guinea",
42972 "Réunion (La Réunion)",
42978 "Romania (România)",
42994 "Saint Barthélemy",
43005 "Saint Kitts and Nevis",
43015 "Saint Martin (Saint-Martin (partie française))",
43021 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43026 "Saint Vincent and the Grenadines",
43041 "São Tomé and Príncipe (São Tomé e Príncipe)",
43046 "Saudi Arabia (المملكة العربية السعودية)",
43051 "Senegal (Sénégal)",
43081 "Slovakia (Slovensko)",
43086 "Slovenia (Slovenija)",
43096 "Somalia (Soomaaliya)",
43106 "South Korea (대한민국)",
43111 "South Sudan (جنوب السودان)",
43121 "Sri Lanka (ශ්රී ලංකාව)",
43126 "Sudan (السودان)",
43136 "Svalbard and Jan Mayen",
43147 "Sweden (Sverige)",
43152 "Switzerland (Schweiz)",
43157 "Syria (سوريا)",
43202 "Trinidad and Tobago",
43207 "Tunisia (تونس)",
43212 "Turkey (Türkiye)",
43222 "Turks and Caicos Islands",
43232 "U.S. Virgin Islands",
43242 "Ukraine (Україна)",
43247 "United Arab Emirates (الإمارات العربية المتحدة)",
43269 "Uzbekistan (Oʻzbekiston)",
43279 "Vatican City (Città del Vaticano)",
43290 "Vietnam (Việt Nam)",
43295 "Wallis and Futuna (Wallis-et-Futuna)",
43300 "Western Sahara (الصحراء الغربية)",
43306 "Yemen (اليمن)",
43330 * This script refer to:
43331 * Title: International Telephone Input
43332 * Author: Jack O'Connor
43333 * Code version: v12.1.12
43334 * Availability: https://github.com/jackocnr/intl-tel-input.git
43338 * @class Roo.bootstrap.form.PhoneInput
43339 * @extends Roo.bootstrap.form.TriggerField
43340 * An input with International dial-code selection
43342 * @cfg {String} defaultDialCode default '+852'
43343 * @cfg {Array} preferedCountries default []
43346 * Create a new PhoneInput.
43347 * @param {Object} config Configuration options
43350 Roo.bootstrap.form.PhoneInput = function(config) {
43351 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43354 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43356 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43358 listWidth: undefined,
43360 selectedClass: 'active',
43362 invalidClass : "has-warning",
43364 validClass: 'has-success',
43366 allowed: '0123456789',
43371 * @cfg {String} defaultDialCode The default dial code when initializing the input
43373 defaultDialCode: '+852',
43376 * @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
43378 preferedCountries: false,
43380 getAutoCreate : function()
43382 var data = Roo.bootstrap.form.PhoneInputData();
43383 var align = this.labelAlign || this.parentLabelAlign();
43386 this.allCountries = [];
43387 this.dialCodeMapping = [];
43389 for (var i = 0; i < data.length; i++) {
43391 this.allCountries[i] = {
43395 priority: c[3] || 0,
43396 areaCodes: c[4] || null
43398 this.dialCodeMapping[c[2]] = {
43401 priority: c[3] || 0,
43402 areaCodes: c[4] || null
43414 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43415 maxlength: this.max_length,
43416 cls : 'form-control tel-input',
43417 autocomplete: 'new-password'
43420 var hiddenInput = {
43423 cls: 'hidden-tel-input'
43427 hiddenInput.name = this.name;
43430 if (this.disabled) {
43431 input.disabled = true;
43434 var flag_container = {
43451 cls: this.hasFeedback ? 'has-feedback' : '',
43457 cls: 'dial-code-holder',
43464 cls: 'roo-select2-container input-group',
43471 if (this.fieldLabel.length) {
43474 tooltip: 'This field is required'
43480 cls: 'control-label',
43486 html: this.fieldLabel
43489 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43495 if(this.indicatorpos == 'right') {
43496 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43503 if(align == 'left') {
43511 if(this.labelWidth > 12){
43512 label.style = "width: " + this.labelWidth + 'px';
43514 if(this.labelWidth < 13 && this.labelmd == 0){
43515 this.labelmd = this.labelWidth;
43517 if(this.labellg > 0){
43518 label.cls += ' col-lg-' + this.labellg;
43519 input.cls += ' col-lg-' + (12 - this.labellg);
43521 if(this.labelmd > 0){
43522 label.cls += ' col-md-' + this.labelmd;
43523 container.cls += ' col-md-' + (12 - this.labelmd);
43525 if(this.labelsm > 0){
43526 label.cls += ' col-sm-' + this.labelsm;
43527 container.cls += ' col-sm-' + (12 - this.labelsm);
43529 if(this.labelxs > 0){
43530 label.cls += ' col-xs-' + this.labelxs;
43531 container.cls += ' col-xs-' + (12 - this.labelxs);
43541 var settings = this;
43543 ['xs','sm','md','lg'].map(function(size){
43544 if (settings[size]) {
43545 cfg.cls += ' col-' + size + '-' + settings[size];
43549 this.store = new Roo.data.Store({
43550 proxy : new Roo.data.MemoryProxy({}),
43551 reader : new Roo.data.JsonReader({
43562 'name' : 'dialCode',
43566 'name' : 'priority',
43570 'name' : 'areaCodes',
43577 if(!this.preferedCountries) {
43578 this.preferedCountries = [
43585 var p = this.preferedCountries.reverse();
43588 for (var i = 0; i < p.length; i++) {
43589 for (var j = 0; j < this.allCountries.length; j++) {
43590 if(this.allCountries[j].iso2 == p[i]) {
43591 var t = this.allCountries[j];
43592 this.allCountries.splice(j,1);
43593 this.allCountries.unshift(t);
43599 this.store.proxy.data = {
43601 data: this.allCountries
43607 initEvents : function()
43610 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43612 this.indicator = this.indicatorEl();
43613 this.flag = this.flagEl();
43614 this.dialCodeHolder = this.dialCodeHolderEl();
43616 this.trigger = this.el.select('div.flag-box',true).first();
43617 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43622 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43623 _this.list.setWidth(lw);
43626 this.list.on('mouseover', this.onViewOver, this);
43627 this.list.on('mousemove', this.onViewMove, this);
43628 this.inputEl().on("keyup", this.onKeyUp, this);
43629 this.inputEl().on("keypress", this.onKeyPress, this);
43631 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43633 this.view = new Roo.View(this.list, this.tpl, {
43634 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43637 this.view.on('click', this.onViewClick, this);
43638 this.setValue(this.defaultDialCode);
43641 onTriggerClick : function(e)
43643 Roo.log('trigger click');
43648 if(this.isExpanded()){
43650 this.hasFocus = false;
43652 this.store.load({});
43653 this.hasFocus = true;
43658 isExpanded : function()
43660 return this.list.isVisible();
43663 collapse : function()
43665 if(!this.isExpanded()){
43669 Roo.get(document).un('mousedown', this.collapseIf, this);
43670 Roo.get(document).un('mousewheel', this.collapseIf, this);
43671 this.fireEvent('collapse', this);
43675 expand : function()
43679 if(this.isExpanded() || !this.hasFocus){
43683 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43684 this.list.setWidth(lw);
43687 this.restrictHeight();
43689 Roo.get(document).on('mousedown', this.collapseIf, this);
43690 Roo.get(document).on('mousewheel', this.collapseIf, this);
43692 this.fireEvent('expand', this);
43695 restrictHeight : function()
43697 this.list.alignTo(this.inputEl(), this.listAlign);
43698 this.list.alignTo(this.inputEl(), this.listAlign);
43701 onViewOver : function(e, t)
43703 if(this.inKeyMode){
43706 var item = this.view.findItemFromChild(t);
43709 var index = this.view.indexOf(item);
43710 this.select(index, false);
43715 onViewClick : function(view, doFocus, el, e)
43717 var index = this.view.getSelectedIndexes()[0];
43719 var r = this.store.getAt(index);
43722 this.onSelect(r, index);
43724 if(doFocus !== false && !this.blockFocus){
43725 this.inputEl().focus();
43729 onViewMove : function(e, t)
43731 this.inKeyMode = false;
43734 select : function(index, scrollIntoView)
43736 this.selectedIndex = index;
43737 this.view.select(index);
43738 if(scrollIntoView !== false){
43739 var el = this.view.getNode(index);
43741 this.list.scrollChildIntoView(el, false);
43746 createList : function()
43748 this.list = Roo.get(document.body).createChild({
43750 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43751 style: 'display:none'
43754 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43757 collapseIf : function(e)
43759 var in_combo = e.within(this.el);
43760 var in_list = e.within(this.list);
43761 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43763 if (in_combo || in_list || is_list) {
43769 onSelect : function(record, index)
43771 if(this.fireEvent('beforeselect', this, record, index) !== false){
43773 this.setFlagClass(record.data.iso2);
43774 this.setDialCode(record.data.dialCode);
43775 this.hasFocus = false;
43777 this.fireEvent('select', this, record, index);
43781 flagEl : function()
43783 var flag = this.el.select('div.flag',true).first();
43790 dialCodeHolderEl : function()
43792 var d = this.el.select('input.dial-code-holder',true).first();
43799 setDialCode : function(v)
43801 this.dialCodeHolder.dom.value = '+'+v;
43804 setFlagClass : function(n)
43806 this.flag.dom.className = 'flag '+n;
43809 getValue : function()
43811 var v = this.inputEl().getValue();
43812 if(this.dialCodeHolder) {
43813 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43818 setValue : function(v)
43820 var d = this.getDialCode(v);
43822 //invalid dial code
43823 if(v.length == 0 || !d || d.length == 0) {
43825 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43826 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43832 this.setFlagClass(this.dialCodeMapping[d].iso2);
43833 this.setDialCode(d);
43834 this.inputEl().dom.value = v.replace('+'+d,'');
43835 this.hiddenEl().dom.value = this.getValue();
43840 getDialCode : function(v)
43844 if (v.length == 0) {
43845 return this.dialCodeHolder.dom.value;
43849 if (v.charAt(0) != "+") {
43852 var numericChars = "";
43853 for (var i = 1; i < v.length; i++) {
43854 var c = v.charAt(i);
43857 if (this.dialCodeMapping[numericChars]) {
43858 dialCode = v.substr(1, i);
43860 if (numericChars.length == 4) {
43870 this.setValue(this.defaultDialCode);
43874 hiddenEl : function()
43876 return this.el.select('input.hidden-tel-input',true).first();
43879 // after setting val
43880 onKeyUp : function(e){
43881 this.setValue(this.getValue());
43884 onKeyPress : function(e){
43885 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43892 * @class Roo.bootstrap.form.MoneyField
43893 * @extends Roo.bootstrap.form.ComboBox
43894 * Bootstrap MoneyField class
43897 * Create a new MoneyField.
43898 * @param {Object} config Configuration options
43901 Roo.bootstrap.form.MoneyField = function(config) {
43903 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43907 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43910 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43912 allowDecimals : true,
43914 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43916 decimalSeparator : ".",
43918 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43920 decimalPrecision : 0,
43922 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43924 allowNegative : true,
43926 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43930 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43932 minValue : Number.NEGATIVE_INFINITY,
43934 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43936 maxValue : Number.MAX_VALUE,
43938 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43940 minText : "The minimum value for this field is {0}",
43942 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43944 maxText : "The maximum value for this field is {0}",
43946 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43947 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43949 nanText : "{0} is not a valid number",
43951 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43955 * @cfg {String} defaults currency of the MoneyField
43956 * value should be in lkey
43958 defaultCurrency : false,
43960 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43962 thousandsDelimiter : false,
43964 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43973 * @cfg {Roo.data.Store} store Store to lookup currency??
43977 getAutoCreate : function()
43979 var align = this.labelAlign || this.parentLabelAlign();
43991 cls : 'form-control roo-money-amount-input',
43992 autocomplete: 'new-password'
43995 var hiddenInput = {
43999 cls: 'hidden-number-input'
44002 if(this.max_length) {
44003 input.maxlength = this.max_length;
44007 hiddenInput.name = this.name;
44010 if (this.disabled) {
44011 input.disabled = true;
44014 var clg = 12 - this.inputlg;
44015 var cmd = 12 - this.inputmd;
44016 var csm = 12 - this.inputsm;
44017 var cxs = 12 - this.inputxs;
44021 cls : 'row roo-money-field',
44025 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44029 cls: 'roo-select2-container input-group',
44033 cls : 'form-control roo-money-currency-input',
44034 autocomplete: 'new-password',
44036 name : this.currencyName
44040 cls : 'input-group-addon',
44054 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44058 cls: this.hasFeedback ? 'has-feedback' : '',
44069 if (this.fieldLabel.length) {
44072 tooltip: 'This field is required'
44078 cls: 'control-label',
44084 html: this.fieldLabel
44087 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44093 if(this.indicatorpos == 'right') {
44094 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44101 if(align == 'left') {
44109 if(this.labelWidth > 12){
44110 label.style = "width: " + this.labelWidth + 'px';
44112 if(this.labelWidth < 13 && this.labelmd == 0){
44113 this.labelmd = this.labelWidth;
44115 if(this.labellg > 0){
44116 label.cls += ' col-lg-' + this.labellg;
44117 input.cls += ' col-lg-' + (12 - this.labellg);
44119 if(this.labelmd > 0){
44120 label.cls += ' col-md-' + this.labelmd;
44121 container.cls += ' col-md-' + (12 - this.labelmd);
44123 if(this.labelsm > 0){
44124 label.cls += ' col-sm-' + this.labelsm;
44125 container.cls += ' col-sm-' + (12 - this.labelsm);
44127 if(this.labelxs > 0){
44128 label.cls += ' col-xs-' + this.labelxs;
44129 container.cls += ' col-xs-' + (12 - this.labelxs);
44140 var settings = this;
44142 ['xs','sm','md','lg'].map(function(size){
44143 if (settings[size]) {
44144 cfg.cls += ' col-' + size + '-' + settings[size];
44151 initEvents : function()
44153 this.indicator = this.indicatorEl();
44155 this.initCurrencyEvent();
44157 this.initNumberEvent();
44160 initCurrencyEvent : function()
44163 throw "can not find store for combo";
44166 this.store = Roo.factory(this.store, Roo.data);
44167 this.store.parent = this;
44171 this.triggerEl = this.el.select('.input-group-addon', true).first();
44173 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44178 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44179 _this.list.setWidth(lw);
44182 this.list.on('mouseover', this.onViewOver, this);
44183 this.list.on('mousemove', this.onViewMove, this);
44184 this.list.on('scroll', this.onViewScroll, this);
44187 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44190 this.view = new Roo.View(this.list, this.tpl, {
44191 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44194 this.view.on('click', this.onViewClick, this);
44196 this.store.on('beforeload', this.onBeforeLoad, this);
44197 this.store.on('load', this.onLoad, this);
44198 this.store.on('loadexception', this.onLoadException, this);
44200 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44201 "up" : function(e){
44202 this.inKeyMode = true;
44206 "down" : function(e){
44207 if(!this.isExpanded()){
44208 this.onTriggerClick();
44210 this.inKeyMode = true;
44215 "enter" : function(e){
44218 if(this.fireEvent("specialkey", this, e)){
44219 this.onViewClick(false);
44225 "esc" : function(e){
44229 "tab" : function(e){
44232 if(this.fireEvent("specialkey", this, e)){
44233 this.onViewClick(false);
44241 doRelay : function(foo, bar, hname){
44242 if(hname == 'down' || this.scope.isExpanded()){
44243 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44251 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44255 initNumberEvent : function(e)
44257 this.inputEl().on("keydown" , this.fireKey, this);
44258 this.inputEl().on("focus", this.onFocus, this);
44259 this.inputEl().on("blur", this.onBlur, this);
44261 this.inputEl().relayEvent('keyup', this);
44263 if(this.indicator){
44264 this.indicator.addClass('invisible');
44267 this.originalValue = this.getValue();
44269 if(this.validationEvent == 'keyup'){
44270 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44271 this.inputEl().on('keyup', this.filterValidation, this);
44273 else if(this.validationEvent !== false){
44274 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44277 if(this.selectOnFocus){
44278 this.on("focus", this.preFocus, this);
44281 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44282 this.inputEl().on("keypress", this.filterKeys, this);
44284 this.inputEl().relayEvent('keypress', this);
44287 var allowed = "0123456789";
44289 if(this.allowDecimals){
44290 allowed += this.decimalSeparator;
44293 if(this.allowNegative){
44297 if(this.thousandsDelimiter) {
44301 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44303 var keyPress = function(e){
44305 var k = e.getKey();
44307 var c = e.getCharCode();
44310 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44311 allowed.indexOf(String.fromCharCode(c)) === -1
44317 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44321 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44326 this.inputEl().on("keypress", keyPress, this);
44330 onTriggerClick : function(e)
44337 this.loadNext = false;
44339 if(this.isExpanded()){
44344 this.hasFocus = true;
44346 if(this.triggerAction == 'all') {
44347 this.doQuery(this.allQuery, true);
44351 this.doQuery(this.getRawValue());
44354 getCurrency : function()
44356 var v = this.currencyEl().getValue();
44361 restrictHeight : function()
44363 this.list.alignTo(this.currencyEl(), this.listAlign);
44364 this.list.alignTo(this.currencyEl(), this.listAlign);
44367 onViewClick : function(view, doFocus, el, e)
44369 var index = this.view.getSelectedIndexes()[0];
44371 var r = this.store.getAt(index);
44374 this.onSelect(r, index);
44378 onSelect : function(record, index){
44380 if(this.fireEvent('beforeselect', this, record, index) !== false){
44382 this.setFromCurrencyData(index > -1 ? record.data : false);
44386 this.fireEvent('select', this, record, index);
44390 setFromCurrencyData : function(o)
44394 this.lastCurrency = o;
44396 if (this.currencyField) {
44397 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44399 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44402 this.lastSelectionText = currency;
44404 //setting default currency
44405 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44406 this.setCurrency(this.defaultCurrency);
44410 this.setCurrency(currency);
44413 setFromData : function(o)
44417 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44419 this.setFromCurrencyData(c);
44424 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44426 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44429 this.setValue(value);
44433 setCurrency : function(v)
44435 this.currencyValue = v;
44438 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44443 setValue : function(v)
44445 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44451 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44453 this.inputEl().dom.value = (v == '') ? '' :
44454 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44456 if(!this.allowZero && v === '0') {
44457 this.hiddenEl().dom.value = '';
44458 this.inputEl().dom.value = '';
44465 getRawValue : function()
44467 var v = this.inputEl().getValue();
44472 getValue : function()
44474 return this.fixPrecision(this.parseValue(this.getRawValue()));
44477 parseValue : function(value)
44479 if(this.thousandsDelimiter) {
44481 r = new RegExp(",", "g");
44482 value = value.replace(r, "");
44485 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44486 return isNaN(value) ? '' : value;
44490 fixPrecision : function(value)
44492 if(this.thousandsDelimiter) {
44494 r = new RegExp(",", "g");
44495 value = value.replace(r, "");
44498 var nan = isNaN(value);
44500 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44501 return nan ? '' : value;
44503 return parseFloat(value).toFixed(this.decimalPrecision);
44506 decimalPrecisionFcn : function(v)
44508 return Math.floor(v);
44511 validateValue : function(value)
44513 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44517 var num = this.parseValue(value);
44520 this.markInvalid(String.format(this.nanText, value));
44524 if(num < this.minValue){
44525 this.markInvalid(String.format(this.minText, this.minValue));
44529 if(num > this.maxValue){
44530 this.markInvalid(String.format(this.maxText, this.maxValue));
44537 validate : function()
44539 if(this.disabled || this.allowBlank){
44544 var currency = this.getCurrency();
44546 if(this.validateValue(this.getRawValue()) && currency.length){
44551 this.markInvalid();
44555 getName: function()
44560 beforeBlur : function()
44566 var v = this.parseValue(this.getRawValue());
44573 onBlur : function()
44577 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44578 //this.el.removeClass(this.focusClass);
44581 this.hasFocus = false;
44583 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44587 var v = this.getValue();
44589 if(String(v) !== String(this.startValue)){
44590 this.fireEvent('change', this, v, this.startValue);
44593 this.fireEvent("blur", this);
44596 inputEl : function()
44598 return this.el.select('.roo-money-amount-input', true).first();
44601 currencyEl : function()
44603 return this.el.select('.roo-money-currency-input', true).first();
44606 hiddenEl : function()
44608 return this.el.select('input.hidden-number-input',true).first();
44612 * @class Roo.bootstrap.BezierSignature
44613 * @extends Roo.bootstrap.Component
44614 * Bootstrap BezierSignature class
44615 * This script refer to:
44616 * Title: Signature Pad
44618 * Availability: https://github.com/szimek/signature_pad
44621 * Create a new BezierSignature
44622 * @param {Object} config The config object
44625 Roo.bootstrap.BezierSignature = function(config){
44626 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44632 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44639 mouse_btn_down: true,
44642 * @cfg {int} canvas height
44644 canvas_height: '200px',
44647 * @cfg {float|function} Radius of a single dot.
44652 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44657 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44662 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44667 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44672 * @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.
44674 bg_color: 'rgba(0, 0, 0, 0)',
44677 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44679 dot_color: 'black',
44682 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44684 velocity_filter_weight: 0.7,
44687 * @cfg {function} Callback when stroke begin.
44692 * @cfg {function} Callback when stroke end.
44696 getAutoCreate : function()
44698 var cls = 'roo-signature column';
44701 cls += ' ' + this.cls;
44711 for(var i = 0; i < col_sizes.length; i++) {
44712 if(this[col_sizes[i]]) {
44713 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44723 cls: 'roo-signature-body',
44727 cls: 'roo-signature-body-canvas',
44728 height: this.canvas_height,
44729 width: this.canvas_width
44736 style: 'display: none'
44744 initEvents: function()
44746 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44748 var canvas = this.canvasEl();
44750 // mouse && touch event swapping...
44751 canvas.dom.style.touchAction = 'none';
44752 canvas.dom.style.msTouchAction = 'none';
44754 this.mouse_btn_down = false;
44755 canvas.on('mousedown', this._handleMouseDown, this);
44756 canvas.on('mousemove', this._handleMouseMove, this);
44757 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44759 if (window.PointerEvent) {
44760 canvas.on('pointerdown', this._handleMouseDown, this);
44761 canvas.on('pointermove', this._handleMouseMove, this);
44762 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44765 if ('ontouchstart' in window) {
44766 canvas.on('touchstart', this._handleTouchStart, this);
44767 canvas.on('touchmove', this._handleTouchMove, this);
44768 canvas.on('touchend', this._handleTouchEnd, this);
44771 Roo.EventManager.onWindowResize(this.resize, this, true);
44773 // file input event
44774 this.fileEl().on('change', this.uploadImage, this);
44781 resize: function(){
44783 var canvas = this.canvasEl().dom;
44784 var ctx = this.canvasElCtx();
44785 var img_data = false;
44787 if(canvas.width > 0) {
44788 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44790 // setting canvas width will clean img data
44793 var style = window.getComputedStyle ?
44794 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44796 var padding_left = parseInt(style.paddingLeft) || 0;
44797 var padding_right = parseInt(style.paddingRight) || 0;
44799 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44802 ctx.putImageData(img_data, 0, 0);
44806 _handleMouseDown: function(e)
44808 if (e.browserEvent.which === 1) {
44809 this.mouse_btn_down = true;
44810 this.strokeBegin(e);
44814 _handleMouseMove: function (e)
44816 if (this.mouse_btn_down) {
44817 this.strokeMoveUpdate(e);
44821 _handleMouseUp: function (e)
44823 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44824 this.mouse_btn_down = false;
44829 _handleTouchStart: function (e) {
44831 e.preventDefault();
44832 if (e.browserEvent.targetTouches.length === 1) {
44833 // var touch = e.browserEvent.changedTouches[0];
44834 // this.strokeBegin(touch);
44836 this.strokeBegin(e); // assume e catching the correct xy...
44840 _handleTouchMove: function (e) {
44841 e.preventDefault();
44842 // var touch = event.targetTouches[0];
44843 // _this._strokeMoveUpdate(touch);
44844 this.strokeMoveUpdate(e);
44847 _handleTouchEnd: function (e) {
44848 var wasCanvasTouched = e.target === this.canvasEl().dom;
44849 if (wasCanvasTouched) {
44850 e.preventDefault();
44851 // var touch = event.changedTouches[0];
44852 // _this._strokeEnd(touch);
44857 reset: function () {
44858 this._lastPoints = [];
44859 this._lastVelocity = 0;
44860 this._lastWidth = (this.min_width + this.max_width) / 2;
44861 this.canvasElCtx().fillStyle = this.dot_color;
44864 strokeMoveUpdate: function(e)
44866 this.strokeUpdate(e);
44868 if (this.throttle) {
44869 this.throttleStroke(this.strokeUpdate, this.throttle);
44872 this.strokeUpdate(e);
44876 strokeBegin: function(e)
44878 var newPointGroup = {
44879 color: this.dot_color,
44883 if (typeof this.onBegin === 'function') {
44887 this.curve_data.push(newPointGroup);
44889 this.strokeUpdate(e);
44892 strokeUpdate: function(e)
44894 var rect = this.canvasEl().dom.getBoundingClientRect();
44895 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44896 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44897 var lastPoints = lastPointGroup.points;
44898 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44899 var isLastPointTooClose = lastPoint
44900 ? point.distanceTo(lastPoint) <= this.min_distance
44902 var color = lastPointGroup.color;
44903 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44904 var curve = this.addPoint(point);
44906 this.drawDot({color: color, point: point});
44909 this.drawCurve({color: color, curve: curve});
44919 strokeEnd: function(e)
44921 this.strokeUpdate(e);
44922 if (typeof this.onEnd === 'function') {
44927 addPoint: function (point) {
44928 var _lastPoints = this._lastPoints;
44929 _lastPoints.push(point);
44930 if (_lastPoints.length > 2) {
44931 if (_lastPoints.length === 3) {
44932 _lastPoints.unshift(_lastPoints[0]);
44934 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44935 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44936 _lastPoints.shift();
44942 calculateCurveWidths: function (startPoint, endPoint) {
44943 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44944 (1 - this.velocity_filter_weight) * this._lastVelocity;
44946 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44949 start: this._lastWidth
44952 this._lastVelocity = velocity;
44953 this._lastWidth = newWidth;
44957 drawDot: function (_a) {
44958 var color = _a.color, point = _a.point;
44959 var ctx = this.canvasElCtx();
44960 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44962 this.drawCurveSegment(point.x, point.y, width);
44964 ctx.fillStyle = color;
44968 drawCurve: function (_a) {
44969 var color = _a.color, curve = _a.curve;
44970 var ctx = this.canvasElCtx();
44971 var widthDelta = curve.endWidth - curve.startWidth;
44972 var drawSteps = Math.floor(curve.length()) * 2;
44974 ctx.fillStyle = color;
44975 for (var i = 0; i < drawSteps; i += 1) {
44976 var t = i / drawSteps;
44982 var x = uuu * curve.startPoint.x;
44983 x += 3 * uu * t * curve.control1.x;
44984 x += 3 * u * tt * curve.control2.x;
44985 x += ttt * curve.endPoint.x;
44986 var y = uuu * curve.startPoint.y;
44987 y += 3 * uu * t * curve.control1.y;
44988 y += 3 * u * tt * curve.control2.y;
44989 y += ttt * curve.endPoint.y;
44990 var width = curve.startWidth + ttt * widthDelta;
44991 this.drawCurveSegment(x, y, width);
44997 drawCurveSegment: function (x, y, width) {
44998 var ctx = this.canvasElCtx();
45000 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45001 this.is_empty = false;
45006 var ctx = this.canvasElCtx();
45007 var canvas = this.canvasEl().dom;
45008 ctx.fillStyle = this.bg_color;
45009 ctx.clearRect(0, 0, canvas.width, canvas.height);
45010 ctx.fillRect(0, 0, canvas.width, canvas.height);
45011 this.curve_data = [];
45013 this.is_empty = true;
45018 return this.el.select('input',true).first();
45021 canvasEl: function()
45023 return this.el.select('canvas',true).first();
45026 canvasElCtx: function()
45028 return this.el.select('canvas',true).first().dom.getContext('2d');
45031 getImage: function(type)
45033 if(this.is_empty) {
45038 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45041 drawFromImage: function(img_src)
45043 var img = new Image();
45045 img.onload = function(){
45046 this.canvasElCtx().drawImage(img, 0, 0);
45051 this.is_empty = false;
45054 selectImage: function()
45056 this.fileEl().dom.click();
45059 uploadImage: function(e)
45061 var reader = new FileReader();
45063 reader.onload = function(e){
45064 var img = new Image();
45065 img.onload = function(){
45067 this.canvasElCtx().drawImage(img, 0, 0);
45069 img.src = e.target.result;
45072 reader.readAsDataURL(e.target.files[0]);
45075 // Bezier Point Constructor
45076 Point: (function () {
45077 function Point(x, y, time) {
45080 this.time = time || Date.now();
45082 Point.prototype.distanceTo = function (start) {
45083 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45085 Point.prototype.equals = function (other) {
45086 return this.x === other.x && this.y === other.y && this.time === other.time;
45088 Point.prototype.velocityFrom = function (start) {
45089 return this.time !== start.time
45090 ? this.distanceTo(start) / (this.time - start.time)
45097 // Bezier Constructor
45098 Bezier: (function () {
45099 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45100 this.startPoint = startPoint;
45101 this.control2 = control2;
45102 this.control1 = control1;
45103 this.endPoint = endPoint;
45104 this.startWidth = startWidth;
45105 this.endWidth = endWidth;
45107 Bezier.fromPoints = function (points, widths, scope) {
45108 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45109 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45110 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45112 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45113 var dx1 = s1.x - s2.x;
45114 var dy1 = s1.y - s2.y;
45115 var dx2 = s2.x - s3.x;
45116 var dy2 = s2.y - s3.y;
45117 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45118 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45119 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45120 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45121 var dxm = m1.x - m2.x;
45122 var dym = m1.y - m2.y;
45123 var k = l2 / (l1 + l2);
45124 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45125 var tx = s2.x - cm.x;
45126 var ty = s2.y - cm.y;
45128 c1: new scope.Point(m1.x + tx, m1.y + ty),
45129 c2: new scope.Point(m2.x + tx, m2.y + ty)
45132 Bezier.prototype.length = function () {
45137 for (var i = 0; i <= steps; i += 1) {
45139 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45140 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45142 var xdiff = cx - px;
45143 var ydiff = cy - py;
45144 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45151 Bezier.prototype.point = function (t, start, c1, c2, end) {
45152 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45153 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45154 + (3.0 * c2 * (1.0 - t) * t * t)
45155 + (end * t * t * t);
45160 throttleStroke: function(fn, wait) {
45161 if (wait === void 0) { wait = 250; }
45163 var timeout = null;
45167 var later = function () {
45168 previous = Date.now();
45170 result = fn.apply(storedContext, storedArgs);
45172 storedContext = null;
45176 return function wrapper() {
45178 for (var _i = 0; _i < arguments.length; _i++) {
45179 args[_i] = arguments[_i];
45181 var now = Date.now();
45182 var remaining = wait - (now - previous);
45183 storedContext = this;
45185 if (remaining <= 0 || remaining > wait) {
45187 clearTimeout(timeout);
45191 result = fn.apply(storedContext, storedArgs);
45193 storedContext = null;
45197 else if (!timeout) {
45198 timeout = window.setTimeout(later, remaining);
45208 // old names for form elements
45209 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45210 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45211 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45212 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45213 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45214 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45215 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45216 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45217 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45218 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45219 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45220 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45221 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45222 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45223 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45224 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45225 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45226 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45227 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45228 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45229 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45230 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45231 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45232 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45233 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45234 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45236 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45237 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45239 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45240 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45242 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45243 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45244 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45245 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator