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);
16671 var record = new Record(values, id);
16673 records[i] = record;
16679 totalRecords : totalRecords
16682 // used when loading children.. @see loadDataFromChildren
16683 toLoadData: function(rec)
16685 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16686 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16687 return { data : data, total : data.length };
16692 * Ext JS Library 1.1.1
16693 * Copyright(c) 2006-2007, Ext JS, LLC.
16695 * Originally Released Under LGPL - original licence link has changed is not relivant.
16698 * <script type="text/javascript">
16702 * @class Roo.data.ArrayReader
16703 * @extends Roo.data.DataReader
16704 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16705 * Each element of that Array represents a row of data fields. The
16706 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16707 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16711 var RecordDef = Roo.data.Record.create([
16712 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16713 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16715 var myReader = new Roo.data.ArrayReader({
16716 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16720 * This would consume an Array like this:
16722 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16726 * Create a new JsonReader
16727 * @param {Object} meta Metadata configuration options.
16728 * @param {Object|Array} recordType Either an Array of field definition objects
16730 * @cfg {Array} fields Array of field definition objects
16731 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16732 * as specified to {@link Roo.data.Record#create},
16733 * or an {@link Roo.data.Record} object
16736 * created using {@link Roo.data.Record#create}.
16738 Roo.data.ArrayReader = function(meta, recordType)
16740 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16746 * Create a data block containing Roo.data.Records from an XML document.
16747 * @param {Object} o An Array of row objects which represents the dataset.
16748 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16749 * a cache of Roo.data.Records.
16751 readRecords : function(o)
16753 var sid = this.meta ? this.meta.id : null;
16754 var recordType = this.recordType, fields = recordType.prototype.fields;
16757 for(var i = 0; i < root.length; i++){
16760 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16761 for(var j = 0, jlen = fields.length; j < jlen; j++){
16762 var f = fields.items[j];
16763 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16764 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16766 values[f.name] = v;
16768 var record = new recordType(values, id);
16770 records[records.length] = record;
16774 totalRecords : records.length
16777 // used when loading children.. @see loadDataFromChildren
16778 toLoadData: function(rec)
16780 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16781 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16792 * @class Roo.bootstrap.form.ComboBox
16793 * @extends Roo.bootstrap.form.TriggerField
16794 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16795 * @cfg {Boolean} append (true|false) default false
16796 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16797 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16798 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16799 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16800 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16801 * @cfg {Boolean} animate default true
16802 * @cfg {Boolean} emptyResultText only for touch device
16803 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16804 * @cfg {String} emptyTitle default ''
16805 * @cfg {Number} width fixed with? experimental
16807 * Create a new ComboBox.
16808 * @param {Object} config Configuration options
16810 Roo.bootstrap.form.ComboBox = function(config){
16811 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16815 * Fires when the dropdown list is expanded
16816 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16821 * Fires when the dropdown list is collapsed
16822 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16826 * @event beforeselect
16827 * Fires before a list item is selected. Return false to cancel the selection.
16828 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16829 * @param {Roo.data.Record} record The data record returned from the underlying store
16830 * @param {Number} index The index of the selected item in the dropdown list
16832 'beforeselect' : true,
16835 * Fires when a list item is selected
16836 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16837 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16838 * @param {Number} index The index of the selected item in the dropdown list
16842 * @event beforequery
16843 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16844 * The event object passed has these properties:
16845 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16846 * @param {String} query The query
16847 * @param {Boolean} forceAll true to force "all" query
16848 * @param {Boolean} cancel true to cancel the query
16849 * @param {Object} e The query event object
16851 'beforequery': true,
16854 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16855 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16861 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16867 * Fires when the remove value from the combobox array
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872 * @event afterremove
16873 * Fires when the remove value from the combobox array
16874 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876 'afterremove' : true,
16878 * @event specialfilter
16879 * Fires when specialfilter
16880 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16882 'specialfilter' : true,
16885 * Fires when tick the element
16886 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890 * @event touchviewdisplay
16891 * Fires when touch view require special display (default is using displayField)
16892 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16893 * @param {Object} cfg set html .
16895 'touchviewdisplay' : true
16900 this.tickItems = [];
16902 this.selectedIndex = -1;
16903 if(this.mode == 'local'){
16904 if(config.queryDelay === undefined){
16905 this.queryDelay = 10;
16907 if(config.minChars === undefined){
16913 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16916 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16917 * rendering into an Roo.Editor, defaults to false)
16920 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16921 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16924 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16927 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16928 * the dropdown list (defaults to undefined, with no header element)
16932 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16936 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16938 listWidth: undefined,
16940 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16941 * mode = 'remote' or 'text' if mode = 'local')
16943 displayField: undefined,
16946 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16947 * mode = 'remote' or 'value' if mode = 'local').
16948 * Note: use of a valueField requires the user make a selection
16949 * in order for a value to be mapped.
16951 valueField: undefined,
16953 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16958 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16959 * field's data value (defaults to the underlying DOM element's name)
16961 hiddenName: undefined,
16963 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16967 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16969 selectedClass: 'active',
16972 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16976 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16977 * anchor positions (defaults to 'tl-bl')
16979 listAlign: 'tl-bl?',
16981 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16985 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16986 * query specified by the allQuery config option (defaults to 'query')
16988 triggerAction: 'query',
16990 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16991 * (defaults to 4, does not apply if editable = false)
16995 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16996 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17000 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17001 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17005 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17006 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17010 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17011 * when editable = true (defaults to false)
17013 selectOnFocus:false,
17015 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17017 queryParam: 'query',
17019 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17020 * when mode = 'remote' (defaults to 'Loading...')
17022 loadingText: 'Loading...',
17024 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17028 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17032 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17033 * traditional select (defaults to true)
17037 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17041 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17045 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17046 * listWidth has a higher value)
17050 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17051 * allow the user to set arbitrary text into the field (defaults to false)
17053 forceSelection:false,
17055 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17056 * if typeAhead = true (defaults to 250)
17058 typeAheadDelay : 250,
17060 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17061 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17063 valueNotFoundText : undefined,
17065 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17067 blockFocus : false,
17070 * @cfg {Boolean} disableClear Disable showing of clear button.
17072 disableClear : false,
17074 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17076 alwaysQuery : false,
17079 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17084 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17086 invalidClass : "has-warning",
17089 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17091 validClass : "has-success",
17094 * @cfg {Boolean} specialFilter (true|false) special filter default false
17096 specialFilter : false,
17099 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17101 mobileTouchView : true,
17104 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17106 useNativeIOS : false,
17109 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17111 mobile_restrict_height : false,
17113 ios_options : false,
17125 btnPosition : 'right',
17126 triggerList : true,
17127 showToggleBtn : true,
17129 emptyResultText: 'Empty',
17130 triggerText : 'Select',
17134 // element that contains real text value.. (when hidden is used..)
17136 getAutoCreate : function()
17141 * Render classic select for iso
17144 if(Roo.isIOS && this.useNativeIOS){
17145 cfg = this.getAutoCreateNativeIOS();
17153 if(Roo.isTouch && this.mobileTouchView){
17154 cfg = this.getAutoCreateTouchView();
17161 if(!this.tickable){
17162 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17167 * ComboBox with tickable selections
17170 var align = this.labelAlign || this.parentLabelAlign();
17173 cls : 'form-group roo-combobox-tickable' //input-group
17176 var btn_text_select = '';
17177 var btn_text_done = '';
17178 var btn_text_cancel = '';
17180 if (this.btn_text_show) {
17181 btn_text_select = 'Select';
17182 btn_text_done = 'Done';
17183 btn_text_cancel = 'Cancel';
17188 cls : 'tickable-buttons',
17193 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17194 //html : this.triggerText
17195 html: btn_text_select
17201 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17203 html: btn_text_done
17209 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17211 html: btn_text_cancel
17217 buttons.cn.unshift({
17219 cls: 'roo-select2-search-field-input'
17225 Roo.each(buttons.cn, function(c){
17227 c.cls += ' btn-' + _this.size;
17230 if (_this.disabled) {
17237 style : 'display: contents',
17242 cls: 'form-hidden-field'
17246 cls: 'roo-select2-choices',
17250 cls: 'roo-select2-search-field',
17261 cls: 'roo-select2-container input-group roo-select2-container-multi',
17267 // cls: 'typeahead typeahead-long dropdown-menu',
17268 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17273 if(this.hasFeedback && !this.allowBlank){
17277 cls: 'glyphicon form-control-feedback'
17280 combobox.cn.push(feedback);
17287 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17288 tooltip : 'This field is required'
17290 if (Roo.bootstrap.version == 4) {
17293 style : 'display:none'
17296 if (align ==='left' && this.fieldLabel.length) {
17298 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17305 cls : 'control-label col-form-label',
17306 html : this.fieldLabel
17318 var labelCfg = cfg.cn[1];
17319 var contentCfg = cfg.cn[2];
17322 if(this.indicatorpos == 'right'){
17328 cls : 'control-label col-form-label',
17332 html : this.fieldLabel
17348 labelCfg = cfg.cn[0];
17349 contentCfg = cfg.cn[1];
17353 if(this.labelWidth > 12){
17354 labelCfg.style = "width: " + this.labelWidth + 'px';
17356 if(this.width * 1 > 0){
17357 contentCfg.style = "width: " + this.width + 'px';
17359 if(this.labelWidth < 13 && this.labelmd == 0){
17360 this.labelmd = this.labelWidth;
17363 if(this.labellg > 0){
17364 labelCfg.cls += ' col-lg-' + this.labellg;
17365 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17368 if(this.labelmd > 0){
17369 labelCfg.cls += ' col-md-' + this.labelmd;
17370 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17373 if(this.labelsm > 0){
17374 labelCfg.cls += ' col-sm-' + this.labelsm;
17375 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17378 if(this.labelxs > 0){
17379 labelCfg.cls += ' col-xs-' + this.labelxs;
17380 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17384 } else if ( this.fieldLabel.length) {
17385 // Roo.log(" label");
17390 //cls : 'input-group-addon',
17391 html : this.fieldLabel
17396 if(this.indicatorpos == 'right'){
17400 //cls : 'input-group-addon',
17401 html : this.fieldLabel
17411 // Roo.log(" no label && no align");
17418 ['xs','sm','md','lg'].map(function(size){
17419 if (settings[size]) {
17420 cfg.cls += ' col-' + size + '-' + settings[size];
17428 _initEventsCalled : false,
17431 initEvents: function()
17433 if (this._initEventsCalled) { // as we call render... prevent looping...
17436 this._initEventsCalled = true;
17439 throw "can not find store for combo";
17442 this.indicator = this.indicatorEl();
17444 this.store = Roo.factory(this.store, Roo.data);
17445 this.store.parent = this;
17447 // if we are building from html. then this element is so complex, that we can not really
17448 // use the rendered HTML.
17449 // so we have to trash and replace the previous code.
17450 if (Roo.XComponent.build_from_html) {
17451 // remove this element....
17452 var e = this.el.dom, k=0;
17453 while (e ) { e = e.previousSibling; ++k;}
17458 this.rendered = false;
17460 this.render(this.parent().getChildContainer(true), k);
17463 if(Roo.isIOS && this.useNativeIOS){
17464 this.initIOSView();
17472 if(Roo.isTouch && this.mobileTouchView){
17473 this.initTouchView();
17478 this.initTickableEvents();
17482 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17484 if(this.hiddenName){
17486 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17488 this.hiddenField.dom.value =
17489 this.hiddenValue !== undefined ? this.hiddenValue :
17490 this.value !== undefined ? this.value : '';
17492 // prevent input submission
17493 this.el.dom.removeAttribute('name');
17494 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17499 // this.el.dom.setAttribute('autocomplete', 'off');
17502 var cls = 'x-combo-list';
17504 //this.list = new Roo.Layer({
17505 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17511 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17512 _this.list.setWidth(lw);
17515 this.list.on('mouseover', this.onViewOver, this);
17516 this.list.on('mousemove', this.onViewMove, this);
17517 this.list.on('scroll', this.onViewScroll, this);
17520 this.list.swallowEvent('mousewheel');
17521 this.assetHeight = 0;
17524 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17525 this.assetHeight += this.header.getHeight();
17528 this.innerList = this.list.createChild({cls:cls+'-inner'});
17529 this.innerList.on('mouseover', this.onViewOver, this);
17530 this.innerList.on('mousemove', this.onViewMove, this);
17531 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17533 if(this.allowBlank && !this.pageSize && !this.disableClear){
17534 this.footer = this.list.createChild({cls:cls+'-ft'});
17535 this.pageTb = new Roo.Toolbar(this.footer);
17539 this.footer = this.list.createChild({cls:cls+'-ft'});
17540 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17541 {pageSize: this.pageSize});
17545 if (this.pageTb && this.allowBlank && !this.disableClear) {
17547 this.pageTb.add(new Roo.Toolbar.Fill(), {
17548 cls: 'x-btn-icon x-btn-clear',
17550 handler: function()
17553 _this.clearValue();
17554 _this.onSelect(false, -1);
17559 this.assetHeight += this.footer.getHeight();
17564 this.tpl = Roo.bootstrap.version == 4 ?
17565 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17566 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17569 this.view = new Roo.View(this.list, this.tpl, {
17570 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17572 //this.view.wrapEl.setDisplayed(false);
17573 this.view.on('click', this.onViewClick, this);
17576 this.store.on('beforeload', this.onBeforeLoad, this);
17577 this.store.on('load', this.onLoad, this);
17578 this.store.on('loadexception', this.onLoadException, this);
17580 if(this.resizable){
17581 this.resizer = new Roo.Resizable(this.list, {
17582 pinned:true, handles:'se'
17584 this.resizer.on('resize', function(r, w, h){
17585 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17586 this.listWidth = w;
17587 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17588 this.restrictHeight();
17590 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17593 if(!this.editable){
17594 this.editable = true;
17595 this.setEditable(false);
17600 if (typeof(this.events.add.listeners) != 'undefined') {
17602 this.addicon = this.wrap.createChild(
17603 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17605 this.addicon.on('click', function(e) {
17606 this.fireEvent('add', this);
17609 if (typeof(this.events.edit.listeners) != 'undefined') {
17611 this.editicon = this.wrap.createChild(
17612 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17613 if (this.addicon) {
17614 this.editicon.setStyle('margin-left', '40px');
17616 this.editicon.on('click', function(e) {
17618 // we fire even if inothing is selected..
17619 this.fireEvent('edit', this, this.lastData );
17625 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17626 "up" : function(e){
17627 this.inKeyMode = true;
17631 "down" : function(e){
17632 if(!this.isExpanded()){
17633 this.onTriggerClick();
17635 this.inKeyMode = true;
17640 "enter" : function(e){
17641 // this.onViewClick();
17645 if(this.fireEvent("specialkey", this, e)){
17646 this.onViewClick(false);
17652 "esc" : function(e){
17656 "tab" : function(e){
17659 if(this.fireEvent("specialkey", this, e)){
17660 this.onViewClick(false);
17668 doRelay : function(foo, bar, hname){
17669 if(hname == 'down' || this.scope.isExpanded()){
17670 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17679 this.queryDelay = Math.max(this.queryDelay || 10,
17680 this.mode == 'local' ? 10 : 250);
17683 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17685 if(this.typeAhead){
17686 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17688 if(this.editable !== false){
17689 this.inputEl().on("keyup", this.onKeyUp, this);
17691 if(this.forceSelection){
17692 this.inputEl().on('blur', this.doForce, this);
17696 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17697 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17701 initTickableEvents: function()
17705 if(this.hiddenName){
17707 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17709 this.hiddenField.dom.value =
17710 this.hiddenValue !== undefined ? this.hiddenValue :
17711 this.value !== undefined ? this.value : '';
17713 // prevent input submission
17714 this.el.dom.removeAttribute('name');
17715 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17720 // this.list = this.el.select('ul.dropdown-menu',true).first();
17722 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17723 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17724 if(this.triggerList){
17725 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17728 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17729 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17731 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17732 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17734 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17735 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17737 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17738 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17739 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17742 this.cancelBtn.hide();
17747 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17748 _this.list.setWidth(lw);
17751 this.list.on('mouseover', this.onViewOver, this);
17752 this.list.on('mousemove', this.onViewMove, this);
17754 this.list.on('scroll', this.onViewScroll, this);
17757 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17758 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17761 this.view = new Roo.View(this.list, this.tpl, {
17766 selectedClass: this.selectedClass
17769 //this.view.wrapEl.setDisplayed(false);
17770 this.view.on('click', this.onViewClick, this);
17774 this.store.on('beforeload', this.onBeforeLoad, this);
17775 this.store.on('load', this.onLoad, this);
17776 this.store.on('loadexception', this.onLoadException, this);
17779 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17780 "up" : function(e){
17781 this.inKeyMode = true;
17785 "down" : function(e){
17786 this.inKeyMode = true;
17790 "enter" : function(e){
17791 if(this.fireEvent("specialkey", this, e)){
17792 this.onViewClick(false);
17798 "esc" : function(e){
17799 this.onTickableFooterButtonClick(e, false, false);
17802 "tab" : function(e){
17803 this.fireEvent("specialkey", this, e);
17805 this.onTickableFooterButtonClick(e, false, false);
17812 doRelay : function(e, fn, key){
17813 if(this.scope.isExpanded()){
17814 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17823 this.queryDelay = Math.max(this.queryDelay || 10,
17824 this.mode == 'local' ? 10 : 250);
17827 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17829 if(this.typeAhead){
17830 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17833 if(this.editable !== false){
17834 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17837 this.indicator = this.indicatorEl();
17839 if(this.indicator){
17840 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17841 this.indicator.hide();
17846 onDestroy : function(){
17848 this.view.setStore(null);
17849 this.view.el.removeAllListeners();
17850 this.view.el.remove();
17851 this.view.purgeListeners();
17854 this.list.dom.innerHTML = '';
17858 this.store.un('beforeload', this.onBeforeLoad, this);
17859 this.store.un('load', this.onLoad, this);
17860 this.store.un('loadexception', this.onLoadException, this);
17862 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17866 fireKey : function(e){
17867 if(e.isNavKeyPress() && !this.list.isVisible()){
17868 this.fireEvent("specialkey", this, e);
17873 onResize: function(w, h)
17877 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17879 // if(typeof w != 'number'){
17880 // // we do not handle it!?!?
17883 // var tw = this.trigger.getWidth();
17884 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17885 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17887 // this.inputEl().setWidth( this.adjustWidth('input', x));
17889 // //this.trigger.setStyle('left', x+'px');
17891 // if(this.list && this.listWidth === undefined){
17892 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17893 // this.list.setWidth(lw);
17894 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17902 * Allow or prevent the user from directly editing the field text. If false is passed,
17903 * the user will only be able to select from the items defined in the dropdown list. This method
17904 * is the runtime equivalent of setting the 'editable' config option at config time.
17905 * @param {Boolean} value True to allow the user to directly edit the field text
17907 setEditable : function(value){
17908 if(value == this.editable){
17911 this.editable = value;
17913 this.inputEl().dom.setAttribute('readOnly', true);
17914 this.inputEl().on('mousedown', this.onTriggerClick, this);
17915 this.inputEl().addClass('x-combo-noedit');
17917 this.inputEl().dom.removeAttribute('readOnly');
17918 this.inputEl().un('mousedown', this.onTriggerClick, this);
17919 this.inputEl().removeClass('x-combo-noedit');
17925 onBeforeLoad : function(combo,opts){
17926 if(!this.hasFocus){
17930 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17932 this.restrictHeight();
17933 this.selectedIndex = -1;
17937 onLoad : function(){
17939 this.hasQuery = false;
17941 if(!this.hasFocus){
17945 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17946 this.loading.hide();
17949 if(this.store.getCount() > 0){
17952 this.restrictHeight();
17953 if(this.lastQuery == this.allQuery){
17954 if(this.editable && !this.tickable){
17955 this.inputEl().dom.select();
17959 !this.selectByValue(this.value, true) &&
17962 !this.store.lastOptions ||
17963 typeof(this.store.lastOptions.add) == 'undefined' ||
17964 this.store.lastOptions.add != true
17967 this.select(0, true);
17970 if(this.autoFocus){
17973 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17974 this.taTask.delay(this.typeAheadDelay);
17978 this.onEmptyResults();
17984 onLoadException : function()
17986 this.hasQuery = false;
17988 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17989 this.loading.hide();
17992 if(this.tickable && this.editable){
17997 // only causes errors at present
17998 //Roo.log(this.store.reader.jsonData);
17999 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18001 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18007 onTypeAhead : function(){
18008 if(this.store.getCount() > 0){
18009 var r = this.store.getAt(0);
18010 var newValue = r.data[this.displayField];
18011 var len = newValue.length;
18012 var selStart = this.getRawValue().length;
18014 if(selStart != len){
18015 this.setRawValue(newValue);
18016 this.selectText(selStart, newValue.length);
18022 onSelect : function(record, index){
18024 if(this.fireEvent('beforeselect', this, record, index) !== false){
18026 this.setFromData(index > -1 ? record.data : false);
18029 this.fireEvent('select', this, record, index);
18034 * Returns the currently selected field value or empty string if no value is set.
18035 * @return {String} value The selected value
18037 getValue : function()
18039 if(Roo.isIOS && this.useNativeIOS){
18040 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18044 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18047 if(this.valueField){
18048 return typeof this.value != 'undefined' ? this.value : '';
18050 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18054 getRawValue : function()
18056 if(Roo.isIOS && this.useNativeIOS){
18057 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18060 var v = this.inputEl().getValue();
18066 * Clears any text/value currently set in the field
18068 clearValue : function(){
18070 if(this.hiddenField){
18071 this.hiddenField.dom.value = '';
18074 this.setRawValue('');
18075 this.lastSelectionText = '';
18076 this.lastData = false;
18078 var close = this.closeTriggerEl();
18089 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18090 * will be displayed in the field. If the value does not match the data value of an existing item,
18091 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18092 * Otherwise the field will be blank (although the value will still be set).
18093 * @param {String} value The value to match
18095 setValue : function(v)
18097 if(Roo.isIOS && this.useNativeIOS){
18098 this.setIOSValue(v);
18108 if(this.valueField){
18109 var r = this.findRecord(this.valueField, v);
18111 text = r.data[this.displayField];
18112 }else if(this.valueNotFoundText !== undefined){
18113 text = this.valueNotFoundText;
18116 this.lastSelectionText = text;
18117 if(this.hiddenField){
18118 this.hiddenField.dom.value = v;
18120 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18123 var close = this.closeTriggerEl();
18126 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18132 * @property {Object} the last set data for the element
18137 * Sets the value of the field based on a object which is related to the record format for the store.
18138 * @param {Object} value the value to set as. or false on reset?
18140 setFromData : function(o){
18147 var dv = ''; // display value
18148 var vv = ''; // value value..
18150 if (this.displayField) {
18151 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18153 // this is an error condition!!!
18154 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18157 if(this.valueField){
18158 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18161 var close = this.closeTriggerEl();
18164 if(dv.length || vv * 1 > 0){
18166 this.blockFocus=true;
18172 if(this.hiddenField){
18173 this.hiddenField.dom.value = vv;
18175 this.lastSelectionText = dv;
18176 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18180 // no hidden field.. - we store the value in 'value', but still display
18181 // display field!!!!
18182 this.lastSelectionText = dv;
18183 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18190 reset : function(){
18191 // overridden so that last data is reset..
18198 this.setValue(this.originalValue);
18199 //this.clearInvalid();
18200 this.lastData = false;
18202 this.view.clearSelections();
18208 findRecord : function(prop, value){
18210 if(this.store.getCount() > 0){
18211 this.store.each(function(r){
18212 if(r.data[prop] == value){
18222 getName: function()
18224 // returns hidden if it's set..
18225 if (!this.rendered) {return ''};
18226 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18230 onViewMove : function(e, t){
18231 this.inKeyMode = false;
18235 onViewOver : function(e, t){
18236 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18239 var item = this.view.findItemFromChild(t);
18242 var index = this.view.indexOf(item);
18243 this.select(index, false);
18248 onViewClick : function(view, doFocus, el, e)
18250 var index = this.view.getSelectedIndexes()[0];
18252 var r = this.store.getAt(index);
18256 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18263 Roo.each(this.tickItems, function(v,k){
18265 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18267 _this.tickItems.splice(k, 1);
18269 if(typeof(e) == 'undefined' && view == false){
18270 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18282 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18283 this.tickItems.push(r.data);
18286 if(typeof(e) == 'undefined' && view == false){
18287 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18294 this.onSelect(r, index);
18296 if(doFocus !== false && !this.blockFocus){
18297 this.inputEl().focus();
18302 restrictHeight : function(){
18303 //this.innerList.dom.style.height = '';
18304 //var inner = this.innerList.dom;
18305 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18306 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18307 //this.list.beginUpdate();
18308 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18309 this.list.alignTo(this.inputEl(), this.listAlign);
18310 this.list.alignTo(this.inputEl(), this.listAlign);
18311 //this.list.endUpdate();
18315 onEmptyResults : function(){
18317 if(this.tickable && this.editable){
18318 this.hasFocus = false;
18319 this.restrictHeight();
18327 * Returns true if the dropdown list is expanded, else false.
18329 isExpanded : function(){
18330 return this.list.isVisible();
18334 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18335 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18336 * @param {String} value The data value of the item to select
18337 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18338 * selected item if it is not currently in view (defaults to true)
18339 * @return {Boolean} True if the value matched an item in the list, else false
18341 selectByValue : function(v, scrollIntoView){
18342 if(v !== undefined && v !== null){
18343 var r = this.findRecord(this.valueField || this.displayField, v);
18345 this.select(this.store.indexOf(r), scrollIntoView);
18353 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18354 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18355 * @param {Number} index The zero-based index of the list item to select
18356 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18357 * selected item if it is not currently in view (defaults to true)
18359 select : function(index, scrollIntoView){
18360 this.selectedIndex = index;
18361 this.view.select(index);
18362 if(scrollIntoView !== false){
18363 var el = this.view.getNode(index);
18365 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18368 this.list.scrollChildIntoView(el, false);
18374 selectNext : function(){
18375 var ct = this.store.getCount();
18377 if(this.selectedIndex == -1){
18379 }else if(this.selectedIndex < ct-1){
18380 this.select(this.selectedIndex+1);
18386 selectPrev : function(){
18387 var ct = this.store.getCount();
18389 if(this.selectedIndex == -1){
18391 }else if(this.selectedIndex != 0){
18392 this.select(this.selectedIndex-1);
18398 onKeyUp : function(e){
18399 if(this.editable !== false && !e.isSpecialKey()){
18400 this.lastKey = e.getKey();
18401 this.dqTask.delay(this.queryDelay);
18406 validateBlur : function(){
18407 return !this.list || !this.list.isVisible();
18411 initQuery : function(){
18413 var v = this.getRawValue();
18415 if(this.tickable && this.editable){
18416 v = this.tickableInputEl().getValue();
18423 doForce : function(){
18424 if(this.inputEl().dom.value.length > 0){
18425 this.inputEl().dom.value =
18426 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18432 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18433 * query allowing the query action to be canceled if needed.
18434 * @param {String} query The SQL query to execute
18435 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18436 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18437 * saved in the current store (defaults to false)
18439 doQuery : function(q, forceAll){
18441 if(q === undefined || q === null){
18446 forceAll: forceAll,
18450 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18455 forceAll = qe.forceAll;
18456 if(forceAll === true || (q.length >= this.minChars)){
18458 this.hasQuery = true;
18460 if(this.lastQuery != q || this.alwaysQuery){
18461 this.lastQuery = q;
18462 if(this.mode == 'local'){
18463 this.selectedIndex = -1;
18465 this.store.clearFilter();
18468 if(this.specialFilter){
18469 this.fireEvent('specialfilter', this);
18474 this.store.filter(this.displayField, q);
18477 this.store.fireEvent("datachanged", this.store);
18484 this.store.baseParams[this.queryParam] = q;
18486 var options = {params : this.getParams(q)};
18489 options.add = true;
18490 options.params.start = this.page * this.pageSize;
18493 this.store.load(options);
18496 * this code will make the page width larger, at the beginning, the list not align correctly,
18497 * we should expand the list on onLoad
18498 * so command out it
18503 this.selectedIndex = -1;
18508 this.loadNext = false;
18512 getParams : function(q){
18514 //p[this.queryParam] = q;
18518 p.limit = this.pageSize;
18524 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18526 collapse : function(){
18527 if(!this.isExpanded()){
18533 this.hasFocus = false;
18537 this.cancelBtn.hide();
18538 this.trigger.show();
18541 this.tickableInputEl().dom.value = '';
18542 this.tickableInputEl().blur();
18547 Roo.get(document).un('mousedown', this.collapseIf, this);
18548 Roo.get(document).un('mousewheel', this.collapseIf, this);
18549 if (!this.editable) {
18550 Roo.get(document).un('keydown', this.listKeyPress, this);
18552 this.fireEvent('collapse', this);
18558 collapseIf : function(e){
18559 var in_combo = e.within(this.el);
18560 var in_list = e.within(this.list);
18561 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18563 if (in_combo || in_list || is_list) {
18564 //e.stopPropagation();
18569 this.onTickableFooterButtonClick(e, false, false);
18577 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18579 expand : function(){
18581 if(this.isExpanded() || !this.hasFocus){
18585 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18586 this.list.setWidth(lw);
18592 this.restrictHeight();
18596 this.tickItems = Roo.apply([], this.item);
18599 this.cancelBtn.show();
18600 this.trigger.hide();
18603 this.tickableInputEl().focus();
18608 Roo.get(document).on('mousedown', this.collapseIf, this);
18609 Roo.get(document).on('mousewheel', this.collapseIf, this);
18610 if (!this.editable) {
18611 Roo.get(document).on('keydown', this.listKeyPress, this);
18614 this.fireEvent('expand', this);
18618 // Implements the default empty TriggerField.onTriggerClick function
18619 onTriggerClick : function(e)
18621 Roo.log('trigger click');
18623 if(this.disabled || !this.triggerList){
18628 this.loadNext = false;
18630 if(this.isExpanded()){
18632 if (!this.blockFocus) {
18633 this.inputEl().focus();
18637 this.hasFocus = true;
18638 if(this.triggerAction == 'all') {
18639 this.doQuery(this.allQuery, true);
18641 this.doQuery(this.getRawValue());
18643 if (!this.blockFocus) {
18644 this.inputEl().focus();
18649 onTickableTriggerClick : function(e)
18656 this.loadNext = false;
18657 this.hasFocus = true;
18659 if(this.triggerAction == 'all') {
18660 this.doQuery(this.allQuery, true);
18662 this.doQuery(this.getRawValue());
18666 onSearchFieldClick : function(e)
18668 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18669 this.onTickableFooterButtonClick(e, false, false);
18673 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18678 this.loadNext = false;
18679 this.hasFocus = true;
18681 if(this.triggerAction == 'all') {
18682 this.doQuery(this.allQuery, true);
18684 this.doQuery(this.getRawValue());
18688 listKeyPress : function(e)
18690 //Roo.log('listkeypress');
18691 // scroll to first matching element based on key pres..
18692 if (e.isSpecialKey()) {
18695 var k = String.fromCharCode(e.getKey()).toUpperCase();
18698 var csel = this.view.getSelectedNodes();
18699 var cselitem = false;
18701 var ix = this.view.indexOf(csel[0]);
18702 cselitem = this.store.getAt(ix);
18703 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18709 this.store.each(function(v) {
18711 // start at existing selection.
18712 if (cselitem.id == v.id) {
18718 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18719 match = this.store.indexOf(v);
18725 if (match === false) {
18726 return true; // no more action?
18729 this.view.select(match);
18730 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18731 sn.scrollIntoView(sn.dom.parentNode, false);
18734 onViewScroll : function(e, t){
18736 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){
18740 this.hasQuery = true;
18742 this.loading = this.list.select('.loading', true).first();
18744 if(this.loading === null){
18745 this.list.createChild({
18747 cls: 'loading roo-select2-more-results roo-select2-active',
18748 html: 'Loading more results...'
18751 this.loading = this.list.select('.loading', true).first();
18753 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18755 this.loading.hide();
18758 this.loading.show();
18763 this.loadNext = true;
18765 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18770 addItem : function(o)
18772 var dv = ''; // display value
18774 if (this.displayField) {
18775 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18777 // this is an error condition!!!
18778 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18785 var choice = this.choices.createChild({
18787 cls: 'roo-select2-search-choice',
18796 cls: 'roo-select2-search-choice-close fa fa-times',
18801 }, this.searchField);
18803 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18805 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18813 this.inputEl().dom.value = '';
18818 onRemoveItem : function(e, _self, o)
18820 e.preventDefault();
18822 this.lastItem = Roo.apply([], this.item);
18824 var index = this.item.indexOf(o.data) * 1;
18827 Roo.log('not this item?!');
18831 this.item.splice(index, 1);
18836 this.fireEvent('remove', this, e);
18842 syncValue : function()
18844 if(!this.item.length){
18851 Roo.each(this.item, function(i){
18852 if(_this.valueField){
18853 value.push(i[_this.valueField]);
18860 this.value = value.join(',');
18862 if(this.hiddenField){
18863 this.hiddenField.dom.value = this.value;
18866 this.store.fireEvent("datachanged", this.store);
18871 clearItem : function()
18873 if(!this.multiple){
18879 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18887 if(this.tickable && !Roo.isTouch){
18888 this.view.refresh();
18892 inputEl: function ()
18894 if(Roo.isIOS && this.useNativeIOS){
18895 return this.el.select('select.roo-ios-select', true).first();
18898 if(Roo.isTouch && this.mobileTouchView){
18899 return this.el.select('input.form-control',true).first();
18903 return this.searchField;
18906 return this.el.select('input.form-control',true).first();
18909 onTickableFooterButtonClick : function(e, btn, el)
18911 e.preventDefault();
18913 this.lastItem = Roo.apply([], this.item);
18915 if(btn && btn.name == 'cancel'){
18916 this.tickItems = Roo.apply([], this.item);
18925 Roo.each(this.tickItems, function(o){
18933 validate : function()
18935 if(this.getVisibilityEl().hasClass('hidden')){
18939 var v = this.getRawValue();
18942 v = this.getValue();
18945 if(this.disabled || this.allowBlank || v.length){
18950 this.markInvalid();
18954 tickableInputEl : function()
18956 if(!this.tickable || !this.editable){
18957 return this.inputEl();
18960 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18964 getAutoCreateTouchView : function()
18969 cls: 'form-group' //input-group
18975 type : this.inputType,
18976 cls : 'form-control x-combo-noedit',
18977 autocomplete: 'new-password',
18978 placeholder : this.placeholder || '',
18983 input.name = this.name;
18987 input.cls += ' input-' + this.size;
18990 if (this.disabled) {
18991 input.disabled = true;
18995 cls : 'roo-combobox-wrap',
19002 inputblock.cls += ' input-group';
19004 inputblock.cn.unshift({
19006 cls : 'input-group-addon input-group-prepend input-group-text',
19011 if(this.removable && !this.multiple){
19012 inputblock.cls += ' roo-removable';
19014 inputblock.cn.push({
19017 cls : 'roo-combo-removable-btn close'
19021 if(this.hasFeedback && !this.allowBlank){
19023 inputblock.cls += ' has-feedback';
19025 inputblock.cn.push({
19027 cls: 'glyphicon form-control-feedback'
19034 inputblock.cls += (this.before) ? '' : ' input-group';
19036 inputblock.cn.push({
19038 cls : 'input-group-addon input-group-append input-group-text',
19044 var ibwrap = inputblock;
19049 cls: 'roo-select2-choices',
19053 cls: 'roo-select2-search-field',
19066 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19071 cls: 'form-hidden-field'
19077 if(!this.multiple && this.showToggleBtn){
19083 if (this.caret != false) {
19086 cls: 'fa fa-' + this.caret
19093 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19095 Roo.bootstrap.version == 3 ? caret : '',
19098 cls: 'combobox-clear',
19112 combobox.cls += ' roo-select2-container-multi';
19115 var required = this.allowBlank ? {
19117 style: 'display: none'
19120 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19121 tooltip : 'This field is required'
19124 var align = this.labelAlign || this.parentLabelAlign();
19126 if (align ==='left' && this.fieldLabel.length) {
19132 cls : 'control-label col-form-label',
19133 html : this.fieldLabel
19137 cls : 'roo-combobox-wrap ',
19144 var labelCfg = cfg.cn[1];
19145 var contentCfg = cfg.cn[2];
19148 if(this.indicatorpos == 'right'){
19153 cls : 'control-label col-form-label',
19157 html : this.fieldLabel
19163 cls : "roo-combobox-wrap ",
19171 labelCfg = cfg.cn[0];
19172 contentCfg = cfg.cn[1];
19177 if(this.labelWidth > 12){
19178 labelCfg.style = "width: " + this.labelWidth + 'px';
19181 if(this.labelWidth < 13 && this.labelmd == 0){
19182 this.labelmd = this.labelWidth;
19185 if(this.labellg > 0){
19186 labelCfg.cls += ' col-lg-' + this.labellg;
19187 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19190 if(this.labelmd > 0){
19191 labelCfg.cls += ' col-md-' + this.labelmd;
19192 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19195 if(this.labelsm > 0){
19196 labelCfg.cls += ' col-sm-' + this.labelsm;
19197 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19200 if(this.labelxs > 0){
19201 labelCfg.cls += ' col-xs-' + this.labelxs;
19202 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19206 } else if ( this.fieldLabel.length) {
19211 cls : 'control-label',
19212 html : this.fieldLabel
19223 if(this.indicatorpos == 'right'){
19227 cls : 'control-label',
19228 html : this.fieldLabel,
19246 var settings = this;
19248 ['xs','sm','md','lg'].map(function(size){
19249 if (settings[size]) {
19250 cfg.cls += ' col-' + size + '-' + settings[size];
19257 initTouchView : function()
19259 this.renderTouchView();
19261 this.touchViewEl.on('scroll', function(){
19262 this.el.dom.scrollTop = 0;
19265 this.originalValue = this.getValue();
19267 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19269 this.inputEl().on("click", this.showTouchView, this);
19270 if (this.triggerEl) {
19271 this.triggerEl.on("click", this.showTouchView, this);
19275 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19276 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19278 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19280 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19281 this.store.on('load', this.onTouchViewLoad, this);
19282 this.store.on('loadexception', this.onTouchViewLoadException, this);
19284 if(this.hiddenName){
19286 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19288 this.hiddenField.dom.value =
19289 this.hiddenValue !== undefined ? this.hiddenValue :
19290 this.value !== undefined ? this.value : '';
19292 this.el.dom.removeAttribute('name');
19293 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19297 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19298 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19301 if(this.removable && !this.multiple){
19302 var close = this.closeTriggerEl();
19304 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19305 close.on('click', this.removeBtnClick, this, close);
19309 * fix the bug in Safari iOS8
19311 this.inputEl().on("focus", function(e){
19312 document.activeElement.blur();
19315 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19322 renderTouchView : function()
19324 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19325 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19328 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19331 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332 this.touchViewBodyEl.setStyle('overflow', 'auto');
19334 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19335 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19338 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342 showTouchView : function()
19348 this.touchViewHeaderEl.hide();
19350 if(this.modalTitle.length){
19351 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19352 this.touchViewHeaderEl.show();
19355 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19356 this.touchViewEl.show();
19358 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19360 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19361 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19363 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19365 if(this.modalTitle.length){
19366 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19369 this.touchViewBodyEl.setHeight(bodyHeight);
19373 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19375 this.touchViewEl.addClass(['in','show']);
19378 if(this._touchViewMask){
19379 Roo.get(document.body).addClass("x-body-masked");
19380 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19381 this._touchViewMask.setStyle('z-index', 10000);
19382 this._touchViewMask.addClass('show');
19385 this.doTouchViewQuery();
19389 hideTouchView : function()
19391 this.touchViewEl.removeClass(['in','show']);
19395 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19397 this.touchViewEl.setStyle('display', 'none');
19400 if(this._touchViewMask){
19401 this._touchViewMask.removeClass('show');
19402 Roo.get(document.body).removeClass("x-body-masked");
19406 setTouchViewValue : function()
19413 Roo.each(this.tickItems, function(o){
19418 this.hideTouchView();
19421 doTouchViewQuery : function()
19430 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19434 if(!this.alwaysQuery || this.mode == 'local'){
19435 this.onTouchViewLoad();
19442 onTouchViewBeforeLoad : function(combo,opts)
19448 onTouchViewLoad : function()
19450 if(this.store.getCount() < 1){
19451 this.onTouchViewEmptyResults();
19455 this.clearTouchView();
19457 var rawValue = this.getRawValue();
19459 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19461 this.tickItems = [];
19463 this.store.data.each(function(d, rowIndex){
19464 var row = this.touchViewListGroup.createChild(template);
19466 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19467 row.addClass(d.data.cls);
19470 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19473 html : d.data[this.displayField]
19476 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19477 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19480 row.removeClass('selected');
19481 if(!this.multiple && this.valueField &&
19482 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19485 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19486 row.addClass('selected');
19489 if(this.multiple && this.valueField &&
19490 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19494 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19495 this.tickItems.push(d.data);
19498 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19502 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19504 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19506 if(this.modalTitle.length){
19507 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19510 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19512 if(this.mobile_restrict_height && listHeight < bodyHeight){
19513 this.touchViewBodyEl.setHeight(listHeight);
19518 if(firstChecked && listHeight > bodyHeight){
19519 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19524 onTouchViewLoadException : function()
19526 this.hideTouchView();
19529 onTouchViewEmptyResults : function()
19531 this.clearTouchView();
19533 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19535 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19539 clearTouchView : function()
19541 this.touchViewListGroup.dom.innerHTML = '';
19544 onTouchViewClick : function(e, el, o)
19546 e.preventDefault();
19549 var rowIndex = o.rowIndex;
19551 var r = this.store.getAt(rowIndex);
19553 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19555 if(!this.multiple){
19556 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19557 c.dom.removeAttribute('checked');
19560 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19562 this.setFromData(r.data);
19564 var close = this.closeTriggerEl();
19570 this.hideTouchView();
19572 this.fireEvent('select', this, r, rowIndex);
19577 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19578 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19579 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19583 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19584 this.addItem(r.data);
19585 this.tickItems.push(r.data);
19589 getAutoCreateNativeIOS : function()
19592 cls: 'form-group' //input-group,
19597 cls : 'roo-ios-select'
19601 combobox.name = this.name;
19604 if (this.disabled) {
19605 combobox.disabled = true;
19608 var settings = this;
19610 ['xs','sm','md','lg'].map(function(size){
19611 if (settings[size]) {
19612 cfg.cls += ' col-' + size + '-' + settings[size];
19622 initIOSView : function()
19624 this.store.on('load', this.onIOSViewLoad, this);
19629 onIOSViewLoad : function()
19631 if(this.store.getCount() < 1){
19635 this.clearIOSView();
19637 if(this.allowBlank) {
19639 var default_text = '-- SELECT --';
19641 if(this.placeholder.length){
19642 default_text = this.placeholder;
19645 if(this.emptyTitle.length){
19646 default_text += ' - ' + this.emptyTitle + ' -';
19649 var opt = this.inputEl().createChild({
19652 html : default_text
19656 o[this.valueField] = 0;
19657 o[this.displayField] = default_text;
19659 this.ios_options.push({
19666 this.store.data.each(function(d, rowIndex){
19670 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19671 html = d.data[this.displayField];
19676 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19677 value = d.data[this.valueField];
19686 if(this.value == d.data[this.valueField]){
19687 option['selected'] = true;
19690 var opt = this.inputEl().createChild(option);
19692 this.ios_options.push({
19699 this.inputEl().on('change', function(){
19700 this.fireEvent('select', this);
19705 clearIOSView: function()
19707 this.inputEl().dom.innerHTML = '';
19709 this.ios_options = [];
19712 setIOSValue: function(v)
19716 if(!this.ios_options){
19720 Roo.each(this.ios_options, function(opts){
19722 opts.el.dom.removeAttribute('selected');
19724 if(opts.data[this.valueField] != v){
19728 opts.el.dom.setAttribute('selected', true);
19734 * @cfg {Boolean} grow
19738 * @cfg {Number} growMin
19742 * @cfg {Number} growMax
19751 Roo.apply(Roo.bootstrap.form.ComboBox, {
19755 cls: 'modal-header',
19777 cls: 'list-group-item',
19781 cls: 'roo-combobox-list-group-item-value'
19785 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19799 listItemCheckbox : {
19801 cls: 'list-group-item',
19805 cls: 'roo-combobox-list-group-item-value'
19809 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19825 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19830 cls: 'modal-footer',
19838 cls: 'col-xs-6 text-left',
19841 cls: 'btn btn-danger roo-touch-view-cancel',
19847 cls: 'col-xs-6 text-right',
19850 cls: 'btn btn-success roo-touch-view-ok',
19861 Roo.apply(Roo.bootstrap.form.ComboBox, {
19863 touchViewTemplate : {
19865 cls: 'modal fade roo-combobox-touch-view',
19869 cls: 'modal-dialog',
19870 style : 'position:fixed', // we have to fix position....
19874 cls: 'modal-content',
19876 Roo.bootstrap.form.ComboBox.header,
19877 Roo.bootstrap.form.ComboBox.body,
19878 Roo.bootstrap.form.ComboBox.footer
19887 * Ext JS Library 1.1.1
19888 * Copyright(c) 2006-2007, Ext JS, LLC.
19890 * Originally Released Under LGPL - original licence link has changed is not relivant.
19893 * <script type="text/javascript">
19898 * @extends Roo.util.Observable
19899 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19900 * This class also supports single and multi selection modes. <br>
19901 * Create a data model bound view:
19903 var store = new Roo.data.Store(...);
19905 var view = new Roo.View({
19907 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19909 singleSelect: true,
19910 selectedClass: "ydataview-selected",
19914 // listen for node click?
19915 view.on("click", function(vw, index, node, e){
19916 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19920 dataModel.load("foobar.xml");
19922 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19924 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19925 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19927 * Note: old style constructor is still suported (container, template, config)
19930 * Create a new View
19931 * @param {Object} config The config object
19934 Roo.View = function(config, depreciated_tpl, depreciated_config){
19936 this.parent = false;
19938 if (typeof(depreciated_tpl) == 'undefined') {
19939 // new way.. - universal constructor.
19940 Roo.apply(this, config);
19941 this.el = Roo.get(this.el);
19944 this.el = Roo.get(config);
19945 this.tpl = depreciated_tpl;
19946 Roo.apply(this, depreciated_config);
19948 this.wrapEl = this.el.wrap().wrap();
19949 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19952 if(typeof(this.tpl) == "string"){
19953 this.tpl = new Roo.Template(this.tpl);
19955 // support xtype ctors..
19956 this.tpl = new Roo.factory(this.tpl, Roo);
19960 this.tpl.compile();
19965 * @event beforeclick
19966 * Fires before a click is processed. Returns false to cancel the default action.
19967 * @param {Roo.View} this
19968 * @param {Number} index The index of the target node
19969 * @param {HTMLElement} node The target node
19970 * @param {Roo.EventObject} e The raw event object
19972 "beforeclick" : true,
19975 * Fires when a template node is clicked.
19976 * @param {Roo.View} this
19977 * @param {Number} index The index of the target node
19978 * @param {HTMLElement} node The target node
19979 * @param {Roo.EventObject} e The raw event object
19984 * Fires when a template node is double clicked.
19985 * @param {Roo.View} this
19986 * @param {Number} index The index of the target node
19987 * @param {HTMLElement} node The target node
19988 * @param {Roo.EventObject} e The raw event object
19992 * @event contextmenu
19993 * Fires when a template node is right clicked.
19994 * @param {Roo.View} this
19995 * @param {Number} index The index of the target node
19996 * @param {HTMLElement} node The target node
19997 * @param {Roo.EventObject} e The raw event object
19999 "contextmenu" : true,
20001 * @event selectionchange
20002 * Fires when the selected nodes change.
20003 * @param {Roo.View} this
20004 * @param {Array} selections Array of the selected nodes
20006 "selectionchange" : true,
20009 * @event beforeselect
20010 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20011 * @param {Roo.View} this
20012 * @param {HTMLElement} node The node to be selected
20013 * @param {Array} selections Array of currently selected nodes
20015 "beforeselect" : true,
20017 * @event preparedata
20018 * Fires on every row to render, to allow you to change the data.
20019 * @param {Roo.View} this
20020 * @param {Object} data to be rendered (change this)
20022 "preparedata" : true
20030 "click": this.onClick,
20031 "dblclick": this.onDblClick,
20032 "contextmenu": this.onContextMenu,
20036 this.selections = [];
20038 this.cmp = new Roo.CompositeElementLite([]);
20040 this.store = Roo.factory(this.store, Roo.data);
20041 this.setStore(this.store, true);
20044 if ( this.footer && this.footer.xtype) {
20046 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20048 this.footer.dataSource = this.store;
20049 this.footer.container = fctr;
20050 this.footer = Roo.factory(this.footer, Roo);
20051 fctr.insertFirst(this.el);
20053 // this is a bit insane - as the paging toolbar seems to detach the el..
20054 // dom.parentNode.parentNode.parentNode
20055 // they get detached?
20059 Roo.View.superclass.constructor.call(this);
20064 Roo.extend(Roo.View, Roo.util.Observable, {
20067 * @cfg {Roo.data.Store} store Data store to load data from.
20072 * @cfg {String|Roo.Element} el The container element.
20077 * @cfg {String|Roo.Template} tpl The template used by this View
20081 * @cfg {String} dataName the named area of the template to use as the data area
20082 * Works with domtemplates roo-name="name"
20086 * @cfg {String} selectedClass The css class to add to selected nodes
20088 selectedClass : "x-view-selected",
20090 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20095 * @cfg {String} text to display on mask (default Loading)
20099 * @cfg {Boolean} multiSelect Allow multiple selection
20101 multiSelect : false,
20103 * @cfg {Boolean} singleSelect Allow single selection
20105 singleSelect: false,
20108 * @cfg {Boolean} toggleSelect - selecting
20110 toggleSelect : false,
20113 * @cfg {Boolean} tickable - selecting
20118 * Returns the element this view is bound to.
20119 * @return {Roo.Element}
20121 getEl : function(){
20122 return this.wrapEl;
20128 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20130 refresh : function(){
20131 //Roo.log('refresh');
20134 // if we are using something like 'domtemplate', then
20135 // the what gets used is:
20136 // t.applySubtemplate(NAME, data, wrapping data..)
20137 // the outer template then get' applied with
20138 // the store 'extra data'
20139 // and the body get's added to the
20140 // roo-name="data" node?
20141 // <span class='roo-tpl-{name}'></span> ?????
20145 this.clearSelections();
20146 this.el.update("");
20148 var records = this.store.getRange();
20149 if(records.length < 1) {
20151 // is this valid?? = should it render a template??
20153 this.el.update(this.emptyText);
20157 if (this.dataName) {
20158 this.el.update(t.apply(this.store.meta)); //????
20159 el = this.el.child('.roo-tpl-' + this.dataName);
20162 for(var i = 0, len = records.length; i < len; i++){
20163 var data = this.prepareData(records[i].data, i, records[i]);
20164 this.fireEvent("preparedata", this, data, i, records[i]);
20166 var d = Roo.apply({}, data);
20169 Roo.apply(d, {'roo-id' : Roo.id()});
20173 Roo.each(this.parent.item, function(item){
20174 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20177 Roo.apply(d, {'roo-data-checked' : 'checked'});
20181 html[html.length] = Roo.util.Format.trim(
20183 t.applySubtemplate(this.dataName, d, this.store.meta) :
20190 el.update(html.join(""));
20191 this.nodes = el.dom.childNodes;
20192 this.updateIndexes(0);
20197 * Function to override to reformat the data that is sent to
20198 * the template for each node.
20199 * DEPRICATED - use the preparedata event handler.
20200 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20201 * a JSON object for an UpdateManager bound view).
20203 prepareData : function(data, index, record)
20205 this.fireEvent("preparedata", this, data, index, record);
20209 onUpdate : function(ds, record){
20210 // Roo.log('on update');
20211 this.clearSelections();
20212 var index = this.store.indexOf(record);
20213 var n = this.nodes[index];
20214 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20215 n.parentNode.removeChild(n);
20216 this.updateIndexes(index, index);
20222 onAdd : function(ds, records, index)
20224 //Roo.log(['on Add', ds, records, index] );
20225 this.clearSelections();
20226 if(this.nodes.length == 0){
20230 var n = this.nodes[index];
20231 for(var i = 0, len = records.length; i < len; i++){
20232 var d = this.prepareData(records[i].data, i, records[i]);
20234 this.tpl.insertBefore(n, d);
20237 this.tpl.append(this.el, d);
20240 this.updateIndexes(index);
20243 onRemove : function(ds, record, index){
20244 // Roo.log('onRemove');
20245 this.clearSelections();
20246 var el = this.dataName ?
20247 this.el.child('.roo-tpl-' + this.dataName) :
20250 el.dom.removeChild(this.nodes[index]);
20251 this.updateIndexes(index);
20255 * Refresh an individual node.
20256 * @param {Number} index
20258 refreshNode : function(index){
20259 this.onUpdate(this.store, this.store.getAt(index));
20262 updateIndexes : function(startIndex, endIndex){
20263 var ns = this.nodes;
20264 startIndex = startIndex || 0;
20265 endIndex = endIndex || ns.length - 1;
20266 for(var i = startIndex; i <= endIndex; i++){
20267 ns[i].nodeIndex = i;
20272 * Changes the data store this view uses and refresh the view.
20273 * @param {Store} store
20275 setStore : function(store, initial){
20276 if(!initial && this.store){
20277 this.store.un("datachanged", this.refresh);
20278 this.store.un("add", this.onAdd);
20279 this.store.un("remove", this.onRemove);
20280 this.store.un("update", this.onUpdate);
20281 this.store.un("clear", this.refresh);
20282 this.store.un("beforeload", this.onBeforeLoad);
20283 this.store.un("load", this.onLoad);
20284 this.store.un("loadexception", this.onLoad);
20288 store.on("datachanged", this.refresh, this);
20289 store.on("add", this.onAdd, this);
20290 store.on("remove", this.onRemove, this);
20291 store.on("update", this.onUpdate, this);
20292 store.on("clear", this.refresh, this);
20293 store.on("beforeload", this.onBeforeLoad, this);
20294 store.on("load", this.onLoad, this);
20295 store.on("loadexception", this.onLoad, this);
20303 * onbeforeLoad - masks the loading area.
20306 onBeforeLoad : function(store,opts)
20308 //Roo.log('onBeforeLoad');
20310 this.el.update("");
20312 this.el.mask(this.mask ? this.mask : "Loading" );
20314 onLoad : function ()
20321 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20322 * @param {HTMLElement} node
20323 * @return {HTMLElement} The template node
20325 findItemFromChild : function(node){
20326 var el = this.dataName ?
20327 this.el.child('.roo-tpl-' + this.dataName,true) :
20330 if(!node || node.parentNode == el){
20333 var p = node.parentNode;
20334 while(p && p != el){
20335 if(p.parentNode == el){
20344 onClick : function(e){
20345 var item = this.findItemFromChild(e.getTarget());
20347 var index = this.indexOf(item);
20348 if(this.onItemClick(item, index, e) !== false){
20349 this.fireEvent("click", this, index, item, e);
20352 this.clearSelections();
20357 onContextMenu : function(e){
20358 var item = this.findItemFromChild(e.getTarget());
20360 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20365 onDblClick : function(e){
20366 var item = this.findItemFromChild(e.getTarget());
20368 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20372 onItemClick : function(item, index, e)
20374 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20377 if (this.toggleSelect) {
20378 var m = this.isSelected(item) ? 'unselect' : 'select';
20381 _t[m](item, true, false);
20384 if(this.multiSelect || this.singleSelect){
20385 if(this.multiSelect && e.shiftKey && this.lastSelection){
20386 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20388 this.select(item, this.multiSelect && e.ctrlKey);
20389 this.lastSelection = item;
20392 if(!this.tickable){
20393 e.preventDefault();
20401 * Get the number of selected nodes.
20404 getSelectionCount : function(){
20405 return this.selections.length;
20409 * Get the currently selected nodes.
20410 * @return {Array} An array of HTMLElements
20412 getSelectedNodes : function(){
20413 return this.selections;
20417 * Get the indexes of the selected nodes.
20420 getSelectedIndexes : function(){
20421 var indexes = [], s = this.selections;
20422 for(var i = 0, len = s.length; i < len; i++){
20423 indexes.push(s[i].nodeIndex);
20429 * Clear all selections
20430 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20432 clearSelections : function(suppressEvent){
20433 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20434 this.cmp.elements = this.selections;
20435 this.cmp.removeClass(this.selectedClass);
20436 this.selections = [];
20437 if(!suppressEvent){
20438 this.fireEvent("selectionchange", this, this.selections);
20444 * Returns true if the passed node is selected
20445 * @param {HTMLElement/Number} node The node or node index
20446 * @return {Boolean}
20448 isSelected : function(node){
20449 var s = this.selections;
20453 node = this.getNode(node);
20454 return s.indexOf(node) !== -1;
20459 * @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
20460 * @param {Boolean} keepExisting (optional) true to keep existing selections
20461 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20463 select : function(nodeInfo, keepExisting, suppressEvent){
20464 if(nodeInfo instanceof Array){
20466 this.clearSelections(true);
20468 for(var i = 0, len = nodeInfo.length; i < len; i++){
20469 this.select(nodeInfo[i], true, true);
20473 var node = this.getNode(nodeInfo);
20474 if(!node || this.isSelected(node)){
20475 return; // already selected.
20478 this.clearSelections(true);
20481 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20482 Roo.fly(node).addClass(this.selectedClass);
20483 this.selections.push(node);
20484 if(!suppressEvent){
20485 this.fireEvent("selectionchange", this, this.selections);
20493 * @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
20494 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20495 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20497 unselect : function(nodeInfo, keepExisting, suppressEvent)
20499 if(nodeInfo instanceof Array){
20500 Roo.each(this.selections, function(s) {
20501 this.unselect(s, nodeInfo);
20505 var node = this.getNode(nodeInfo);
20506 if(!node || !this.isSelected(node)){
20507 //Roo.log("not selected");
20508 return; // not selected.
20512 Roo.each(this.selections, function(s) {
20514 Roo.fly(node).removeClass(this.selectedClass);
20521 this.selections= ns;
20522 this.fireEvent("selectionchange", this, this.selections);
20526 * Gets a template node.
20527 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20528 * @return {HTMLElement} The node or null if it wasn't found
20530 getNode : function(nodeInfo){
20531 if(typeof nodeInfo == "string"){
20532 return document.getElementById(nodeInfo);
20533 }else if(typeof nodeInfo == "number"){
20534 return this.nodes[nodeInfo];
20540 * Gets a range template nodes.
20541 * @param {Number} startIndex
20542 * @param {Number} endIndex
20543 * @return {Array} An array of nodes
20545 getNodes : function(start, end){
20546 var ns = this.nodes;
20547 start = start || 0;
20548 end = typeof end == "undefined" ? ns.length - 1 : end;
20551 for(var i = start; i <= end; i++){
20555 for(var i = start; i >= end; i--){
20563 * Finds the index of the passed node
20564 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20565 * @return {Number} The index of the node or -1
20567 indexOf : function(node){
20568 node = this.getNode(node);
20569 if(typeof node.nodeIndex == "number"){
20570 return node.nodeIndex;
20572 var ns = this.nodes;
20573 for(var i = 0, len = ns.length; i < len; i++){
20584 * based on jquery fullcalendar
20588 Roo.bootstrap = Roo.bootstrap || {};
20590 * @class Roo.bootstrap.Calendar
20591 * @extends Roo.bootstrap.Component
20592 * Bootstrap Calendar class
20593 * @cfg {Boolean} loadMask (true|false) default false
20594 * @cfg {Object} header generate the user specific header of the calendar, default false
20597 * Create a new Container
20598 * @param {Object} config The config object
20603 Roo.bootstrap.Calendar = function(config){
20604 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20608 * Fires when a date is selected
20609 * @param {DatePicker} this
20610 * @param {Date} date The selected date
20614 * @event monthchange
20615 * Fires when the displayed month changes
20616 * @param {DatePicker} this
20617 * @param {Date} date The selected month
20619 'monthchange': true,
20621 * @event evententer
20622 * Fires when mouse over an event
20623 * @param {Calendar} this
20624 * @param {event} Event
20626 'evententer': true,
20628 * @event eventleave
20629 * Fires when the mouse leaves an
20630 * @param {Calendar} this
20633 'eventleave': true,
20635 * @event eventclick
20636 * Fires when the mouse click an
20637 * @param {Calendar} this
20646 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20649 * @cfg {Roo.data.Store} store
20650 * The data source for the calendar
20654 * @cfg {Number} startDay
20655 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20663 getAutoCreate : function(){
20666 var fc_button = function(name, corner, style, content ) {
20667 return Roo.apply({},{
20669 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20671 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20674 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20685 style : 'width:100%',
20692 cls : 'fc-header-left',
20694 fc_button('prev', 'left', 'arrow', '‹' ),
20695 fc_button('next', 'right', 'arrow', '›' ),
20696 { tag: 'span', cls: 'fc-header-space' },
20697 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20705 cls : 'fc-header-center',
20709 cls: 'fc-header-title',
20712 html : 'month / year'
20720 cls : 'fc-header-right',
20722 /* fc_button('month', 'left', '', 'month' ),
20723 fc_button('week', '', '', 'week' ),
20724 fc_button('day', 'right', '', 'day' )
20736 header = this.header;
20739 var cal_heads = function() {
20741 // fixme - handle this.
20743 for (var i =0; i < Date.dayNames.length; i++) {
20744 var d = Date.dayNames[i];
20747 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20748 html : d.substring(0,3)
20752 ret[0].cls += ' fc-first';
20753 ret[6].cls += ' fc-last';
20756 var cal_cell = function(n) {
20759 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20764 cls: 'fc-day-number',
20768 cls: 'fc-day-content',
20772 style: 'position: relative;' // height: 17px;
20784 var cal_rows = function() {
20787 for (var r = 0; r < 6; r++) {
20794 for (var i =0; i < Date.dayNames.length; i++) {
20795 var d = Date.dayNames[i];
20796 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20799 row.cn[0].cls+=' fc-first';
20800 row.cn[0].cn[0].style = 'min-height:90px';
20801 row.cn[6].cls+=' fc-last';
20805 ret[0].cls += ' fc-first';
20806 ret[4].cls += ' fc-prev-last';
20807 ret[5].cls += ' fc-last';
20814 cls: 'fc-border-separate',
20815 style : 'width:100%',
20823 cls : 'fc-first fc-last',
20841 cls : 'fc-content',
20842 style : "position: relative;",
20845 cls : 'fc-view fc-view-month fc-grid',
20846 style : 'position: relative',
20847 unselectable : 'on',
20850 cls : 'fc-event-container',
20851 style : 'position:absolute;z-index:8;top:0;left:0;'
20869 initEvents : function()
20872 throw "can not find store for calendar";
20878 style: "text-align:center",
20882 style: "background-color:white;width:50%;margin:250 auto",
20886 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20897 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20899 var size = this.el.select('.fc-content', true).first().getSize();
20900 this.maskEl.setSize(size.width, size.height);
20901 this.maskEl.enableDisplayMode("block");
20902 if(!this.loadMask){
20903 this.maskEl.hide();
20906 this.store = Roo.factory(this.store, Roo.data);
20907 this.store.on('load', this.onLoad, this);
20908 this.store.on('beforeload', this.onBeforeLoad, this);
20912 this.cells = this.el.select('.fc-day',true);
20913 //Roo.log(this.cells);
20914 this.textNodes = this.el.query('.fc-day-number');
20915 this.cells.addClassOnOver('fc-state-hover');
20917 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20918 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20919 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20920 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20922 this.on('monthchange', this.onMonthChange, this);
20924 this.update(new Date().clearTime());
20927 resize : function() {
20928 var sz = this.el.getSize();
20930 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20931 this.el.select('.fc-day-content div',true).setHeight(34);
20936 showPrevMonth : function(e){
20937 this.update(this.activeDate.add("mo", -1));
20939 showToday : function(e){
20940 this.update(new Date().clearTime());
20943 showNextMonth : function(e){
20944 this.update(this.activeDate.add("mo", 1));
20948 showPrevYear : function(){
20949 this.update(this.activeDate.add("y", -1));
20953 showNextYear : function(){
20954 this.update(this.activeDate.add("y", 1));
20959 update : function(date)
20961 var vd = this.activeDate;
20962 this.activeDate = date;
20963 // if(vd && this.el){
20964 // var t = date.getTime();
20965 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20966 // Roo.log('using add remove');
20968 // this.fireEvent('monthchange', this, date);
20970 // this.cells.removeClass("fc-state-highlight");
20971 // this.cells.each(function(c){
20972 // if(c.dateValue == t){
20973 // c.addClass("fc-state-highlight");
20974 // setTimeout(function(){
20975 // try{c.dom.firstChild.focus();}catch(e){}
20985 var days = date.getDaysInMonth();
20987 var firstOfMonth = date.getFirstDateOfMonth();
20988 var startingPos = firstOfMonth.getDay()-this.startDay;
20990 if(startingPos < this.startDay){
20994 var pm = date.add(Date.MONTH, -1);
20995 var prevStart = pm.getDaysInMonth()-startingPos;
20997 this.cells = this.el.select('.fc-day',true);
20998 this.textNodes = this.el.query('.fc-day-number');
20999 this.cells.addClassOnOver('fc-state-hover');
21001 var cells = this.cells.elements;
21002 var textEls = this.textNodes;
21004 Roo.each(cells, function(cell){
21005 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21008 days += startingPos;
21010 // convert everything to numbers so it's fast
21011 var day = 86400000;
21012 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21015 //Roo.log(prevStart);
21017 var today = new Date().clearTime().getTime();
21018 var sel = date.clearTime().getTime();
21019 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21020 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21021 var ddMatch = this.disabledDatesRE;
21022 var ddText = this.disabledDatesText;
21023 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21024 var ddaysText = this.disabledDaysText;
21025 var format = this.format;
21027 var setCellClass = function(cal, cell){
21031 //Roo.log('set Cell Class');
21033 var t = d.getTime();
21037 cell.dateValue = t;
21039 cell.className += " fc-today";
21040 cell.className += " fc-state-highlight";
21041 cell.title = cal.todayText;
21044 // disable highlight in other month..
21045 //cell.className += " fc-state-highlight";
21050 cell.className = " fc-state-disabled";
21051 cell.title = cal.minText;
21055 cell.className = " fc-state-disabled";
21056 cell.title = cal.maxText;
21060 if(ddays.indexOf(d.getDay()) != -1){
21061 cell.title = ddaysText;
21062 cell.className = " fc-state-disabled";
21065 if(ddMatch && format){
21066 var fvalue = d.dateFormat(format);
21067 if(ddMatch.test(fvalue)){
21068 cell.title = ddText.replace("%0", fvalue);
21069 cell.className = " fc-state-disabled";
21073 if (!cell.initialClassName) {
21074 cell.initialClassName = cell.dom.className;
21077 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21082 for(; i < startingPos; i++) {
21083 textEls[i].innerHTML = (++prevStart);
21084 d.setDate(d.getDate()+1);
21086 cells[i].className = "fc-past fc-other-month";
21087 setCellClass(this, cells[i]);
21092 for(; i < days; i++){
21093 intDay = i - startingPos + 1;
21094 textEls[i].innerHTML = (intDay);
21095 d.setDate(d.getDate()+1);
21097 cells[i].className = ''; // "x-date-active";
21098 setCellClass(this, cells[i]);
21102 for(; i < 42; i++) {
21103 textEls[i].innerHTML = (++extraDays);
21104 d.setDate(d.getDate()+1);
21106 cells[i].className = "fc-future fc-other-month";
21107 setCellClass(this, cells[i]);
21110 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21112 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21114 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21115 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21117 if(totalRows != 6){
21118 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21119 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21122 this.fireEvent('monthchange', this, date);
21126 if(!this.internalRender){
21127 var main = this.el.dom.firstChild;
21128 var w = main.offsetWidth;
21129 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21130 Roo.fly(main).setWidth(w);
21131 this.internalRender = true;
21132 // opera does not respect the auto grow header center column
21133 // then, after it gets a width opera refuses to recalculate
21134 // without a second pass
21135 if(Roo.isOpera && !this.secondPass){
21136 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21137 this.secondPass = true;
21138 this.update.defer(10, this, [date]);
21145 findCell : function(dt) {
21146 dt = dt.clearTime().getTime();
21148 this.cells.each(function(c){
21149 //Roo.log("check " +c.dateValue + '?=' + dt);
21150 if(c.dateValue == dt){
21160 findCells : function(ev) {
21161 var s = ev.start.clone().clearTime().getTime();
21163 var e= ev.end.clone().clearTime().getTime();
21166 this.cells.each(function(c){
21167 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21169 if(c.dateValue > e){
21172 if(c.dateValue < s){
21181 // findBestRow: function(cells)
21185 // for (var i =0 ; i < cells.length;i++) {
21186 // ret = Math.max(cells[i].rows || 0,ret);
21193 addItem : function(ev)
21195 // look for vertical location slot in
21196 var cells = this.findCells(ev);
21198 // ev.row = this.findBestRow(cells);
21200 // work out the location.
21204 for(var i =0; i < cells.length; i++) {
21206 cells[i].row = cells[0].row;
21209 cells[i].row = cells[i].row + 1;
21219 if (crow.start.getY() == cells[i].getY()) {
21221 crow.end = cells[i];
21238 cells[0].events.push(ev);
21240 this.calevents.push(ev);
21243 clearEvents: function() {
21245 if(!this.calevents){
21249 Roo.each(this.cells.elements, function(c){
21255 Roo.each(this.calevents, function(e) {
21256 Roo.each(e.els, function(el) {
21257 el.un('mouseenter' ,this.onEventEnter, this);
21258 el.un('mouseleave' ,this.onEventLeave, this);
21263 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21269 renderEvents: function()
21273 this.cells.each(function(c) {
21282 if(c.row != c.events.length){
21283 r = 4 - (4 - (c.row - c.events.length));
21286 c.events = ev.slice(0, r);
21287 c.more = ev.slice(r);
21289 if(c.more.length && c.more.length == 1){
21290 c.events.push(c.more.pop());
21293 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21297 this.cells.each(function(c) {
21299 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21302 for (var e = 0; e < c.events.length; e++){
21303 var ev = c.events[e];
21304 var rows = ev.rows;
21306 for(var i = 0; i < rows.length; i++) {
21308 // how many rows should it span..
21311 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21312 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21314 unselectable : "on",
21317 cls: 'fc-event-inner',
21321 // cls: 'fc-event-time',
21322 // html : cells.length > 1 ? '' : ev.time
21326 cls: 'fc-event-title',
21327 html : String.format('{0}', ev.title)
21334 cls: 'ui-resizable-handle ui-resizable-e',
21335 html : '  '
21342 cfg.cls += ' fc-event-start';
21344 if ((i+1) == rows.length) {
21345 cfg.cls += ' fc-event-end';
21348 var ctr = _this.el.select('.fc-event-container',true).first();
21349 var cg = ctr.createChild(cfg);
21351 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21352 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21354 var r = (c.more.length) ? 1 : 0;
21355 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21356 cg.setWidth(ebox.right - sbox.x -2);
21358 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21359 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21360 cg.on('click', _this.onEventClick, _this, ev);
21371 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21372 style : 'position: absolute',
21373 unselectable : "on",
21376 cls: 'fc-event-inner',
21380 cls: 'fc-event-title',
21388 cls: 'ui-resizable-handle ui-resizable-e',
21389 html : '  '
21395 var ctr = _this.el.select('.fc-event-container',true).first();
21396 var cg = ctr.createChild(cfg);
21398 var sbox = c.select('.fc-day-content',true).first().getBox();
21399 var ebox = c.select('.fc-day-content',true).first().getBox();
21401 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21402 cg.setWidth(ebox.right - sbox.x -2);
21404 cg.on('click', _this.onMoreEventClick, _this, c.more);
21414 onEventEnter: function (e, el,event,d) {
21415 this.fireEvent('evententer', this, el, event);
21418 onEventLeave: function (e, el,event,d) {
21419 this.fireEvent('eventleave', this, el, event);
21422 onEventClick: function (e, el,event,d) {
21423 this.fireEvent('eventclick', this, el, event);
21426 onMonthChange: function () {
21430 onMoreEventClick: function(e, el, more)
21434 this.calpopover.placement = 'right';
21435 this.calpopover.setTitle('More');
21437 this.calpopover.setContent('');
21439 var ctr = this.calpopover.el.select('.popover-content', true).first();
21441 Roo.each(more, function(m){
21443 cls : 'fc-event-hori fc-event-draggable',
21446 var cg = ctr.createChild(cfg);
21448 cg.on('click', _this.onEventClick, _this, m);
21451 this.calpopover.show(el);
21456 onLoad: function ()
21458 this.calevents = [];
21461 if(this.store.getCount() > 0){
21462 this.store.data.each(function(d){
21465 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21466 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21467 time : d.data.start_time,
21468 title : d.data.title,
21469 description : d.data.description,
21470 venue : d.data.venue
21475 this.renderEvents();
21477 if(this.calevents.length && this.loadMask){
21478 this.maskEl.hide();
21482 onBeforeLoad: function()
21484 this.clearEvents();
21486 this.maskEl.show();
21500 * @class Roo.bootstrap.Popover
21501 * @extends Roo.bootstrap.Component
21502 * @parent none builder
21503 * @children Roo.bootstrap.Component
21504 * Bootstrap Popover class
21505 * @cfg {String} html contents of the popover (or false to use children..)
21506 * @cfg {String} title of popover (or false to hide)
21507 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21508 * @cfg {String} trigger click || hover (or false to trigger manually)
21509 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21510 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21511 * - if false and it has a 'parent' then it will be automatically added to that element
21512 * - if string - Roo.get will be called
21513 * @cfg {Number} delay - delay before showing
21516 * Create a new Popover
21517 * @param {Object} config The config object
21520 Roo.bootstrap.Popover = function(config){
21521 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21527 * After the popover show
21529 * @param {Roo.bootstrap.Popover} this
21534 * After the popover hide
21536 * @param {Roo.bootstrap.Popover} this
21542 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21547 placement : 'right',
21548 trigger : 'hover', // hover
21554 can_build_overlaid : false,
21556 maskEl : false, // the mask element
21559 alignEl : false, // when show is called with an element - this get's stored.
21561 getChildContainer : function()
21563 return this.contentEl;
21566 getPopoverHeader : function()
21568 this.title = true; // flag not to hide it..
21569 this.headerEl.addClass('p-0');
21570 return this.headerEl
21574 getAutoCreate : function(){
21577 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21578 style: 'display:block',
21584 cls : 'popover-inner ',
21588 cls: 'popover-title popover-header',
21589 html : this.title === false ? '' : this.title
21592 cls : 'popover-content popover-body ' + (this.cls || ''),
21593 html : this.html || ''
21604 * @param {string} the title
21606 setTitle: function(str)
21610 this.headerEl.dom.innerHTML = str;
21615 * @param {string} the body content
21617 setContent: function(str)
21620 if (this.contentEl) {
21621 this.contentEl.dom.innerHTML = str;
21625 // as it get's added to the bottom of the page.
21626 onRender : function(ct, position)
21628 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21633 var cfg = Roo.apply({}, this.getAutoCreate());
21637 cfg.cls += ' ' + this.cls;
21640 cfg.style = this.style;
21642 //Roo.log("adding to ");
21643 this.el = Roo.get(document.body).createChild(cfg, position);
21644 // Roo.log(this.el);
21647 this.contentEl = this.el.select('.popover-content',true).first();
21648 this.headerEl = this.el.select('.popover-title',true).first();
21651 if(typeof(this.items) != 'undefined'){
21652 var items = this.items;
21655 for(var i =0;i < items.length;i++) {
21656 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21660 this.items = nitems;
21662 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21663 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21670 resizeMask : function()
21672 this.maskEl.setSize(
21673 Roo.lib.Dom.getViewWidth(true),
21674 Roo.lib.Dom.getViewHeight(true)
21678 initEvents : function()
21682 Roo.bootstrap.Popover.register(this);
21685 this.arrowEl = this.el.select('.arrow',true).first();
21686 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21687 this.el.enableDisplayMode('block');
21691 if (this.over === false && !this.parent()) {
21694 if (this.triggers === false) {
21699 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21700 var triggers = this.trigger ? this.trigger.split(' ') : [];
21701 Roo.each(triggers, function(trigger) {
21703 if (trigger == 'click') {
21704 on_el.on('click', this.toggle, this);
21705 } else if (trigger != 'manual') {
21706 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21707 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21709 on_el.on(eventIn ,this.enter, this);
21710 on_el.on(eventOut, this.leave, this);
21720 toggle : function () {
21721 this.hoverState == 'in' ? this.leave() : this.enter();
21724 enter : function () {
21726 clearTimeout(this.timeout);
21728 this.hoverState = 'in';
21730 if (!this.delay || !this.delay.show) {
21735 this.timeout = setTimeout(function () {
21736 if (_t.hoverState == 'in') {
21739 }, this.delay.show)
21742 leave : function() {
21743 clearTimeout(this.timeout);
21745 this.hoverState = 'out';
21747 if (!this.delay || !this.delay.hide) {
21752 this.timeout = setTimeout(function () {
21753 if (_t.hoverState == 'out') {
21756 }, this.delay.hide)
21760 * update the position of the dialog
21761 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21766 doAlign : function()
21769 if (this.alignEl) {
21770 this.updatePosition(this.placement, true);
21773 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21774 var es = this.el.getSize();
21775 var x = Roo.lib.Dom.getViewWidth()/2;
21776 var y = Roo.lib.Dom.getViewHeight()/2;
21777 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21789 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21790 * @param {string} (left|right|top|bottom) position
21792 show : function (on_el, placement)
21794 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21795 on_el = on_el || false; // default to false
21798 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21799 on_el = this.parent().el;
21800 } else if (this.over) {
21801 on_el = Roo.get(this.over);
21806 this.alignEl = Roo.get( on_el );
21809 this.render(document.body);
21815 if (this.title === false) {
21816 this.headerEl.hide();
21821 this.el.dom.style.display = 'block';
21825 //var arrow = this.el.select('.arrow',true).first();
21826 //arrow.set(align[2],
21828 this.el.addClass('in');
21832 this.hoverState = 'in';
21835 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21836 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21837 this.maskEl.dom.style.display = 'block';
21838 this.maskEl.addClass('show');
21840 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21842 this.fireEvent('show', this);
21846 * fire this manually after loading a grid in the table for example
21847 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21848 * @param {Boolean} try and move it if we cant get right position.
21850 updatePosition : function(placement, try_move)
21852 // allow for calling with no parameters
21853 placement = placement ? placement : this.placement;
21854 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21856 this.el.removeClass([
21857 'fade','top','bottom', 'left', 'right','in',
21858 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21860 this.el.addClass(placement + ' bs-popover-' + placement);
21862 if (!this.alignEl ) {
21866 switch (placement) {
21868 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21869 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21870 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21871 //normal display... or moved up/down.
21872 this.el.setXY(offset);
21873 var xy = this.alignEl.getAnchorXY('tr', false);
21875 this.arrowEl.setXY(xy);
21878 // continue through...
21879 return this.updatePosition('left', false);
21883 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21884 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21885 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21886 //normal display... or moved up/down.
21887 this.el.setXY(offset);
21888 var xy = this.alignEl.getAnchorXY('tl', false);
21889 xy[0]-=10;xy[1]+=5; // << fix me
21890 this.arrowEl.setXY(xy);
21894 return this.updatePosition('right', false);
21897 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21898 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21899 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21900 //normal display... or moved up/down.
21901 this.el.setXY(offset);
21902 var xy = this.alignEl.getAnchorXY('t', false);
21903 xy[1]-=10; // << fix me
21904 this.arrowEl.setXY(xy);
21908 return this.updatePosition('bottom', false);
21911 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21912 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21913 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21914 //normal display... or moved up/down.
21915 this.el.setXY(offset);
21916 var xy = this.alignEl.getAnchorXY('b', false);
21917 xy[1]+=2; // << fix me
21918 this.arrowEl.setXY(xy);
21922 return this.updatePosition('top', false);
21933 this.el.setXY([0,0]);
21934 this.el.removeClass('in');
21936 this.hoverState = null;
21937 this.maskEl.hide(); // always..
21938 this.fireEvent('hide', this);
21944 Roo.apply(Roo.bootstrap.Popover, {
21947 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21948 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21949 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21950 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21955 clickHander : false,
21959 onMouseDown : function(e)
21961 if (this.popups.length && !e.getTarget(".roo-popover")) {
21962 /// what is nothing is showing..
21971 register : function(popup)
21973 if (!Roo.bootstrap.Popover.clickHandler) {
21974 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21976 // hide other popups.
21977 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21978 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21979 this.hideAll(); //<< why?
21980 //this.popups.push(popup);
21982 hideAll : function()
21984 this.popups.forEach(function(p) {
21988 onShow : function() {
21989 Roo.bootstrap.Popover.popups.push(this);
21991 onHide : function() {
21992 Roo.bootstrap.Popover.popups.remove(this);
21997 * @class Roo.bootstrap.PopoverNav
21998 * @extends Roo.bootstrap.nav.Simplebar
21999 * @parent Roo.bootstrap.Popover
22000 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22002 * Bootstrap Popover header navigation class
22003 * FIXME? should this go under nav?
22007 * Create a new Popover Header Navigation
22008 * @param {Object} config The config object
22011 Roo.bootstrap.PopoverNav = function(config){
22012 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22015 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22018 container_method : 'getPopoverHeader'
22036 * @class Roo.bootstrap.Progress
22037 * @extends Roo.bootstrap.Component
22038 * @children Roo.bootstrap.ProgressBar
22039 * Bootstrap Progress class
22040 * @cfg {Boolean} striped striped of the progress bar
22041 * @cfg {Boolean} active animated of the progress bar
22045 * Create a new Progress
22046 * @param {Object} config The config object
22049 Roo.bootstrap.Progress = function(config){
22050 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22053 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22058 getAutoCreate : function(){
22066 cfg.cls += ' progress-striped';
22070 cfg.cls += ' active';
22089 * @class Roo.bootstrap.ProgressBar
22090 * @extends Roo.bootstrap.Component
22091 * Bootstrap ProgressBar class
22092 * @cfg {Number} aria_valuenow aria-value now
22093 * @cfg {Number} aria_valuemin aria-value min
22094 * @cfg {Number} aria_valuemax aria-value max
22095 * @cfg {String} label label for the progress bar
22096 * @cfg {String} panel (success | info | warning | danger )
22097 * @cfg {String} role role of the progress bar
22098 * @cfg {String} sr_only text
22102 * Create a new ProgressBar
22103 * @param {Object} config The config object
22106 Roo.bootstrap.ProgressBar = function(config){
22107 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22110 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22114 aria_valuemax : 100,
22120 getAutoCreate : function()
22125 cls: 'progress-bar',
22126 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22138 cfg.role = this.role;
22141 if(this.aria_valuenow){
22142 cfg['aria-valuenow'] = this.aria_valuenow;
22145 if(this.aria_valuemin){
22146 cfg['aria-valuemin'] = this.aria_valuemin;
22149 if(this.aria_valuemax){
22150 cfg['aria-valuemax'] = this.aria_valuemax;
22153 if(this.label && !this.sr_only){
22154 cfg.html = this.label;
22158 cfg.cls += ' progress-bar-' + this.panel;
22164 update : function(aria_valuenow)
22166 this.aria_valuenow = aria_valuenow;
22168 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22176 * @class Roo.bootstrap.TabGroup
22177 * @extends Roo.bootstrap.Column
22178 * @children Roo.bootstrap.TabPanel
22179 * Bootstrap Column class
22180 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22181 * @cfg {Boolean} carousel true to make the group behave like a carousel
22182 * @cfg {Boolean} bullets show bullets for the panels
22183 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22184 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22185 * @cfg {Boolean} showarrow (true|false) show arrow default true
22188 * Create a new TabGroup
22189 * @param {Object} config The config object
22192 Roo.bootstrap.TabGroup = function(config){
22193 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22195 this.navId = Roo.id();
22198 Roo.bootstrap.TabGroup.register(this);
22202 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22205 transition : false,
22210 slideOnTouch : false,
22213 getAutoCreate : function()
22215 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22217 cfg.cls += ' tab-content';
22219 if (this.carousel) {
22220 cfg.cls += ' carousel slide';
22223 cls : 'carousel-inner',
22227 if(this.bullets && !Roo.isTouch){
22230 cls : 'carousel-bullets',
22234 if(this.bullets_cls){
22235 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22242 cfg.cn[0].cn.push(bullets);
22245 if(this.showarrow){
22246 cfg.cn[0].cn.push({
22248 class : 'carousel-arrow',
22252 class : 'carousel-prev',
22256 class : 'fa fa-chevron-left'
22262 class : 'carousel-next',
22266 class : 'fa fa-chevron-right'
22279 initEvents: function()
22281 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22282 // this.el.on("touchstart", this.onTouchStart, this);
22285 if(this.autoslide){
22288 this.slideFn = window.setInterval(function() {
22289 _this.showPanelNext();
22293 if(this.showarrow){
22294 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22295 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22301 // onTouchStart : function(e, el, o)
22303 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22307 // this.showPanelNext();
22311 getChildContainer : function()
22313 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22317 * register a Navigation item
22318 * @param {Roo.bootstrap.nav.Item} the navitem to add
22320 register : function(item)
22322 this.tabs.push( item);
22323 item.navId = this.navId; // not really needed..
22328 getActivePanel : function()
22331 Roo.each(this.tabs, function(t) {
22341 getPanelByName : function(n)
22344 Roo.each(this.tabs, function(t) {
22345 if (t.tabId == n) {
22353 indexOfPanel : function(p)
22356 Roo.each(this.tabs, function(t,i) {
22357 if (t.tabId == p.tabId) {
22366 * show a specific panel
22367 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22368 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22370 showPanel : function (pan)
22372 if(this.transition || typeof(pan) == 'undefined'){
22373 Roo.log("waiting for the transitionend");
22377 if (typeof(pan) == 'number') {
22378 pan = this.tabs[pan];
22381 if (typeof(pan) == 'string') {
22382 pan = this.getPanelByName(pan);
22385 var cur = this.getActivePanel();
22388 Roo.log('pan or acitve pan is undefined');
22392 if (pan.tabId == this.getActivePanel().tabId) {
22396 if (false === cur.fireEvent('beforedeactivate')) {
22400 if(this.bullets > 0 && !Roo.isTouch){
22401 this.setActiveBullet(this.indexOfPanel(pan));
22404 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22406 //class="carousel-item carousel-item-next carousel-item-left"
22408 this.transition = true;
22409 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22410 var lr = dir == 'next' ? 'left' : 'right';
22411 pan.el.addClass(dir); // or prev
22412 pan.el.addClass('carousel-item-' + dir); // or prev
22413 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22414 cur.el.addClass(lr); // or right
22415 pan.el.addClass(lr);
22416 cur.el.addClass('carousel-item-' +lr); // or right
22417 pan.el.addClass('carousel-item-' +lr);
22421 cur.el.on('transitionend', function() {
22422 Roo.log("trans end?");
22424 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22425 pan.setActive(true);
22427 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22428 cur.setActive(false);
22430 _this.transition = false;
22432 }, this, { single: true } );
22437 cur.setActive(false);
22438 pan.setActive(true);
22443 showPanelNext : function()
22445 var i = this.indexOfPanel(this.getActivePanel());
22447 if (i >= this.tabs.length - 1 && !this.autoslide) {
22451 if (i >= this.tabs.length - 1 && this.autoslide) {
22455 this.showPanel(this.tabs[i+1]);
22458 showPanelPrev : function()
22460 var i = this.indexOfPanel(this.getActivePanel());
22462 if (i < 1 && !this.autoslide) {
22466 if (i < 1 && this.autoslide) {
22467 i = this.tabs.length;
22470 this.showPanel(this.tabs[i-1]);
22474 addBullet: function()
22476 if(!this.bullets || Roo.isTouch){
22479 var ctr = this.el.select('.carousel-bullets',true).first();
22480 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22481 var bullet = ctr.createChild({
22482 cls : 'bullet bullet-' + i
22483 },ctr.dom.lastChild);
22488 bullet.on('click', (function(e, el, o, ii, t){
22490 e.preventDefault();
22492 this.showPanel(ii);
22494 if(this.autoslide && this.slideFn){
22495 clearInterval(this.slideFn);
22496 this.slideFn = window.setInterval(function() {
22497 _this.showPanelNext();
22501 }).createDelegate(this, [i, bullet], true));
22506 setActiveBullet : function(i)
22512 Roo.each(this.el.select('.bullet', true).elements, function(el){
22513 el.removeClass('selected');
22516 var bullet = this.el.select('.bullet-' + i, true).first();
22522 bullet.addClass('selected');
22533 Roo.apply(Roo.bootstrap.TabGroup, {
22537 * register a Navigation Group
22538 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22540 register : function(navgrp)
22542 this.groups[navgrp.navId] = navgrp;
22546 * fetch a Navigation Group based on the navigation ID
22547 * if one does not exist , it will get created.
22548 * @param {string} the navgroup to add
22549 * @returns {Roo.bootstrap.nav.Group} the navgroup
22551 get: function(navId) {
22552 if (typeof(this.groups[navId]) == 'undefined') {
22553 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22555 return this.groups[navId] ;
22570 * @class Roo.bootstrap.TabPanel
22571 * @extends Roo.bootstrap.Component
22572 * @children Roo.bootstrap.Component
22573 * Bootstrap TabPanel class
22574 * @cfg {Boolean} active panel active
22575 * @cfg {String} html panel content
22576 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22577 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22578 * @cfg {String} href click to link..
22579 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22583 * Create a new TabPanel
22584 * @param {Object} config The config object
22587 Roo.bootstrap.TabPanel = function(config){
22588 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22592 * Fires when the active status changes
22593 * @param {Roo.bootstrap.TabPanel} this
22594 * @param {Boolean} state the new state
22599 * @event beforedeactivate
22600 * Fires before a tab is de-activated - can be used to do validation on a form.
22601 * @param {Roo.bootstrap.TabPanel} this
22602 * @return {Boolean} false if there is an error
22605 'beforedeactivate': true
22608 this.tabId = this.tabId || Roo.id();
22612 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22619 touchSlide : false,
22620 getAutoCreate : function(){
22625 // item is needed for carousel - not sure if it has any effect otherwise
22626 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22627 html: this.html || ''
22631 cfg.cls += ' active';
22635 cfg.tabId = this.tabId;
22643 initEvents: function()
22645 var p = this.parent();
22647 this.navId = this.navId || p.navId;
22649 if (typeof(this.navId) != 'undefined') {
22650 // not really needed.. but just in case.. parent should be a NavGroup.
22651 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22655 var i = tg.tabs.length - 1;
22657 if(this.active && tg.bullets > 0 && i < tg.bullets){
22658 tg.setActiveBullet(i);
22662 this.el.on('click', this.onClick, this);
22664 if(Roo.isTouch && this.touchSlide){
22665 this.el.on("touchstart", this.onTouchStart, this);
22666 this.el.on("touchmove", this.onTouchMove, this);
22667 this.el.on("touchend", this.onTouchEnd, this);
22672 onRender : function(ct, position)
22674 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22677 setActive : function(state)
22679 Roo.log("panel - set active " + this.tabId + "=" + state);
22681 this.active = state;
22683 this.el.removeClass('active');
22685 } else if (!this.el.hasClass('active')) {
22686 this.el.addClass('active');
22689 this.fireEvent('changed', this, state);
22692 onClick : function(e)
22694 e.preventDefault();
22696 if(!this.href.length){
22700 window.location.href = this.href;
22709 onTouchStart : function(e)
22711 this.swiping = false;
22713 this.startX = e.browserEvent.touches[0].clientX;
22714 this.startY = e.browserEvent.touches[0].clientY;
22717 onTouchMove : function(e)
22719 this.swiping = true;
22721 this.endX = e.browserEvent.touches[0].clientX;
22722 this.endY = e.browserEvent.touches[0].clientY;
22725 onTouchEnd : function(e)
22732 var tabGroup = this.parent();
22734 if(this.endX > this.startX){ // swiping right
22735 tabGroup.showPanelPrev();
22739 if(this.startX > this.endX){ // swiping left
22740 tabGroup.showPanelNext();
22759 * @class Roo.bootstrap.form.DateField
22760 * @extends Roo.bootstrap.form.Input
22761 * Bootstrap DateField class
22762 * @cfg {Number} weekStart default 0
22763 * @cfg {String} viewMode default empty, (months|years)
22764 * @cfg {String} minViewMode default empty, (months|years)
22765 * @cfg {Number} startDate default -Infinity
22766 * @cfg {Number} endDate default Infinity
22767 * @cfg {Boolean} todayHighlight default false
22768 * @cfg {Boolean} todayBtn default false
22769 * @cfg {Boolean} calendarWeeks default false
22770 * @cfg {Object} daysOfWeekDisabled default empty
22771 * @cfg {Boolean} singleMode default false (true | false)
22773 * @cfg {Boolean} keyboardNavigation default true
22774 * @cfg {String} language default en
22777 * Create a new DateField
22778 * @param {Object} config The config object
22781 Roo.bootstrap.form.DateField = function(config){
22782 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22786 * Fires when this field show.
22787 * @param {Roo.bootstrap.form.DateField} this
22788 * @param {Mixed} date The date value
22793 * Fires when this field hide.
22794 * @param {Roo.bootstrap.form.DateField} this
22795 * @param {Mixed} date The date value
22800 * Fires when select a date.
22801 * @param {Roo.bootstrap.form.DateField} this
22802 * @param {Mixed} date The date value
22806 * @event beforeselect
22807 * Fires when before select a date.
22808 * @param {Roo.bootstrap.form.DateField} this
22809 * @param {Mixed} date The date value
22811 beforeselect : true
22815 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22818 * @cfg {String} format
22819 * The default date format string which can be overriden for localization support. The format must be
22820 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22824 * @cfg {String} altFormats
22825 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22826 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22828 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22836 todayHighlight : false,
22842 keyboardNavigation: true,
22844 calendarWeeks: false,
22846 startDate: -Infinity,
22850 daysOfWeekDisabled: [],
22854 singleMode : false,
22856 UTCDate: function()
22858 return new Date(Date.UTC.apply(Date, arguments));
22861 UTCToday: function()
22863 var today = new Date();
22864 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22867 getDate: function() {
22868 var d = this.getUTCDate();
22869 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22872 getUTCDate: function() {
22876 setDate: function(d) {
22877 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22880 setUTCDate: function(d) {
22882 this.setValue(this.formatDate(this.date));
22885 onRender: function(ct, position)
22888 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22890 this.language = this.language || 'en';
22891 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22892 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22894 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22895 this.format = this.format || 'm/d/y';
22896 this.isInline = false;
22897 this.isInput = true;
22898 this.component = this.el.select('.add-on', true).first() || false;
22899 this.component = (this.component && this.component.length === 0) ? false : this.component;
22900 this.hasInput = this.component && this.inputEl().length;
22902 if (typeof(this.minViewMode === 'string')) {
22903 switch (this.minViewMode) {
22905 this.minViewMode = 1;
22908 this.minViewMode = 2;
22911 this.minViewMode = 0;
22916 if (typeof(this.viewMode === 'string')) {
22917 switch (this.viewMode) {
22930 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22932 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22934 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22936 this.picker().on('mousedown', this.onMousedown, this);
22937 this.picker().on('click', this.onClick, this);
22939 this.picker().addClass('datepicker-dropdown');
22941 this.startViewMode = this.viewMode;
22943 if(this.singleMode){
22944 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22945 v.setVisibilityMode(Roo.Element.DISPLAY);
22949 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22950 v.setStyle('width', '189px');
22954 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22955 if(!this.calendarWeeks){
22960 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22961 v.attr('colspan', function(i, val){
22962 return parseInt(val) + 1;
22967 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22969 this.setStartDate(this.startDate);
22970 this.setEndDate(this.endDate);
22972 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22979 if(this.isInline) {
22984 picker : function()
22986 return this.pickerEl;
22987 // return this.el.select('.datepicker', true).first();
22990 fillDow: function()
22992 var dowCnt = this.weekStart;
23001 if(this.calendarWeeks){
23009 while (dowCnt < this.weekStart + 7) {
23013 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23017 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23020 fillMonths: function()
23023 var months = this.picker().select('>.datepicker-months td', true).first();
23025 months.dom.innerHTML = '';
23031 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23034 months.createChild(month);
23041 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;
23043 if (this.date < this.startDate) {
23044 this.viewDate = new Date(this.startDate);
23045 } else if (this.date > this.endDate) {
23046 this.viewDate = new Date(this.endDate);
23048 this.viewDate = new Date(this.date);
23056 var d = new Date(this.viewDate),
23057 year = d.getUTCFullYear(),
23058 month = d.getUTCMonth(),
23059 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23060 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23061 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23062 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23063 currentDate = this.date && this.date.valueOf(),
23064 today = this.UTCToday();
23066 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23068 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23070 // this.picker.select('>tfoot th.today').
23071 // .text(dates[this.language].today)
23072 // .toggle(this.todayBtn !== false);
23074 this.updateNavArrows();
23077 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23079 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23081 prevMonth.setUTCDate(day);
23083 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23085 var nextMonth = new Date(prevMonth);
23087 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23089 nextMonth = nextMonth.valueOf();
23091 var fillMonths = false;
23093 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23095 while(prevMonth.valueOf() <= nextMonth) {
23098 if (prevMonth.getUTCDay() === this.weekStart) {
23100 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23108 if(this.calendarWeeks){
23109 // ISO 8601: First week contains first thursday.
23110 // ISO also states week starts on Monday, but we can be more abstract here.
23112 // Start of current week: based on weekstart/current date
23113 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23114 // Thursday of this week
23115 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23116 // First Thursday of year, year from thursday
23117 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23118 // Calendar week: ms between thursdays, div ms per day, div 7 days
23119 calWeek = (th - yth) / 864e5 / 7 + 1;
23121 fillMonths.cn.push({
23129 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23131 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23134 if (this.todayHighlight &&
23135 prevMonth.getUTCFullYear() == today.getFullYear() &&
23136 prevMonth.getUTCMonth() == today.getMonth() &&
23137 prevMonth.getUTCDate() == today.getDate()) {
23138 clsName += ' today';
23141 if (currentDate && prevMonth.valueOf() === currentDate) {
23142 clsName += ' active';
23145 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23146 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23147 clsName += ' disabled';
23150 fillMonths.cn.push({
23152 cls: 'day ' + clsName,
23153 html: prevMonth.getDate()
23156 prevMonth.setDate(prevMonth.getDate()+1);
23159 var currentYear = this.date && this.date.getUTCFullYear();
23160 var currentMonth = this.date && this.date.getUTCMonth();
23162 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23164 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23165 v.removeClass('active');
23167 if(currentYear === year && k === currentMonth){
23168 v.addClass('active');
23171 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23172 v.addClass('disabled');
23178 year = parseInt(year/10, 10) * 10;
23180 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23182 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23185 for (var i = -1; i < 11; i++) {
23186 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23188 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23196 showMode: function(dir)
23199 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23202 Roo.each(this.picker().select('>div',true).elements, function(v){
23203 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23206 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23211 if(this.isInline) {
23215 this.picker().removeClass(['bottom', 'top']);
23217 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23219 * place to the top of element!
23223 this.picker().addClass('top');
23224 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23229 this.picker().addClass('bottom');
23231 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23234 parseDate : function(value)
23236 if(!value || value instanceof Date){
23239 var v = Date.parseDate(value, this.format);
23240 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23241 v = Date.parseDate(value, 'Y-m-d');
23243 if(!v && this.altFormats){
23244 if(!this.altFormatsArray){
23245 this.altFormatsArray = this.altFormats.split("|");
23247 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23248 v = Date.parseDate(value, this.altFormatsArray[i]);
23254 formatDate : function(date, fmt)
23256 return (!date || !(date instanceof Date)) ?
23257 date : date.dateFormat(fmt || this.format);
23260 onFocus : function()
23262 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23266 onBlur : function()
23268 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23270 var d = this.inputEl().getValue();
23277 showPopup : function()
23279 this.picker().show();
23283 this.fireEvent('showpopup', this, this.date);
23286 hidePopup : function()
23288 if(this.isInline) {
23291 this.picker().hide();
23292 this.viewMode = this.startViewMode;
23295 this.fireEvent('hidepopup', this, this.date);
23299 onMousedown: function(e)
23301 e.stopPropagation();
23302 e.preventDefault();
23307 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23311 setValue: function(v)
23313 if(this.fireEvent('beforeselect', this, v) !== false){
23314 var d = new Date(this.parseDate(v) ).clearTime();
23316 if(isNaN(d.getTime())){
23317 this.date = this.viewDate = '';
23318 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23322 v = this.formatDate(d);
23324 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23326 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23330 this.fireEvent('select', this, this.date);
23334 getValue: function()
23336 return this.formatDate(this.date);
23339 fireKey: function(e)
23341 if (!this.picker().isVisible()){
23342 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23348 var dateChanged = false,
23350 newDate, newViewDate;
23355 e.preventDefault();
23359 if (!this.keyboardNavigation) {
23362 dir = e.keyCode == 37 ? -1 : 1;
23365 newDate = this.moveYear(this.date, dir);
23366 newViewDate = this.moveYear(this.viewDate, dir);
23367 } else if (e.shiftKey){
23368 newDate = this.moveMonth(this.date, dir);
23369 newViewDate = this.moveMonth(this.viewDate, dir);
23371 newDate = new Date(this.date);
23372 newDate.setUTCDate(this.date.getUTCDate() + dir);
23373 newViewDate = new Date(this.viewDate);
23374 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23376 if (this.dateWithinRange(newDate)){
23377 this.date = newDate;
23378 this.viewDate = newViewDate;
23379 this.setValue(this.formatDate(this.date));
23381 e.preventDefault();
23382 dateChanged = true;
23387 if (!this.keyboardNavigation) {
23390 dir = e.keyCode == 38 ? -1 : 1;
23392 newDate = this.moveYear(this.date, dir);
23393 newViewDate = this.moveYear(this.viewDate, dir);
23394 } else if (e.shiftKey){
23395 newDate = this.moveMonth(this.date, dir);
23396 newViewDate = this.moveMonth(this.viewDate, dir);
23398 newDate = new Date(this.date);
23399 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23400 newViewDate = new Date(this.viewDate);
23401 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23403 if (this.dateWithinRange(newDate)){
23404 this.date = newDate;
23405 this.viewDate = newViewDate;
23406 this.setValue(this.formatDate(this.date));
23408 e.preventDefault();
23409 dateChanged = true;
23413 this.setValue(this.formatDate(this.date));
23415 e.preventDefault();
23418 this.setValue(this.formatDate(this.date));
23432 onClick: function(e)
23434 e.stopPropagation();
23435 e.preventDefault();
23437 var target = e.getTarget();
23439 if(target.nodeName.toLowerCase() === 'i'){
23440 target = Roo.get(target).dom.parentNode;
23443 var nodeName = target.nodeName;
23444 var className = target.className;
23445 var html = target.innerHTML;
23446 //Roo.log(nodeName);
23448 switch(nodeName.toLowerCase()) {
23450 switch(className) {
23456 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23457 switch(this.viewMode){
23459 this.viewDate = this.moveMonth(this.viewDate, dir);
23463 this.viewDate = this.moveYear(this.viewDate, dir);
23469 var date = new Date();
23470 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23472 this.setValue(this.formatDate(this.date));
23479 if (className.indexOf('disabled') < 0) {
23480 if (!this.viewDate) {
23481 this.viewDate = new Date();
23483 this.viewDate.setUTCDate(1);
23484 if (className.indexOf('month') > -1) {
23485 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23487 var year = parseInt(html, 10) || 0;
23488 this.viewDate.setUTCFullYear(year);
23492 if(this.singleMode){
23493 this.setValue(this.formatDate(this.viewDate));
23504 //Roo.log(className);
23505 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23506 var day = parseInt(html, 10) || 1;
23507 var year = (this.viewDate || new Date()).getUTCFullYear(),
23508 month = (this.viewDate || new Date()).getUTCMonth();
23510 if (className.indexOf('old') > -1) {
23517 } else if (className.indexOf('new') > -1) {
23525 //Roo.log([year,month,day]);
23526 this.date = this.UTCDate(year, month, day,0,0,0,0);
23527 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23529 //Roo.log(this.formatDate(this.date));
23530 this.setValue(this.formatDate(this.date));
23537 setStartDate: function(startDate)
23539 this.startDate = startDate || -Infinity;
23540 if (this.startDate !== -Infinity) {
23541 this.startDate = this.parseDate(this.startDate);
23544 this.updateNavArrows();
23547 setEndDate: function(endDate)
23549 this.endDate = endDate || Infinity;
23550 if (this.endDate !== Infinity) {
23551 this.endDate = this.parseDate(this.endDate);
23554 this.updateNavArrows();
23557 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23559 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23560 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23561 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23563 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23564 return parseInt(d, 10);
23567 this.updateNavArrows();
23570 updateNavArrows: function()
23572 if(this.singleMode){
23576 var d = new Date(this.viewDate),
23577 year = d.getUTCFullYear(),
23578 month = d.getUTCMonth();
23580 Roo.each(this.picker().select('.prev', true).elements, function(v){
23582 switch (this.viewMode) {
23585 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23591 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23598 Roo.each(this.picker().select('.next', true).elements, function(v){
23600 switch (this.viewMode) {
23603 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23609 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23617 moveMonth: function(date, dir)
23622 var new_date = new Date(date.valueOf()),
23623 day = new_date.getUTCDate(),
23624 month = new_date.getUTCMonth(),
23625 mag = Math.abs(dir),
23627 dir = dir > 0 ? 1 : -1;
23630 // If going back one month, make sure month is not current month
23631 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23633 return new_date.getUTCMonth() == month;
23635 // If going forward one month, make sure month is as expected
23636 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23638 return new_date.getUTCMonth() != new_month;
23640 new_month = month + dir;
23641 new_date.setUTCMonth(new_month);
23642 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23643 if (new_month < 0 || new_month > 11) {
23644 new_month = (new_month + 12) % 12;
23647 // For magnitudes >1, move one month at a time...
23648 for (var i=0; i<mag; i++) {
23649 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23650 new_date = this.moveMonth(new_date, dir);
23652 // ...then reset the day, keeping it in the new month
23653 new_month = new_date.getUTCMonth();
23654 new_date.setUTCDate(day);
23656 return new_month != new_date.getUTCMonth();
23659 // Common date-resetting loop -- if date is beyond end of month, make it
23662 new_date.setUTCDate(--day);
23663 new_date.setUTCMonth(new_month);
23668 moveYear: function(date, dir)
23670 return this.moveMonth(date, dir*12);
23673 dateWithinRange: function(date)
23675 return date >= this.startDate && date <= this.endDate;
23681 this.picker().remove();
23684 validateValue : function(value)
23686 if(this.getVisibilityEl().hasClass('hidden')){
23690 if(value.length < 1) {
23691 if(this.allowBlank){
23697 if(value.length < this.minLength){
23700 if(value.length > this.maxLength){
23704 var vt = Roo.form.VTypes;
23705 if(!vt[this.vtype](value, this)){
23709 if(typeof this.validator == "function"){
23710 var msg = this.validator(value);
23716 if(this.regex && !this.regex.test(value)){
23720 if(typeof(this.parseDate(value)) == 'undefined'){
23724 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23728 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23738 this.date = this.viewDate = '';
23740 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23745 Roo.apply(Roo.bootstrap.form.DateField, {
23756 html: '<i class="fa fa-arrow-left"/>'
23766 html: '<i class="fa fa-arrow-right"/>'
23808 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23809 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23810 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23811 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23812 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23825 navFnc: 'FullYear',
23830 navFnc: 'FullYear',
23835 Roo.apply(Roo.bootstrap.form.DateField, {
23839 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23843 cls: 'datepicker-days',
23847 cls: 'table-condensed',
23849 Roo.bootstrap.form.DateField.head,
23853 Roo.bootstrap.form.DateField.footer
23860 cls: 'datepicker-months',
23864 cls: 'table-condensed',
23866 Roo.bootstrap.form.DateField.head,
23867 Roo.bootstrap.form.DateField.content,
23868 Roo.bootstrap.form.DateField.footer
23875 cls: 'datepicker-years',
23879 cls: 'table-condensed',
23881 Roo.bootstrap.form.DateField.head,
23882 Roo.bootstrap.form.DateField.content,
23883 Roo.bootstrap.form.DateField.footer
23902 * @class Roo.bootstrap.form.TimeField
23903 * @extends Roo.bootstrap.form.Input
23904 * Bootstrap DateField class
23908 * Create a new TimeField
23909 * @param {Object} config The config object
23912 Roo.bootstrap.form.TimeField = function(config){
23913 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23917 * Fires when this field show.
23918 * @param {Roo.bootstrap.form.DateField} thisthis
23919 * @param {Mixed} date The date value
23924 * Fires when this field hide.
23925 * @param {Roo.bootstrap.form.DateField} this
23926 * @param {Mixed} date The date value
23931 * Fires when select a date.
23932 * @param {Roo.bootstrap.form.DateField} this
23933 * @param {Mixed} date The date value
23939 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23942 * @cfg {String} format
23943 * The default time format string which can be overriden for localization support. The format must be
23944 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23948 getAutoCreate : function()
23950 this.after = '<i class="fa far fa-clock"></i>';
23951 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23955 onRender: function(ct, position)
23958 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23960 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23962 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23964 this.pop = this.picker().select('>.datepicker-time',true).first();
23965 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23967 this.picker().on('mousedown', this.onMousedown, this);
23968 this.picker().on('click', this.onClick, this);
23970 this.picker().addClass('datepicker-dropdown');
23975 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23976 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23977 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23978 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23979 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23980 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23984 fireKey: function(e){
23985 if (!this.picker().isVisible()){
23986 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23992 e.preventDefault();
24000 this.onTogglePeriod();
24003 this.onIncrementMinutes();
24006 this.onDecrementMinutes();
24015 onClick: function(e) {
24016 e.stopPropagation();
24017 e.preventDefault();
24020 picker : function()
24022 return this.pickerEl;
24025 fillTime: function()
24027 var time = this.pop.select('tbody', true).first();
24029 time.dom.innerHTML = '';
24044 cls: 'hours-up fa fas fa-chevron-up'
24064 cls: 'minutes-up fa fas fa-chevron-up'
24085 cls: 'timepicker-hour',
24100 cls: 'timepicker-minute',
24115 cls: 'btn btn-primary period',
24137 cls: 'hours-down fa fas fa-chevron-down'
24157 cls: 'minutes-down fa fas fa-chevron-down'
24175 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24182 var hours = this.time.getHours();
24183 var minutes = this.time.getMinutes();
24196 hours = hours - 12;
24200 hours = '0' + hours;
24204 minutes = '0' + minutes;
24207 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24208 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24209 this.pop.select('button', true).first().dom.innerHTML = period;
24215 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24217 var cls = ['bottom'];
24219 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24226 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24230 //this.picker().setXY(20000,20000);
24231 this.picker().addClass(cls.join('-'));
24235 Roo.each(cls, function(c){
24240 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24241 //_this.picker().setTop(_this.inputEl().getHeight());
24245 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24247 //_this.picker().setTop(0 - _this.picker().getHeight());
24252 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24256 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24264 onFocus : function()
24266 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24270 onBlur : function()
24272 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24278 this.picker().show();
24283 this.fireEvent('show', this, this.date);
24288 this.picker().hide();
24291 this.fireEvent('hide', this, this.date);
24294 setTime : function()
24297 this.setValue(this.time.format(this.format));
24299 this.fireEvent('select', this, this.date);
24304 onMousedown: function(e){
24305 e.stopPropagation();
24306 e.preventDefault();
24309 onIncrementHours: function()
24311 Roo.log('onIncrementHours');
24312 this.time = this.time.add(Date.HOUR, 1);
24317 onDecrementHours: function()
24319 Roo.log('onDecrementHours');
24320 this.time = this.time.add(Date.HOUR, -1);
24324 onIncrementMinutes: function()
24326 Roo.log('onIncrementMinutes');
24327 this.time = this.time.add(Date.MINUTE, 1);
24331 onDecrementMinutes: function()
24333 Roo.log('onDecrementMinutes');
24334 this.time = this.time.add(Date.MINUTE, -1);
24338 onTogglePeriod: function()
24340 Roo.log('onTogglePeriod');
24341 this.time = this.time.add(Date.HOUR, 12);
24349 Roo.apply(Roo.bootstrap.form.TimeField, {
24353 cls: 'datepicker dropdown-menu',
24357 cls: 'datepicker-time',
24361 cls: 'table-condensed',
24390 cls: 'btn btn-info ok',
24418 * @class Roo.bootstrap.form.MonthField
24419 * @extends Roo.bootstrap.form.Input
24420 * Bootstrap MonthField class
24422 * @cfg {String} language default en
24425 * Create a new MonthField
24426 * @param {Object} config The config object
24429 Roo.bootstrap.form.MonthField = function(config){
24430 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24435 * Fires when this field show.
24436 * @param {Roo.bootstrap.form.MonthField} this
24437 * @param {Mixed} date The date value
24442 * Fires when this field hide.
24443 * @param {Roo.bootstrap.form.MonthField} this
24444 * @param {Mixed} date The date value
24449 * Fires when select a date.
24450 * @param {Roo.bootstrap.form.MonthField} this
24451 * @param {String} oldvalue The old value
24452 * @param {String} newvalue The new value
24458 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24460 onRender: function(ct, position)
24463 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24465 this.language = this.language || 'en';
24466 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24467 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24469 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24470 this.isInline = false;
24471 this.isInput = true;
24472 this.component = this.el.select('.add-on', true).first() || false;
24473 this.component = (this.component && this.component.length === 0) ? false : this.component;
24474 this.hasInput = this.component && this.inputEL().length;
24476 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24478 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24480 this.picker().on('mousedown', this.onMousedown, this);
24481 this.picker().on('click', this.onClick, this);
24483 this.picker().addClass('datepicker-dropdown');
24485 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24486 v.setStyle('width', '189px');
24493 if(this.isInline) {
24499 setValue: function(v, suppressEvent)
24501 var o = this.getValue();
24503 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24507 if(suppressEvent !== true){
24508 this.fireEvent('select', this, o, v);
24513 getValue: function()
24518 onClick: function(e)
24520 e.stopPropagation();
24521 e.preventDefault();
24523 var target = e.getTarget();
24525 if(target.nodeName.toLowerCase() === 'i'){
24526 target = Roo.get(target).dom.parentNode;
24529 var nodeName = target.nodeName;
24530 var className = target.className;
24531 var html = target.innerHTML;
24533 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24537 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24539 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24545 picker : function()
24547 return this.pickerEl;
24550 fillMonths: function()
24553 var months = this.picker().select('>.datepicker-months td', true).first();
24555 months.dom.innerHTML = '';
24561 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24564 months.createChild(month);
24573 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24574 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24577 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24578 e.removeClass('active');
24580 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24581 e.addClass('active');
24588 if(this.isInline) {
24592 this.picker().removeClass(['bottom', 'top']);
24594 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24596 * place to the top of element!
24600 this.picker().addClass('top');
24601 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24606 this.picker().addClass('bottom');
24608 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24611 onFocus : function()
24613 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24617 onBlur : function()
24619 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24621 var d = this.inputEl().getValue();
24630 this.picker().show();
24631 this.picker().select('>.datepicker-months', true).first().show();
24635 this.fireEvent('show', this, this.date);
24640 if(this.isInline) {
24643 this.picker().hide();
24644 this.fireEvent('hide', this, this.date);
24648 onMousedown: function(e)
24650 e.stopPropagation();
24651 e.preventDefault();
24656 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24660 fireKey: function(e)
24662 if (!this.picker().isVisible()){
24663 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24674 e.preventDefault();
24678 dir = e.keyCode == 37 ? -1 : 1;
24680 this.vIndex = this.vIndex + dir;
24682 if(this.vIndex < 0){
24686 if(this.vIndex > 11){
24690 if(isNaN(this.vIndex)){
24694 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24700 dir = e.keyCode == 38 ? -1 : 1;
24702 this.vIndex = this.vIndex + dir * 4;
24704 if(this.vIndex < 0){
24708 if(this.vIndex > 11){
24712 if(isNaN(this.vIndex)){
24716 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24721 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24722 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24726 e.preventDefault();
24729 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24730 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24746 this.picker().remove();
24751 Roo.apply(Roo.bootstrap.form.MonthField, {
24770 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24771 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24776 Roo.apply(Roo.bootstrap.form.MonthField, {
24780 cls: 'datepicker dropdown-menu roo-dynamic',
24784 cls: 'datepicker-months',
24788 cls: 'table-condensed',
24790 Roo.bootstrap.form.DateField.content
24810 * @class Roo.bootstrap.form.CheckBox
24811 * @extends Roo.bootstrap.form.Input
24812 * Bootstrap CheckBox class
24814 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24815 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24816 * @cfg {String} boxLabel The text that appears beside the checkbox
24817 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24818 * @cfg {Boolean} checked initnal the element
24819 * @cfg {Boolean} inline inline the element (default false)
24820 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24821 * @cfg {String} tooltip label tooltip
24824 * Create a new CheckBox
24825 * @param {Object} config The config object
24828 Roo.bootstrap.form.CheckBox = function(config){
24829 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24834 * Fires when the element is checked or unchecked.
24835 * @param {Roo.bootstrap.form.CheckBox} this This input
24836 * @param {Boolean} checked The new checked value
24841 * Fires when the element is click.
24842 * @param {Roo.bootstrap.form.CheckBox} this This input
24849 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24851 inputType: 'checkbox',
24860 // checkbox success does not make any sense really..
24865 getAutoCreate : function()
24867 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24873 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24876 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24882 type : this.inputType,
24883 value : this.inputValue,
24884 cls : 'roo-' + this.inputType, //'form-box',
24885 placeholder : this.placeholder || ''
24889 if(this.inputType != 'radio'){
24893 cls : 'roo-hidden-value',
24894 value : this.checked ? this.inputValue : this.valueOff
24899 if (this.weight) { // Validity check?
24900 cfg.cls += " " + this.inputType + "-" + this.weight;
24903 if (this.disabled) {
24904 input.disabled=true;
24908 input.checked = this.checked;
24913 input.name = this.name;
24915 if(this.inputType != 'radio'){
24916 hidden.name = this.name;
24917 input.name = '_hidden_' + this.name;
24922 input.cls += ' input-' + this.size;
24927 ['xs','sm','md','lg'].map(function(size){
24928 if (settings[size]) {
24929 cfg.cls += ' col-' + size + '-' + settings[size];
24933 var inputblock = input;
24935 if (this.before || this.after) {
24938 cls : 'input-group',
24943 inputblock.cn.push({
24945 cls : 'input-group-addon',
24950 inputblock.cn.push(input);
24952 if(this.inputType != 'radio'){
24953 inputblock.cn.push(hidden);
24957 inputblock.cn.push({
24959 cls : 'input-group-addon',
24965 var boxLabelCfg = false;
24971 //'for': id, // box label is handled by onclick - so no for...
24973 html: this.boxLabel
24976 boxLabelCfg.tooltip = this.tooltip;
24982 if (align ==='left' && this.fieldLabel.length) {
24983 // Roo.log("left and has label");
24988 cls : 'control-label',
24989 html : this.fieldLabel
25000 cfg.cn[1].cn.push(boxLabelCfg);
25003 if(this.labelWidth > 12){
25004 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25007 if(this.labelWidth < 13 && this.labelmd == 0){
25008 this.labelmd = this.labelWidth;
25011 if(this.labellg > 0){
25012 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25013 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25016 if(this.labelmd > 0){
25017 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25018 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25021 if(this.labelsm > 0){
25022 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25023 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25026 if(this.labelxs > 0){
25027 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25028 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25031 } else if ( this.fieldLabel.length) {
25032 // Roo.log(" label");
25036 tag: this.boxLabel ? 'span' : 'label',
25038 cls: 'control-label box-input-label',
25039 //cls : 'input-group-addon',
25040 html : this.fieldLabel
25047 cfg.cn.push(boxLabelCfg);
25052 // Roo.log(" no label && no align");
25053 cfg.cn = [ inputblock ] ;
25055 cfg.cn.push(boxLabelCfg);
25063 if(this.inputType != 'radio'){
25064 cfg.cn.push(hidden);
25072 * return the real input element.
25074 inputEl: function ()
25076 return this.el.select('input.roo-' + this.inputType,true).first();
25078 hiddenEl: function ()
25080 return this.el.select('input.roo-hidden-value',true).first();
25083 labelEl: function()
25085 return this.el.select('label.control-label',true).first();
25087 /* depricated... */
25091 return this.labelEl();
25094 boxLabelEl: function()
25096 return this.el.select('label.box-label',true).first();
25099 initEvents : function()
25101 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25103 this.inputEl().on('click', this.onClick, this);
25105 if (this.boxLabel) {
25106 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25109 this.startValue = this.getValue();
25112 Roo.bootstrap.form.CheckBox.register(this);
25116 onClick : function(e)
25118 if(this.fireEvent('click', this, e) !== false){
25119 this.setChecked(!this.checked);
25124 setChecked : function(state,suppressEvent)
25126 this.startValue = this.getValue();
25128 if(this.inputType == 'radio'){
25130 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25131 e.dom.checked = false;
25134 this.inputEl().dom.checked = true;
25136 this.inputEl().dom.value = this.inputValue;
25138 if(suppressEvent !== true){
25139 this.fireEvent('check', this, true);
25147 this.checked = state;
25149 this.inputEl().dom.checked = state;
25152 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25154 if(suppressEvent !== true){
25155 this.fireEvent('check', this, state);
25161 getValue : function()
25163 if(this.inputType == 'radio'){
25164 return this.getGroupValue();
25167 return this.hiddenEl().dom.value;
25171 getGroupValue : function()
25173 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25177 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25180 setValue : function(v,suppressEvent)
25182 if(this.inputType == 'radio'){
25183 this.setGroupValue(v, suppressEvent);
25187 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25192 setGroupValue : function(v, suppressEvent)
25194 this.startValue = this.getValue();
25196 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25197 e.dom.checked = false;
25199 if(e.dom.value == v){
25200 e.dom.checked = true;
25204 if(suppressEvent !== true){
25205 this.fireEvent('check', this, true);
25213 validate : function()
25215 if(this.getVisibilityEl().hasClass('hidden')){
25221 (this.inputType == 'radio' && this.validateRadio()) ||
25222 (this.inputType == 'checkbox' && this.validateCheckbox())
25228 this.markInvalid();
25232 validateRadio : function()
25234 if(this.getVisibilityEl().hasClass('hidden')){
25238 if(this.allowBlank){
25244 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25245 if(!e.dom.checked){
25257 validateCheckbox : function()
25260 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25261 //return (this.getValue() == this.inputValue) ? true : false;
25264 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25272 for(var i in group){
25273 if(group[i].el.isVisible(true)){
25281 for(var i in group){
25286 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25293 * Mark this field as valid
25295 markValid : function()
25299 this.fireEvent('valid', this);
25301 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25304 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25311 if(this.inputType == 'radio'){
25312 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25313 var fg = e.findParent('.form-group', false, true);
25314 if (Roo.bootstrap.version == 3) {
25315 fg.removeClass([_this.invalidClass, _this.validClass]);
25316 fg.addClass(_this.validClass);
25318 fg.removeClass(['is-valid', 'is-invalid']);
25319 fg.addClass('is-valid');
25327 var fg = this.el.findParent('.form-group', false, true);
25328 if (Roo.bootstrap.version == 3) {
25329 fg.removeClass([this.invalidClass, this.validClass]);
25330 fg.addClass(this.validClass);
25332 fg.removeClass(['is-valid', 'is-invalid']);
25333 fg.addClass('is-valid');
25338 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25344 for(var i in group){
25345 var fg = group[i].el.findParent('.form-group', false, true);
25346 if (Roo.bootstrap.version == 3) {
25347 fg.removeClass([this.invalidClass, this.validClass]);
25348 fg.addClass(this.validClass);
25350 fg.removeClass(['is-valid', 'is-invalid']);
25351 fg.addClass('is-valid');
25357 * Mark this field as invalid
25358 * @param {String} msg The validation message
25360 markInvalid : function(msg)
25362 if(this.allowBlank){
25368 this.fireEvent('invalid', this, msg);
25370 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25373 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25377 label.markInvalid();
25380 if(this.inputType == 'radio'){
25382 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25383 var fg = e.findParent('.form-group', false, true);
25384 if (Roo.bootstrap.version == 3) {
25385 fg.removeClass([_this.invalidClass, _this.validClass]);
25386 fg.addClass(_this.invalidClass);
25388 fg.removeClass(['is-invalid', 'is-valid']);
25389 fg.addClass('is-invalid');
25397 var fg = this.el.findParent('.form-group', false, true);
25398 if (Roo.bootstrap.version == 3) {
25399 fg.removeClass([_this.invalidClass, _this.validClass]);
25400 fg.addClass(_this.invalidClass);
25402 fg.removeClass(['is-invalid', 'is-valid']);
25403 fg.addClass('is-invalid');
25408 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25414 for(var i in group){
25415 var fg = group[i].el.findParent('.form-group', false, true);
25416 if (Roo.bootstrap.version == 3) {
25417 fg.removeClass([_this.invalidClass, _this.validClass]);
25418 fg.addClass(_this.invalidClass);
25420 fg.removeClass(['is-invalid', 'is-valid']);
25421 fg.addClass('is-invalid');
25427 clearInvalid : function()
25429 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25431 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25433 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25435 if (label && label.iconEl) {
25436 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25437 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25441 disable : function()
25443 if(this.inputType != 'radio'){
25444 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25451 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25452 _this.getActionEl().addClass(this.disabledClass);
25453 e.dom.disabled = true;
25457 this.disabled = true;
25458 this.fireEvent("disable", this);
25462 enable : function()
25464 if(this.inputType != 'radio'){
25465 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25472 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25473 _this.getActionEl().removeClass(this.disabledClass);
25474 e.dom.disabled = false;
25478 this.disabled = false;
25479 this.fireEvent("enable", this);
25483 setBoxLabel : function(v)
25488 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25494 Roo.apply(Roo.bootstrap.form.CheckBox, {
25499 * register a CheckBox Group
25500 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25502 register : function(checkbox)
25504 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25505 this.groups[checkbox.groupId] = {};
25508 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25512 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25516 * fetch a CheckBox Group based on the group ID
25517 * @param {string} the group ID
25518 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25520 get: function(groupId) {
25521 if (typeof(this.groups[groupId]) == 'undefined') {
25525 return this.groups[groupId] ;
25538 * @class Roo.bootstrap.form.Radio
25539 * @extends Roo.bootstrap.Component
25540 * Bootstrap Radio class
25541 * @cfg {String} boxLabel - the label associated
25542 * @cfg {String} value - the value of radio
25545 * Create a new Radio
25546 * @param {Object} config The config object
25548 Roo.bootstrap.form.Radio = function(config){
25549 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25553 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25559 getAutoCreate : function()
25563 cls : 'form-group radio',
25568 html : this.boxLabel
25576 initEvents : function()
25578 this.parent().register(this);
25580 this.el.on('click', this.onClick, this);
25584 onClick : function(e)
25586 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25587 this.setChecked(true);
25591 setChecked : function(state, suppressEvent)
25593 this.parent().setValue(this.value, suppressEvent);
25597 setBoxLabel : function(v)
25602 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25617 * @class Roo.bootstrap.form.SecurePass
25618 * @extends Roo.bootstrap.form.Input
25619 * Bootstrap SecurePass class
25623 * Create a new SecurePass
25624 * @param {Object} config The config object
25627 Roo.bootstrap.form.SecurePass = function (config) {
25628 // these go here, so the translation tool can replace them..
25630 PwdEmpty: "Please type a password, and then retype it to confirm.",
25631 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25632 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25633 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25634 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25635 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25636 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25637 TooWeak: "Your password is Too Weak."
25639 this.meterLabel = "Password strength:";
25640 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25641 this.meterClass = [
25642 "roo-password-meter-tooweak",
25643 "roo-password-meter-weak",
25644 "roo-password-meter-medium",
25645 "roo-password-meter-strong",
25646 "roo-password-meter-grey"
25651 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25654 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25656 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25658 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25659 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25660 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25661 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25662 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25663 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25664 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25674 * @cfg {String/Object} Label for the strength meter (defaults to
25675 * 'Password strength:')
25680 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25681 * ['Weak', 'Medium', 'Strong'])
25684 pwdStrengths: false,
25697 initEvents: function ()
25699 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25701 if (this.el.is('input[type=password]') && Roo.isSafari) {
25702 this.el.on('keydown', this.SafariOnKeyDown, this);
25705 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25708 onRender: function (ct, position)
25710 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25711 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25712 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25714 this.trigger.createChild({
25719 cls: 'roo-password-meter-grey col-xs-12',
25722 //width: this.meterWidth + 'px'
25726 cls: 'roo-password-meter-text'
25732 if (this.hideTrigger) {
25733 this.trigger.setDisplayed(false);
25735 this.setSize(this.width || '', this.height || '');
25738 onDestroy: function ()
25740 if (this.trigger) {
25741 this.trigger.removeAllListeners();
25742 this.trigger.remove();
25745 this.wrap.remove();
25747 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25750 checkStrength: function ()
25752 var pwd = this.inputEl().getValue();
25753 if (pwd == this._lastPwd) {
25758 if (this.ClientSideStrongPassword(pwd)) {
25760 } else if (this.ClientSideMediumPassword(pwd)) {
25762 } else if (this.ClientSideWeakPassword(pwd)) {
25768 Roo.log('strength1: ' + strength);
25770 //var pm = this.trigger.child('div/div/div').dom;
25771 var pm = this.trigger.child('div/div');
25772 pm.removeClass(this.meterClass);
25773 pm.addClass(this.meterClass[strength]);
25776 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25778 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25780 this._lastPwd = pwd;
25784 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25786 this._lastPwd = '';
25788 var pm = this.trigger.child('div/div');
25789 pm.removeClass(this.meterClass);
25790 pm.addClass('roo-password-meter-grey');
25793 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25796 this.inputEl().dom.type='password';
25799 validateValue: function (value)
25801 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25804 if (value.length == 0) {
25805 if (this.allowBlank) {
25806 this.clearInvalid();
25810 this.markInvalid(this.errors.PwdEmpty);
25811 this.errorMsg = this.errors.PwdEmpty;
25819 if (!value.match(/[\x21-\x7e]+/)) {
25820 this.markInvalid(this.errors.PwdBadChar);
25821 this.errorMsg = this.errors.PwdBadChar;
25824 if (value.length < 6) {
25825 this.markInvalid(this.errors.PwdShort);
25826 this.errorMsg = this.errors.PwdShort;
25829 if (value.length > 16) {
25830 this.markInvalid(this.errors.PwdLong);
25831 this.errorMsg = this.errors.PwdLong;
25835 if (this.ClientSideStrongPassword(value)) {
25837 } else if (this.ClientSideMediumPassword(value)) {
25839 } else if (this.ClientSideWeakPassword(value)) {
25846 if (strength < 2) {
25847 //this.markInvalid(this.errors.TooWeak);
25848 this.errorMsg = this.errors.TooWeak;
25853 console.log('strength2: ' + strength);
25855 //var pm = this.trigger.child('div/div/div').dom;
25857 var pm = this.trigger.child('div/div');
25858 pm.removeClass(this.meterClass);
25859 pm.addClass(this.meterClass[strength]);
25861 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25863 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25865 this.errorMsg = '';
25869 CharacterSetChecks: function (type)
25872 this.fResult = false;
25875 isctype: function (character, type)
25878 case this.kCapitalLetter:
25879 if (character >= 'A' && character <= 'Z') {
25884 case this.kSmallLetter:
25885 if (character >= 'a' && character <= 'z') {
25891 if (character >= '0' && character <= '9') {
25896 case this.kPunctuation:
25897 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25908 IsLongEnough: function (pwd, size)
25910 return !(pwd == null || isNaN(size) || pwd.length < size);
25913 SpansEnoughCharacterSets: function (word, nb)
25915 if (!this.IsLongEnough(word, nb))
25920 var characterSetChecks = new Array(
25921 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25922 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25925 for (var index = 0; index < word.length; ++index) {
25926 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25927 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25928 characterSetChecks[nCharSet].fResult = true;
25935 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25936 if (characterSetChecks[nCharSet].fResult) {
25941 if (nCharSets < nb) {
25947 ClientSideStrongPassword: function (pwd)
25949 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25952 ClientSideMediumPassword: function (pwd)
25954 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25957 ClientSideWeakPassword: function (pwd)
25959 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25962 })//<script type="text/javascript">
25965 * Based Ext JS Library 1.1.1
25966 * Copyright(c) 2006-2007, Ext JS, LLC.
25972 * @class Roo.HtmlEditorCore
25973 * @extends Roo.Component
25974 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25976 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25979 Roo.HtmlEditorCore = function(config){
25982 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25987 * @event initialize
25988 * Fires when the editor is fully initialized (including the iframe)
25989 * @param {Roo.HtmlEditorCore} this
25994 * Fires when the editor is first receives the focus. Any insertion must wait
25995 * until after this event.
25996 * @param {Roo.HtmlEditorCore} this
26000 * @event beforesync
26001 * Fires before the textarea is updated with content from the editor iframe. Return false
26002 * to cancel the sync.
26003 * @param {Roo.HtmlEditorCore} this
26004 * @param {String} html
26008 * @event beforepush
26009 * Fires before the iframe editor is updated with content from the textarea. Return false
26010 * to cancel the push.
26011 * @param {Roo.HtmlEditorCore} this
26012 * @param {String} html
26017 * Fires when the textarea is updated with content from the editor iframe.
26018 * @param {Roo.HtmlEditorCore} this
26019 * @param {String} html
26024 * Fires when the iframe editor is updated with content from the textarea.
26025 * @param {Roo.HtmlEditorCore} this
26026 * @param {String} html
26031 * @event editorevent
26032 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26033 * @param {Roo.HtmlEditorCore} this
26039 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26041 // defaults : white / black...
26042 this.applyBlacklists();
26049 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26053 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26059 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26064 * @cfg {Number} height (in pixels)
26068 * @cfg {Number} width (in pixels)
26073 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26076 stylesheets: false,
26079 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26081 allowComments: false,
26085 // private properties
26086 validationEvent : false,
26088 initialized : false,
26090 sourceEditMode : false,
26091 onFocus : Roo.emptyFn,
26093 hideMode:'offsets',
26097 // blacklist + whitelisted elements..
26104 * Protected method that will not generally be called directly. It
26105 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26106 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26108 getDocMarkup : function(){
26112 // inherit styels from page...??
26113 if (this.stylesheets === false) {
26115 Roo.get(document.head).select('style').each(function(node) {
26116 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26119 Roo.get(document.head).select('link').each(function(node) {
26120 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26123 } else if (!this.stylesheets.length) {
26125 st = '<style type="text/css">' +
26126 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26129 for (var i in this.stylesheets) {
26130 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26135 st += '<style type="text/css">' +
26136 'IMG { cursor: pointer } ' +
26139 var cls = 'roo-htmleditor-body';
26141 if(this.bodyCls.length){
26142 cls += ' ' + this.bodyCls;
26145 return '<html><head>' + st +
26146 //<style type="text/css">' +
26147 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26149 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26153 onRender : function(ct, position)
26156 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26157 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26160 this.el.dom.style.border = '0 none';
26161 this.el.dom.setAttribute('tabIndex', -1);
26162 this.el.addClass('x-hidden hide');
26166 if(Roo.isIE){ // fix IE 1px bogus margin
26167 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26171 this.frameId = Roo.id();
26175 var iframe = this.owner.wrap.createChild({
26177 cls: 'form-control', // bootstrap..
26179 name: this.frameId,
26180 frameBorder : 'no',
26181 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26186 this.iframe = iframe.dom;
26188 this.assignDocWin();
26190 this.doc.designMode = 'on';
26193 this.doc.write(this.getDocMarkup());
26197 var task = { // must defer to wait for browser to be ready
26199 //console.log("run task?" + this.doc.readyState);
26200 this.assignDocWin();
26201 if(this.doc.body || this.doc.readyState == 'complete'){
26203 this.doc.designMode="on";
26207 Roo.TaskMgr.stop(task);
26208 this.initEditor.defer(10, this);
26215 Roo.TaskMgr.start(task);
26220 onResize : function(w, h)
26222 Roo.log('resize: ' +w + ',' + h );
26223 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26227 if(typeof w == 'number'){
26229 this.iframe.style.width = w + 'px';
26231 if(typeof h == 'number'){
26233 this.iframe.style.height = h + 'px';
26235 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26242 * Toggles the editor between standard and source edit mode.
26243 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26245 toggleSourceEdit : function(sourceEditMode){
26247 this.sourceEditMode = sourceEditMode === true;
26249 if(this.sourceEditMode){
26251 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26254 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26255 //this.iframe.className = '';
26258 //this.setSize(this.owner.wrap.getSize());
26259 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26266 * Protected method that will not generally be called directly. If you need/want
26267 * custom HTML cleanup, this is the method you should override.
26268 * @param {String} html The HTML to be cleaned
26269 * return {String} The cleaned HTML
26271 cleanHtml : function(html){
26272 html = String(html);
26273 if(html.length > 5){
26274 if(Roo.isSafari){ // strip safari nonsense
26275 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26278 if(html == ' '){
26285 * HTML Editor -> Textarea
26286 * Protected method that will not generally be called directly. Syncs the contents
26287 * of the editor iframe with the textarea.
26289 syncValue : function(){
26290 if(this.initialized){
26291 var bd = (this.doc.body || this.doc.documentElement);
26292 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26293 var html = bd.innerHTML;
26295 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26296 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26298 html = '<div style="'+m[0]+'">' + html + '</div>';
26301 html = this.cleanHtml(html);
26302 // fix up the special chars.. normaly like back quotes in word...
26303 // however we do not want to do this with chinese..
26304 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26306 var cc = match.charCodeAt();
26308 // Get the character value, handling surrogate pairs
26309 if (match.length == 2) {
26310 // It's a surrogate pair, calculate the Unicode code point
26311 var high = match.charCodeAt(0) - 0xD800;
26312 var low = match.charCodeAt(1) - 0xDC00;
26313 cc = (high * 0x400) + low + 0x10000;
26315 (cc >= 0x4E00 && cc < 0xA000 ) ||
26316 (cc >= 0x3400 && cc < 0x4E00 ) ||
26317 (cc >= 0xf900 && cc < 0xfb00 )
26322 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26323 return "&#" + cc + ";";
26330 if(this.owner.fireEvent('beforesync', this, html) !== false){
26331 this.el.dom.value = html;
26332 this.owner.fireEvent('sync', this, html);
26338 * Protected method that will not generally be called directly. Pushes the value of the textarea
26339 * into the iframe editor.
26341 pushValue : function(){
26342 if(this.initialized){
26343 var v = this.el.dom.value.trim();
26345 // if(v.length < 1){
26349 if(this.owner.fireEvent('beforepush', this, v) !== false){
26350 var d = (this.doc.body || this.doc.documentElement);
26352 this.cleanUpPaste();
26353 this.el.dom.value = d.innerHTML;
26354 this.owner.fireEvent('push', this, v);
26360 deferFocus : function(){
26361 this.focus.defer(10, this);
26365 focus : function(){
26366 if(this.win && !this.sourceEditMode){
26373 assignDocWin: function()
26375 var iframe = this.iframe;
26378 this.doc = iframe.contentWindow.document;
26379 this.win = iframe.contentWindow;
26381 // if (!Roo.get(this.frameId)) {
26384 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26385 // this.win = Roo.get(this.frameId).dom.contentWindow;
26387 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26391 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26392 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26397 initEditor : function(){
26398 //console.log("INIT EDITOR");
26399 this.assignDocWin();
26403 this.doc.designMode="on";
26405 this.doc.write(this.getDocMarkup());
26408 var dbody = (this.doc.body || this.doc.documentElement);
26409 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26410 // this copies styles from the containing element into thsi one..
26411 // not sure why we need all of this..
26412 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26414 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26415 //ss['background-attachment'] = 'fixed'; // w3c
26416 dbody.bgProperties = 'fixed'; // ie
26417 //Roo.DomHelper.applyStyles(dbody, ss);
26418 Roo.EventManager.on(this.doc, {
26419 //'mousedown': this.onEditorEvent,
26420 'mouseup': this.onEditorEvent,
26421 'dblclick': this.onEditorEvent,
26422 'click': this.onEditorEvent,
26423 'keyup': this.onEditorEvent,
26428 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26430 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26431 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26433 this.initialized = true;
26435 this.owner.fireEvent('initialize', this);
26440 onDestroy : function(){
26446 //for (var i =0; i < this.toolbars.length;i++) {
26447 // // fixme - ask toolbars for heights?
26448 // this.toolbars[i].onDestroy();
26451 //this.wrap.dom.innerHTML = '';
26452 //this.wrap.remove();
26457 onFirstFocus : function(){
26459 this.assignDocWin();
26462 this.activated = true;
26465 if(Roo.isGecko){ // prevent silly gecko errors
26467 var s = this.win.getSelection();
26468 if(!s.focusNode || s.focusNode.nodeType != 3){
26469 var r = s.getRangeAt(0);
26470 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26475 this.execCmd('useCSS', true);
26476 this.execCmd('styleWithCSS', false);
26479 this.owner.fireEvent('activate', this);
26483 adjustFont: function(btn){
26484 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26485 //if(Roo.isSafari){ // safari
26488 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26489 if(Roo.isSafari){ // safari
26490 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26491 v = (v < 10) ? 10 : v;
26492 v = (v > 48) ? 48 : v;
26493 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26498 v = Math.max(1, v+adjust);
26500 this.execCmd('FontSize', v );
26503 onEditorEvent : function(e)
26505 this.owner.fireEvent('editorevent', this, e);
26506 // this.updateToolbar();
26507 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26510 insertTag : function(tg)
26512 // could be a bit smarter... -> wrap the current selected tRoo..
26513 if (tg.toLowerCase() == 'span' ||
26514 tg.toLowerCase() == 'code' ||
26515 tg.toLowerCase() == 'sup' ||
26516 tg.toLowerCase() == 'sub'
26519 range = this.createRange(this.getSelection());
26520 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26521 wrappingNode.appendChild(range.extractContents());
26522 range.insertNode(wrappingNode);
26529 this.execCmd("formatblock", tg);
26533 insertText : function(txt)
26537 var range = this.createRange();
26538 range.deleteContents();
26539 //alert(Sender.getAttribute('label'));
26541 range.insertNode(this.doc.createTextNode(txt));
26547 * Executes a Midas editor command on the editor document and performs necessary focus and
26548 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26549 * @param {String} cmd The Midas command
26550 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26552 relayCmd : function(cmd, value){
26554 this.execCmd(cmd, value);
26555 this.owner.fireEvent('editorevent', this);
26556 //this.updateToolbar();
26557 this.owner.deferFocus();
26561 * Executes a Midas editor command directly on the editor document.
26562 * For visual commands, you should use {@link #relayCmd} instead.
26563 * <b>This should only be called after the editor is initialized.</b>
26564 * @param {String} cmd The Midas command
26565 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26567 execCmd : function(cmd, value){
26568 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26575 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26577 * @param {String} text | dom node..
26579 insertAtCursor : function(text)
26582 if(!this.activated){
26588 var r = this.doc.selection.createRange();
26599 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26603 // from jquery ui (MIT licenced)
26605 var win = this.win;
26607 if (win.getSelection && win.getSelection().getRangeAt) {
26608 range = win.getSelection().getRangeAt(0);
26609 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26610 range.insertNode(node);
26611 } else if (win.document.selection && win.document.selection.createRange) {
26612 // no firefox support
26613 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26614 win.document.selection.createRange().pasteHTML(txt);
26616 // no firefox support
26617 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26618 this.execCmd('InsertHTML', txt);
26627 mozKeyPress : function(e){
26629 var c = e.getCharCode(), cmd;
26632 c = String.fromCharCode(c).toLowerCase();
26646 this.cleanUpPaste.defer(100, this);
26654 e.preventDefault();
26662 fixKeys : function(){ // load time branching for fastest keydown performance
26664 return function(e){
26665 var k = e.getKey(), r;
26668 r = this.doc.selection.createRange();
26671 r.pasteHTML('    ');
26678 r = this.doc.selection.createRange();
26680 var target = r.parentElement();
26681 if(!target || target.tagName.toLowerCase() != 'li'){
26683 r.pasteHTML('<br />');
26689 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26690 this.cleanUpPaste.defer(100, this);
26696 }else if(Roo.isOpera){
26697 return function(e){
26698 var k = e.getKey();
26702 this.execCmd('InsertHTML','    ');
26705 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26706 this.cleanUpPaste.defer(100, this);
26711 }else if(Roo.isSafari){
26712 return function(e){
26713 var k = e.getKey();
26717 this.execCmd('InsertText','\t');
26721 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26722 this.cleanUpPaste.defer(100, this);
26730 getAllAncestors: function()
26732 var p = this.getSelectedNode();
26735 a.push(p); // push blank onto stack..
26736 p = this.getParentElement();
26740 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26744 a.push(this.doc.body);
26748 lastSelNode : false,
26751 getSelection : function()
26753 this.assignDocWin();
26754 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26757 getSelectedNode: function()
26759 // this may only work on Gecko!!!
26761 // should we cache this!!!!
26766 var range = this.createRange(this.getSelection()).cloneRange();
26769 var parent = range.parentElement();
26771 var testRange = range.duplicate();
26772 testRange.moveToElementText(parent);
26773 if (testRange.inRange(range)) {
26776 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26779 parent = parent.parentElement;
26784 // is ancestor a text element.
26785 var ac = range.commonAncestorContainer;
26786 if (ac.nodeType == 3) {
26787 ac = ac.parentNode;
26790 var ar = ac.childNodes;
26793 var other_nodes = [];
26794 var has_other_nodes = false;
26795 for (var i=0;i<ar.length;i++) {
26796 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26799 // fullly contained node.
26801 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26806 // probably selected..
26807 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26808 other_nodes.push(ar[i]);
26812 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26817 has_other_nodes = true;
26819 if (!nodes.length && other_nodes.length) {
26820 nodes= other_nodes;
26822 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26828 createRange: function(sel)
26830 // this has strange effects when using with
26831 // top toolbar - not sure if it's a great idea.
26832 //this.editor.contentWindow.focus();
26833 if (typeof sel != "undefined") {
26835 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26837 return this.doc.createRange();
26840 return this.doc.createRange();
26843 getParentElement: function()
26846 this.assignDocWin();
26847 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26849 var range = this.createRange(sel);
26852 var p = range.commonAncestorContainer;
26853 while (p.nodeType == 3) { // text node
26864 * Range intersection.. the hard stuff...
26868 * [ -- selected range --- ]
26872 * if end is before start or hits it. fail.
26873 * if start is after end or hits it fail.
26875 * if either hits (but other is outside. - then it's not
26881 // @see http://www.thismuchiknow.co.uk/?p=64.
26882 rangeIntersectsNode : function(range, node)
26884 var nodeRange = node.ownerDocument.createRange();
26886 nodeRange.selectNode(node);
26888 nodeRange.selectNodeContents(node);
26891 var rangeStartRange = range.cloneRange();
26892 rangeStartRange.collapse(true);
26894 var rangeEndRange = range.cloneRange();
26895 rangeEndRange.collapse(false);
26897 var nodeStartRange = nodeRange.cloneRange();
26898 nodeStartRange.collapse(true);
26900 var nodeEndRange = nodeRange.cloneRange();
26901 nodeEndRange.collapse(false);
26903 return rangeStartRange.compareBoundaryPoints(
26904 Range.START_TO_START, nodeEndRange) == -1 &&
26905 rangeEndRange.compareBoundaryPoints(
26906 Range.START_TO_START, nodeStartRange) == 1;
26910 rangeCompareNode : function(range, node)
26912 var nodeRange = node.ownerDocument.createRange();
26914 nodeRange.selectNode(node);
26916 nodeRange.selectNodeContents(node);
26920 range.collapse(true);
26922 nodeRange.collapse(true);
26924 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26925 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26927 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26929 var nodeIsBefore = ss == 1;
26930 var nodeIsAfter = ee == -1;
26932 if (nodeIsBefore && nodeIsAfter) {
26935 if (!nodeIsBefore && nodeIsAfter) {
26936 return 1; //right trailed.
26939 if (nodeIsBefore && !nodeIsAfter) {
26940 return 2; // left trailed.
26946 // private? - in a new class?
26947 cleanUpPaste : function()
26949 // cleans up the whole document..
26950 Roo.log('cleanuppaste');
26952 this.cleanUpChildren(this.doc.body);
26953 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26954 if (clean != this.doc.body.innerHTML) {
26955 this.doc.body.innerHTML = clean;
26960 cleanWordChars : function(input) {// change the chars to hex code
26961 var he = Roo.HtmlEditorCore;
26963 var output = input;
26964 Roo.each(he.swapCodes, function(sw) {
26965 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26967 output = output.replace(swapper, sw[1]);
26974 cleanUpChildren : function (n)
26976 if (!n.childNodes.length) {
26979 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26980 this.cleanUpChild(n.childNodes[i]);
26987 cleanUpChild : function (node)
26990 //console.log(node);
26991 if (node.nodeName == "#text") {
26992 // clean up silly Windows -- stuff?
26995 if (node.nodeName == "#comment") {
26996 if (!this.allowComments) {
26997 node.parentNode.removeChild(node);
26999 // clean up silly Windows -- stuff?
27002 var lcname = node.tagName.toLowerCase();
27003 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27004 // whitelist of tags..
27006 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27008 node.parentNode.removeChild(node);
27013 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27015 // spans with no attributes - just remove them..
27016 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27017 remove_keep_children = true;
27020 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27021 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27023 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27024 // remove_keep_children = true;
27027 if (remove_keep_children) {
27028 this.cleanUpChildren(node);
27029 // inserts everything just before this node...
27030 while (node.childNodes.length) {
27031 var cn = node.childNodes[0];
27032 node.removeChild(cn);
27033 node.parentNode.insertBefore(cn, node);
27035 node.parentNode.removeChild(node);
27039 if (!node.attributes || !node.attributes.length) {
27044 this.cleanUpChildren(node);
27048 function cleanAttr(n,v)
27051 if (v.match(/^\./) || v.match(/^\//)) {
27054 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27057 if (v.match(/^#/)) {
27060 if (v.match(/^\{/)) { // allow template editing.
27063 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27064 node.removeAttribute(n);
27068 var cwhite = this.cwhite;
27069 var cblack = this.cblack;
27071 function cleanStyle(n,v)
27073 if (v.match(/expression/)) { //XSS?? should we even bother..
27074 node.removeAttribute(n);
27078 var parts = v.split(/;/);
27081 Roo.each(parts, function(p) {
27082 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27086 var l = p.split(':').shift().replace(/\s+/g,'');
27087 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27089 if ( cwhite.length && cblack.indexOf(l) > -1) {
27090 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27091 //node.removeAttribute(n);
27095 // only allow 'c whitelisted system attributes'
27096 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27097 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27098 //node.removeAttribute(n);
27108 if (clean.length) {
27109 node.setAttribute(n, clean.join(';'));
27111 node.removeAttribute(n);
27117 for (var i = node.attributes.length-1; i > -1 ; i--) {
27118 var a = node.attributes[i];
27121 if (a.name.toLowerCase().substr(0,2)=='on') {
27122 node.removeAttribute(a.name);
27125 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27126 node.removeAttribute(a.name);
27129 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27130 cleanAttr(a.name,a.value); // fixme..
27133 if (a.name == 'style') {
27134 cleanStyle(a.name,a.value);
27137 /// clean up MS crap..
27138 // tecnically this should be a list of valid class'es..
27141 if (a.name == 'class') {
27142 if (a.value.match(/^Mso/)) {
27143 node.removeAttribute('class');
27146 if (a.value.match(/^body$/)) {
27147 node.removeAttribute('class');
27158 this.cleanUpChildren(node);
27164 * Clean up MS wordisms...
27166 cleanWord : function(node)
27169 this.cleanWord(this.doc.body);
27174 node.nodeName == 'SPAN' &&
27175 !node.hasAttributes() &&
27176 node.childNodes.length == 1 &&
27177 node.firstChild.nodeName == "#text"
27179 var textNode = node.firstChild;
27180 node.removeChild(textNode);
27181 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27182 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27184 node.parentNode.insertBefore(textNode, node);
27185 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27186 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27188 node.parentNode.removeChild(node);
27191 if (node.nodeName == "#text") {
27192 // clean up silly Windows -- stuff?
27195 if (node.nodeName == "#comment") {
27196 node.parentNode.removeChild(node);
27197 // clean up silly Windows -- stuff?
27201 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27202 node.parentNode.removeChild(node);
27205 //Roo.log(node.tagName);
27206 // remove - but keep children..
27207 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27208 //Roo.log('-- removed');
27209 while (node.childNodes.length) {
27210 var cn = node.childNodes[0];
27211 node.removeChild(cn);
27212 node.parentNode.insertBefore(cn, node);
27213 // move node to parent - and clean it..
27214 this.cleanWord(cn);
27216 node.parentNode.removeChild(node);
27217 /// no need to iterate chidlren = it's got none..
27218 //this.iterateChildren(node, this.cleanWord);
27222 if (node.className.length) {
27224 var cn = node.className.split(/\W+/);
27226 Roo.each(cn, function(cls) {
27227 if (cls.match(/Mso[a-zA-Z]+/)) {
27232 node.className = cna.length ? cna.join(' ') : '';
27234 node.removeAttribute("class");
27238 if (node.hasAttribute("lang")) {
27239 node.removeAttribute("lang");
27242 if (node.hasAttribute("style")) {
27244 var styles = node.getAttribute("style").split(";");
27246 Roo.each(styles, function(s) {
27247 if (!s.match(/:/)) {
27250 var kv = s.split(":");
27251 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27254 // what ever is left... we allow.
27257 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27258 if (!nstyle.length) {
27259 node.removeAttribute('style');
27262 this.iterateChildren(node, this.cleanWord);
27268 * iterateChildren of a Node, calling fn each time, using this as the scole..
27269 * @param {DomNode} node node to iterate children of.
27270 * @param {Function} fn method of this class to call on each item.
27272 iterateChildren : function(node, fn)
27274 if (!node.childNodes.length) {
27277 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27278 fn.call(this, node.childNodes[i])
27284 * cleanTableWidths.
27286 * Quite often pasting from word etc.. results in tables with column and widths.
27287 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27290 cleanTableWidths : function(node)
27295 this.cleanTableWidths(this.doc.body);
27300 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27303 Roo.log(node.tagName);
27304 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27305 this.iterateChildren(node, this.cleanTableWidths);
27308 if (node.hasAttribute('width')) {
27309 node.removeAttribute('width');
27313 if (node.hasAttribute("style")) {
27316 var styles = node.getAttribute("style").split(";");
27318 Roo.each(styles, function(s) {
27319 if (!s.match(/:/)) {
27322 var kv = s.split(":");
27323 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27326 // what ever is left... we allow.
27329 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27330 if (!nstyle.length) {
27331 node.removeAttribute('style');
27335 this.iterateChildren(node, this.cleanTableWidths);
27343 domToHTML : function(currentElement, depth, nopadtext) {
27345 depth = depth || 0;
27346 nopadtext = nopadtext || false;
27348 if (!currentElement) {
27349 return this.domToHTML(this.doc.body);
27352 //Roo.log(currentElement);
27354 var allText = false;
27355 var nodeName = currentElement.nodeName;
27356 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27358 if (nodeName == '#text') {
27360 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27365 if (nodeName != 'BODY') {
27368 // Prints the node tagName, such as <A>, <IMG>, etc
27371 for(i = 0; i < currentElement.attributes.length;i++) {
27373 var aname = currentElement.attributes.item(i).name;
27374 if (!currentElement.attributes.item(i).value.length) {
27377 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27380 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27389 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27392 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27397 // Traverse the tree
27399 var currentElementChild = currentElement.childNodes.item(i);
27400 var allText = true;
27401 var innerHTML = '';
27403 while (currentElementChild) {
27404 // Formatting code (indent the tree so it looks nice on the screen)
27405 var nopad = nopadtext;
27406 if (lastnode == 'SPAN') {
27410 if (currentElementChild.nodeName == '#text') {
27411 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27412 toadd = nopadtext ? toadd : toadd.trim();
27413 if (!nopad && toadd.length > 80) {
27414 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27416 innerHTML += toadd;
27419 currentElementChild = currentElement.childNodes.item(i);
27425 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27427 // Recursively traverse the tree structure of the child node
27428 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27429 lastnode = currentElementChild.nodeName;
27431 currentElementChild=currentElement.childNodes.item(i);
27437 // The remaining code is mostly for formatting the tree
27438 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27443 ret+= "</"+tagName+">";
27449 applyBlacklists : function()
27451 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27452 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27456 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27457 if (b.indexOf(tag) > -1) {
27460 this.white.push(tag);
27464 Roo.each(w, function(tag) {
27465 if (b.indexOf(tag) > -1) {
27468 if (this.white.indexOf(tag) > -1) {
27471 this.white.push(tag);
27476 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27477 if (w.indexOf(tag) > -1) {
27480 this.black.push(tag);
27484 Roo.each(b, function(tag) {
27485 if (w.indexOf(tag) > -1) {
27488 if (this.black.indexOf(tag) > -1) {
27491 this.black.push(tag);
27496 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27497 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27501 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27502 if (b.indexOf(tag) > -1) {
27505 this.cwhite.push(tag);
27509 Roo.each(w, function(tag) {
27510 if (b.indexOf(tag) > -1) {
27513 if (this.cwhite.indexOf(tag) > -1) {
27516 this.cwhite.push(tag);
27521 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27522 if (w.indexOf(tag) > -1) {
27525 this.cblack.push(tag);
27529 Roo.each(b, function(tag) {
27530 if (w.indexOf(tag) > -1) {
27533 if (this.cblack.indexOf(tag) > -1) {
27536 this.cblack.push(tag);
27541 setStylesheets : function(stylesheets)
27543 if(typeof(stylesheets) == 'string'){
27544 Roo.get(this.iframe.contentDocument.head).createChild({
27546 rel : 'stylesheet',
27555 Roo.each(stylesheets, function(s) {
27560 Roo.get(_this.iframe.contentDocument.head).createChild({
27562 rel : 'stylesheet',
27571 removeStylesheets : function()
27575 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27580 setStyle : function(style)
27582 Roo.get(this.iframe.contentDocument.head).createChild({
27591 // hide stuff that is not compatible
27605 * @event specialkey
27609 * @cfg {String} fieldClass @hide
27612 * @cfg {String} focusClass @hide
27615 * @cfg {String} autoCreate @hide
27618 * @cfg {String} inputType @hide
27621 * @cfg {String} invalidClass @hide
27624 * @cfg {String} invalidText @hide
27627 * @cfg {String} msgFx @hide
27630 * @cfg {String} validateOnBlur @hide
27634 Roo.HtmlEditorCore.white = [
27635 'area', 'br', 'img', 'input', 'hr', 'wbr',
27637 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27638 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27639 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27640 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27641 'table', 'ul', 'xmp',
27643 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27646 'dir', 'menu', 'ol', 'ul', 'dl',
27652 Roo.HtmlEditorCore.black = [
27653 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27655 'base', 'basefont', 'bgsound', 'blink', 'body',
27656 'frame', 'frameset', 'head', 'html', 'ilayer',
27657 'iframe', 'layer', 'link', 'meta', 'object',
27658 'script', 'style' ,'title', 'xml' // clean later..
27660 Roo.HtmlEditorCore.clean = [
27661 'script', 'style', 'title', 'xml'
27663 Roo.HtmlEditorCore.remove = [
27668 Roo.HtmlEditorCore.ablack = [
27672 Roo.HtmlEditorCore.aclean = [
27673 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27677 Roo.HtmlEditorCore.pwhite= [
27678 'http', 'https', 'mailto'
27681 // white listed style attributes.
27682 Roo.HtmlEditorCore.cwhite= [
27683 // 'text-align', /// default is to allow most things..
27689 // black listed style attributes.
27690 Roo.HtmlEditorCore.cblack= [
27691 // 'font-size' -- this can be set by the project
27695 Roo.HtmlEditorCore.swapCodes =[
27696 [ 8211, "–" ],
27697 [ 8212, "—" ],
27714 * @class Roo.bootstrap.form.HtmlEditor
27715 * @extends Roo.bootstrap.form.TextArea
27716 * Bootstrap HtmlEditor class
27719 * Create a new HtmlEditor
27720 * @param {Object} config The config object
27723 Roo.bootstrap.form.HtmlEditor = function(config){
27724 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27725 if (!this.toolbars) {
27726 this.toolbars = [];
27729 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27732 * @event initialize
27733 * Fires when the editor is fully initialized (including the iframe)
27734 * @param {HtmlEditor} this
27739 * Fires when the editor is first receives the focus. Any insertion must wait
27740 * until after this event.
27741 * @param {HtmlEditor} this
27745 * @event beforesync
27746 * Fires before the textarea is updated with content from the editor iframe. Return false
27747 * to cancel the sync.
27748 * @param {HtmlEditor} this
27749 * @param {String} html
27753 * @event beforepush
27754 * Fires before the iframe editor is updated with content from the textarea. Return false
27755 * to cancel the push.
27756 * @param {HtmlEditor} this
27757 * @param {String} html
27762 * Fires when the textarea is updated with content from the editor iframe.
27763 * @param {HtmlEditor} this
27764 * @param {String} html
27769 * Fires when the iframe editor is updated with content from the textarea.
27770 * @param {HtmlEditor} this
27771 * @param {String} html
27775 * @event editmodechange
27776 * Fires when the editor switches edit modes
27777 * @param {HtmlEditor} this
27778 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27780 editmodechange: true,
27782 * @event editorevent
27783 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27784 * @param {HtmlEditor} this
27788 * @event firstfocus
27789 * Fires when on first focus - needed by toolbars..
27790 * @param {HtmlEditor} this
27795 * Auto save the htmlEditor value as a file into Events
27796 * @param {HtmlEditor} this
27800 * @event savedpreview
27801 * preview the saved version of htmlEditor
27802 * @param {HtmlEditor} this
27809 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27813 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27818 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27823 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27828 * @cfg {Number} height (in pixels)
27832 * @cfg {Number} width (in pixels)
27837 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27840 stylesheets: false,
27845 // private properties
27846 validationEvent : false,
27848 initialized : false,
27851 onFocus : Roo.emptyFn,
27853 hideMode:'offsets',
27855 tbContainer : false,
27859 toolbarContainer :function() {
27860 return this.wrap.select('.x-html-editor-tb',true).first();
27864 * Protected method that will not generally be called directly. It
27865 * is called when the editor creates its toolbar. Override this method if you need to
27866 * add custom toolbar buttons.
27867 * @param {HtmlEditor} editor
27869 createToolbar : function(){
27870 Roo.log('renewing');
27871 Roo.log("create toolbars");
27873 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27874 this.toolbars[0].render(this.toolbarContainer());
27878 // if (!editor.toolbars || !editor.toolbars.length) {
27879 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27882 // for (var i =0 ; i < editor.toolbars.length;i++) {
27883 // editor.toolbars[i] = Roo.factory(
27884 // typeof(editor.toolbars[i]) == 'string' ?
27885 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27886 // Roo.bootstrap.form.HtmlEditor);
27887 // editor.toolbars[i].init(editor);
27893 onRender : function(ct, position)
27895 // Roo.log("Call onRender: " + this.xtype);
27897 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27899 this.wrap = this.inputEl().wrap({
27900 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27903 this.editorcore.onRender(ct, position);
27905 if (this.resizable) {
27906 this.resizeEl = new Roo.Resizable(this.wrap, {
27910 minHeight : this.height,
27911 height: this.height,
27912 handles : this.resizable,
27915 resize : function(r, w, h) {
27916 _t.onResize(w,h); // -something
27922 this.createToolbar(this);
27925 if(!this.width && this.resizable){
27926 this.setSize(this.wrap.getSize());
27928 if (this.resizeEl) {
27929 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27930 // should trigger onReize..
27936 onResize : function(w, h)
27938 Roo.log('resize: ' +w + ',' + h );
27939 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27943 if(this.inputEl() ){
27944 if(typeof w == 'number'){
27945 var aw = w - this.wrap.getFrameWidth('lr');
27946 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27949 if(typeof h == 'number'){
27950 var tbh = -11; // fixme it needs to tool bar size!
27951 for (var i =0; i < this.toolbars.length;i++) {
27952 // fixme - ask toolbars for heights?
27953 tbh += this.toolbars[i].el.getHeight();
27954 //if (this.toolbars[i].footer) {
27955 // tbh += this.toolbars[i].footer.el.getHeight();
27963 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27964 ah -= 5; // knock a few pixes off for look..
27965 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27969 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27970 this.editorcore.onResize(ew,eh);
27975 * Toggles the editor between standard and source edit mode.
27976 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27978 toggleSourceEdit : function(sourceEditMode)
27980 this.editorcore.toggleSourceEdit(sourceEditMode);
27982 if(this.editorcore.sourceEditMode){
27983 Roo.log('editor - showing textarea');
27986 // Roo.log(this.syncValue());
27988 this.inputEl().removeClass(['hide', 'x-hidden']);
27989 this.inputEl().dom.removeAttribute('tabIndex');
27990 this.inputEl().focus();
27992 Roo.log('editor - hiding textarea');
27994 // Roo.log(this.pushValue());
27997 this.inputEl().addClass(['hide', 'x-hidden']);
27998 this.inputEl().dom.setAttribute('tabIndex', -1);
27999 //this.deferFocus();
28002 if(this.resizable){
28003 this.setSize(this.wrap.getSize());
28006 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28009 // private (for BoxComponent)
28010 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28012 // private (for BoxComponent)
28013 getResizeEl : function(){
28017 // private (for BoxComponent)
28018 getPositionEl : function(){
28023 initEvents : function(){
28024 this.originalValue = this.getValue();
28028 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28031 // markInvalid : Roo.emptyFn,
28033 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28036 // clearInvalid : Roo.emptyFn,
28038 setValue : function(v){
28039 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28040 this.editorcore.pushValue();
28045 deferFocus : function(){
28046 this.focus.defer(10, this);
28050 focus : function(){
28051 this.editorcore.focus();
28057 onDestroy : function(){
28063 for (var i =0; i < this.toolbars.length;i++) {
28064 // fixme - ask toolbars for heights?
28065 this.toolbars[i].onDestroy();
28068 this.wrap.dom.innerHTML = '';
28069 this.wrap.remove();
28074 onFirstFocus : function(){
28075 //Roo.log("onFirstFocus");
28076 this.editorcore.onFirstFocus();
28077 for (var i =0; i < this.toolbars.length;i++) {
28078 this.toolbars[i].onFirstFocus();
28084 syncValue : function()
28086 this.editorcore.syncValue();
28089 pushValue : function()
28091 this.editorcore.pushValue();
28095 // hide stuff that is not compatible
28109 * @event specialkey
28113 * @cfg {String} fieldClass @hide
28116 * @cfg {String} focusClass @hide
28119 * @cfg {String} autoCreate @hide
28122 * @cfg {String} inputType @hide
28126 * @cfg {String} invalidText @hide
28129 * @cfg {String} msgFx @hide
28132 * @cfg {String} validateOnBlur @hide
28141 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28143 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28144 * @parent Roo.bootstrap.form.HtmlEditor
28145 * @extends Roo.bootstrap.nav.Simplebar
28151 new Roo.bootstrap.form.HtmlEditor({
28154 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28155 disable : { fonts: 1 , format: 1, ..., ... , ...],
28161 * @cfg {Object} disable List of elements to disable..
28162 * @cfg {Array} btns List of additional buttons.
28166 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28169 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28172 Roo.apply(this, config);
28174 // default disabled, based on 'good practice'..
28175 this.disable = this.disable || {};
28176 Roo.applyIf(this.disable, {
28179 specialElements : true
28181 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28183 this.editor = config.editor;
28184 this.editorcore = config.editor.editorcore;
28186 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28188 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28189 // dont call parent... till later.
28191 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28196 editorcore : false,
28201 "h1","h2","h3","h4","h5","h6",
28203 "abbr", "acronym", "address", "cite", "samp", "var",
28207 onRender : function(ct, position)
28209 // Roo.log("Call onRender: " + this.xtype);
28211 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28213 this.el.dom.style.marginBottom = '0';
28215 var editorcore = this.editorcore;
28216 var editor= this.editor;
28219 var btn = function(id,cmd , toggle, handler, html){
28221 var event = toggle ? 'toggle' : 'click';
28226 xns: Roo.bootstrap,
28230 enableToggle:toggle !== false,
28232 pressed : toggle ? false : null,
28235 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28236 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28242 // var cb_box = function...
28247 xns: Roo.bootstrap,
28252 xns: Roo.bootstrap,
28256 Roo.each(this.formats, function(f) {
28257 style.menu.items.push({
28259 xns: Roo.bootstrap,
28260 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28265 editorcore.insertTag(this.tagname);
28272 children.push(style);
28274 btn('bold',false,true);
28275 btn('italic',false,true);
28276 btn('align-left', 'justifyleft',true);
28277 btn('align-center', 'justifycenter',true);
28278 btn('align-right' , 'justifyright',true);
28279 btn('link', false, false, function(btn) {
28280 //Roo.log("create link?");
28281 var url = prompt(this.createLinkText, this.defaultLinkValue);
28282 if(url && url != 'http:/'+'/'){
28283 this.editorcore.relayCmd('createlink', url);
28286 btn('list','insertunorderedlist',true);
28287 btn('pencil', false,true, function(btn){
28289 this.toggleSourceEdit(btn.pressed);
28292 if (this.editor.btns.length > 0) {
28293 for (var i = 0; i<this.editor.btns.length; i++) {
28294 children.push(this.editor.btns[i]);
28302 xns: Roo.bootstrap,
28307 xns: Roo.bootstrap,
28312 cog.menu.items.push({
28314 xns: Roo.bootstrap,
28315 html : Clean styles,
28320 editorcore.insertTag(this.tagname);
28329 this.xtype = 'NavSimplebar';
28331 for(var i=0;i< children.length;i++) {
28333 this.buttons.add(this.addxtypeChild(children[i]));
28337 editor.on('editorevent', this.updateToolbar, this);
28339 onBtnClick : function(id)
28341 this.editorcore.relayCmd(id);
28342 this.editorcore.focus();
28346 * Protected method that will not generally be called directly. It triggers
28347 * a toolbar update by reading the markup state of the current selection in the editor.
28349 updateToolbar: function(){
28351 if(!this.editorcore.activated){
28352 this.editor.onFirstFocus(); // is this neeed?
28356 var btns = this.buttons;
28357 var doc = this.editorcore.doc;
28358 btns.get('bold').setActive(doc.queryCommandState('bold'));
28359 btns.get('italic').setActive(doc.queryCommandState('italic'));
28360 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28362 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28363 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28364 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28366 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28367 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28370 var ans = this.editorcore.getAllAncestors();
28371 if (this.formatCombo) {
28374 var store = this.formatCombo.store;
28375 this.formatCombo.setValue("");
28376 for (var i =0; i < ans.length;i++) {
28377 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28379 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28387 // hides menus... - so this cant be on a menu...
28388 Roo.bootstrap.MenuMgr.hideAll();
28390 Roo.bootstrap.menu.Manager.hideAll();
28391 //this.editorsyncValue();
28393 onFirstFocus: function() {
28394 this.buttons.each(function(item){
28398 toggleSourceEdit : function(sourceEditMode){
28401 if(sourceEditMode){
28402 Roo.log("disabling buttons");
28403 this.buttons.each( function(item){
28404 if(item.cmd != 'pencil'){
28410 Roo.log("enabling buttons");
28411 if(this.editorcore.initialized){
28412 this.buttons.each( function(item){
28418 Roo.log("calling toggole on editor");
28419 // tell the editor that it's been pressed..
28420 this.editor.toggleSourceEdit(sourceEditMode);
28434 * @class Roo.bootstrap.form.Markdown
28435 * @extends Roo.bootstrap.form.TextArea
28436 * Bootstrap Showdown editable area
28437 * @cfg {string} content
28440 * Create a new Showdown
28443 Roo.bootstrap.form.Markdown = function(config){
28444 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28448 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28452 initEvents : function()
28455 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28456 this.markdownEl = this.el.createChild({
28457 cls : 'roo-markdown-area'
28459 this.inputEl().addClass('d-none');
28460 if (this.getValue() == '') {
28461 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28464 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28466 this.markdownEl.on('click', this.toggleTextEdit, this);
28467 this.on('blur', this.toggleTextEdit, this);
28468 this.on('specialkey', this.resizeTextArea, this);
28471 toggleTextEdit : function()
28473 var sh = this.markdownEl.getHeight();
28474 this.inputEl().addClass('d-none');
28475 this.markdownEl.addClass('d-none');
28476 if (!this.editing) {
28478 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28479 this.inputEl().removeClass('d-none');
28480 this.inputEl().focus();
28481 this.editing = true;
28484 // show showdown...
28485 this.updateMarkdown();
28486 this.markdownEl.removeClass('d-none');
28487 this.editing = false;
28490 updateMarkdown : function()
28492 if (this.getValue() == '') {
28493 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28497 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28500 resizeTextArea: function () {
28503 Roo.log([sh, this.getValue().split("\n").length * 30]);
28504 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28506 setValue : function(val)
28508 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28509 if (!this.editing) {
28510 this.updateMarkdown();
28516 if (!this.editing) {
28517 this.toggleTextEdit();
28525 * Ext JS Library 1.1.1
28526 * Copyright(c) 2006-2007, Ext JS, LLC.
28528 * Originally Released Under LGPL - original licence link has changed is not relivant.
28531 * <script type="text/javascript">
28535 * @class Roo.bootstrap.PagingToolbar
28536 * @extends Roo.bootstrap.nav.Simplebar
28537 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28539 * Create a new PagingToolbar
28540 * @param {Object} config The config object
28541 * @param {Roo.data.Store} store
28543 Roo.bootstrap.PagingToolbar = function(config)
28545 // old args format still supported... - xtype is prefered..
28546 // created from xtype...
28548 this.ds = config.dataSource;
28550 if (config.store && !this.ds) {
28551 this.store= Roo.factory(config.store, Roo.data);
28552 this.ds = this.store;
28553 this.ds.xmodule = this.xmodule || false;
28556 this.toolbarItems = [];
28557 if (config.items) {
28558 this.toolbarItems = config.items;
28561 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28566 this.bind(this.ds);
28569 if (Roo.bootstrap.version == 4) {
28570 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28572 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28577 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28579 * @cfg {Roo.bootstrap.Button} buttons[]
28580 * Buttons for the toolbar
28583 * @cfg {Roo.data.Store} store
28584 * The underlying data store providing the paged data
28587 * @cfg {String/HTMLElement/Element} container
28588 * container The id or element that will contain the toolbar
28591 * @cfg {Boolean} displayInfo
28592 * True to display the displayMsg (defaults to false)
28595 * @cfg {Number} pageSize
28596 * The number of records to display per page (defaults to 20)
28600 * @cfg {String} displayMsg
28601 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28603 displayMsg : 'Displaying {0} - {1} of {2}',
28605 * @cfg {String} emptyMsg
28606 * The message to display when no records are found (defaults to "No data to display")
28608 emptyMsg : 'No data to display',
28610 * Customizable piece of the default paging text (defaults to "Page")
28613 beforePageText : "Page",
28615 * Customizable piece of the default paging text (defaults to "of %0")
28618 afterPageText : "of {0}",
28620 * Customizable piece of the default paging text (defaults to "First Page")
28623 firstText : "First Page",
28625 * Customizable piece of the default paging text (defaults to "Previous Page")
28628 prevText : "Previous Page",
28630 * Customizable piece of the default paging text (defaults to "Next Page")
28633 nextText : "Next Page",
28635 * Customizable piece of the default paging text (defaults to "Last Page")
28638 lastText : "Last Page",
28640 * Customizable piece of the default paging text (defaults to "Refresh")
28643 refreshText : "Refresh",
28647 onRender : function(ct, position)
28649 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28650 this.navgroup.parentId = this.id;
28651 this.navgroup.onRender(this.el, null);
28652 // add the buttons to the navgroup
28654 if(this.displayInfo){
28655 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28656 this.displayEl = this.el.select('.x-paging-info', true).first();
28657 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28658 // this.displayEl = navel.el.select('span',true).first();
28664 Roo.each(_this.buttons, function(e){ // this might need to use render????
28665 Roo.factory(e).render(_this.el);
28669 Roo.each(_this.toolbarItems, function(e) {
28670 _this.navgroup.addItem(e);
28674 this.first = this.navgroup.addItem({
28675 tooltip: this.firstText,
28676 cls: "prev btn-outline-secondary",
28677 html : ' <i class="fa fa-step-backward"></i>',
28679 preventDefault: true,
28680 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28683 this.prev = this.navgroup.addItem({
28684 tooltip: this.prevText,
28685 cls: "prev btn-outline-secondary",
28686 html : ' <i class="fa fa-backward"></i>',
28688 preventDefault: true,
28689 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28691 //this.addSeparator();
28694 var field = this.navgroup.addItem( {
28696 cls : 'x-paging-position btn-outline-secondary',
28698 html : this.beforePageText +
28699 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28700 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28703 this.field = field.el.select('input', true).first();
28704 this.field.on("keydown", this.onPagingKeydown, this);
28705 this.field.on("focus", function(){this.dom.select();});
28708 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28709 //this.field.setHeight(18);
28710 //this.addSeparator();
28711 this.next = this.navgroup.addItem({
28712 tooltip: this.nextText,
28713 cls: "next btn-outline-secondary",
28714 html : ' <i class="fa fa-forward"></i>',
28716 preventDefault: true,
28717 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28719 this.last = this.navgroup.addItem({
28720 tooltip: this.lastText,
28721 html : ' <i class="fa fa-step-forward"></i>',
28722 cls: "next btn-outline-secondary",
28724 preventDefault: true,
28725 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28727 //this.addSeparator();
28728 this.loading = this.navgroup.addItem({
28729 tooltip: this.refreshText,
28730 cls: "btn-outline-secondary",
28731 html : ' <i class="fa fa-refresh"></i>',
28732 preventDefault: true,
28733 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28739 updateInfo : function(){
28740 if(this.displayEl){
28741 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28742 var msg = count == 0 ?
28746 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28748 this.displayEl.update(msg);
28753 onLoad : function(ds, r, o)
28755 this.cursor = o.params && o.params.start ? o.params.start : 0;
28757 var d = this.getPageData(),
28762 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28763 this.field.dom.value = ap;
28764 this.first.setDisabled(ap == 1);
28765 this.prev.setDisabled(ap == 1);
28766 this.next.setDisabled(ap == ps);
28767 this.last.setDisabled(ap == ps);
28768 this.loading.enable();
28773 getPageData : function(){
28774 var total = this.ds.getTotalCount();
28777 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28778 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28783 onLoadError : function(proxy, o){
28784 this.loading.enable();
28785 if (this.ds.events.loadexception.listeners.length < 2) {
28786 // nothing has been assigned to loadexception except this...
28788 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28794 onPagingKeydown : function(e){
28795 var k = e.getKey();
28796 var d = this.getPageData();
28798 var v = this.field.dom.value, pageNum;
28799 if(!v || isNaN(pageNum = parseInt(v, 10))){
28800 this.field.dom.value = d.activePage;
28803 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28804 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28807 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))
28809 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28810 this.field.dom.value = pageNum;
28811 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28814 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28816 var v = this.field.dom.value, pageNum;
28817 var increment = (e.shiftKey) ? 10 : 1;
28818 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28821 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28822 this.field.dom.value = d.activePage;
28825 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28827 this.field.dom.value = parseInt(v, 10) + increment;
28828 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28829 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28836 beforeLoad : function(){
28838 this.loading.disable();
28843 onClick : function(which){
28852 ds.load({params:{start: 0, limit: this.pageSize}});
28855 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28858 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28861 var total = ds.getTotalCount();
28862 var extra = total % this.pageSize;
28863 var lastStart = extra ? (total - extra) : total-this.pageSize;
28864 ds.load({params:{start: lastStart, limit: this.pageSize}});
28867 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28873 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28874 * @param {Roo.data.Store} store The data store to unbind
28876 unbind : function(ds){
28877 ds.un("beforeload", this.beforeLoad, this);
28878 ds.un("load", this.onLoad, this);
28879 ds.un("loadexception", this.onLoadError, this);
28880 ds.un("remove", this.updateInfo, this);
28881 ds.un("add", this.updateInfo, this);
28882 this.ds = undefined;
28886 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28887 * @param {Roo.data.Store} store The data store to bind
28889 bind : function(ds){
28890 ds.on("beforeload", this.beforeLoad, this);
28891 ds.on("load", this.onLoad, this);
28892 ds.on("loadexception", this.onLoadError, this);
28893 ds.on("remove", this.updateInfo, this);
28894 ds.on("add", this.updateInfo, this);
28905 * @class Roo.bootstrap.MessageBar
28906 * @extends Roo.bootstrap.Component
28907 * Bootstrap MessageBar class
28908 * @cfg {String} html contents of the MessageBar
28909 * @cfg {String} weight (info | success | warning | danger) default info
28910 * @cfg {String} beforeClass insert the bar before the given class
28911 * @cfg {Boolean} closable (true | false) default false
28912 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28915 * Create a new Element
28916 * @param {Object} config The config object
28919 Roo.bootstrap.MessageBar = function(config){
28920 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28923 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28929 beforeClass: 'bootstrap-sticky-wrap',
28931 getAutoCreate : function(){
28935 cls: 'alert alert-dismissable alert-' + this.weight,
28940 html: this.html || ''
28946 cfg.cls += ' alert-messages-fixed';
28960 onRender : function(ct, position)
28962 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28965 var cfg = Roo.apply({}, this.getAutoCreate());
28969 cfg.cls += ' ' + this.cls;
28972 cfg.style = this.style;
28974 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28976 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28979 this.el.select('>button.close').on('click', this.hide, this);
28985 if (!this.rendered) {
28991 this.fireEvent('show', this);
28997 if (!this.rendered) {
29003 this.fireEvent('hide', this);
29006 update : function()
29008 // var e = this.el.dom.firstChild;
29010 // if(this.closable){
29011 // e = e.nextSibling;
29014 // e.data = this.html || '';
29016 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29032 * @class Roo.bootstrap.Graph
29033 * @extends Roo.bootstrap.Component
29034 * Bootstrap Graph class
29038 @cfg {String} graphtype bar | vbar | pie
29039 @cfg {number} g_x coodinator | centre x (pie)
29040 @cfg {number} g_y coodinator | centre y (pie)
29041 @cfg {number} g_r radius (pie)
29042 @cfg {number} g_height height of the chart (respected by all elements in the set)
29043 @cfg {number} g_width width of the chart (respected by all elements in the set)
29044 @cfg {Object} title The title of the chart
29047 -opts (object) options for the chart
29049 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29050 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29052 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.
29053 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29055 o stretch (boolean)
29057 -opts (object) options for the pie
29060 o startAngle (number)
29061 o endAngle (number)
29065 * Create a new Input
29066 * @param {Object} config The config object
29069 Roo.bootstrap.Graph = function(config){
29070 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29076 * The img click event for the img.
29077 * @param {Roo.EventObject} e
29083 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29094 //g_colors: this.colors,
29101 getAutoCreate : function(){
29112 onRender : function(ct,position){
29115 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29117 if (typeof(Raphael) == 'undefined') {
29118 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29122 this.raphael = Raphael(this.el.dom);
29124 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29125 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29126 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29127 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29129 r.text(160, 10, "Single Series Chart").attr(txtattr);
29130 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29131 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29132 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29134 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29135 r.barchart(330, 10, 300, 220, data1);
29136 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29137 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29140 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29141 // r.barchart(30, 30, 560, 250, xdata, {
29142 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29143 // axis : "0 0 1 1",
29144 // axisxlabels : xdata
29145 // //yvalues : cols,
29148 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29150 // this.load(null,xdata,{
29151 // axis : "0 0 1 1",
29152 // axisxlabels : xdata
29157 load : function(graphtype,xdata,opts)
29159 this.raphael.clear();
29161 graphtype = this.graphtype;
29166 var r = this.raphael,
29167 fin = function () {
29168 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29170 fout = function () {
29171 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29173 pfin = function() {
29174 this.sector.stop();
29175 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29178 this.label[0].stop();
29179 this.label[0].attr({ r: 7.5 });
29180 this.label[1].attr({ "font-weight": 800 });
29183 pfout = function() {
29184 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29187 this.label[0].animate({ r: 5 }, 500, "bounce");
29188 this.label[1].attr({ "font-weight": 400 });
29194 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29197 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29200 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29201 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29203 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29210 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29215 setTitle: function(o)
29220 initEvents: function() {
29223 this.el.on('click', this.onClick, this);
29227 onClick : function(e)
29229 Roo.log('img onclick');
29230 this.fireEvent('click', this, e);
29242 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29245 * @class Roo.bootstrap.dash.NumberBox
29246 * @extends Roo.bootstrap.Component
29247 * Bootstrap NumberBox class
29248 * @cfg {String} headline Box headline
29249 * @cfg {String} content Box content
29250 * @cfg {String} icon Box icon
29251 * @cfg {String} footer Footer text
29252 * @cfg {String} fhref Footer href
29255 * Create a new NumberBox
29256 * @param {Object} config The config object
29260 Roo.bootstrap.dash.NumberBox = function(config){
29261 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29265 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29274 getAutoCreate : function(){
29278 cls : 'small-box ',
29286 cls : 'roo-headline',
29287 html : this.headline
29291 cls : 'roo-content',
29292 html : this.content
29306 cls : 'ion ' + this.icon
29315 cls : 'small-box-footer',
29316 href : this.fhref || '#',
29320 cfg.cn.push(footer);
29327 onRender : function(ct,position){
29328 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29335 setHeadline: function (value)
29337 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29340 setFooter: function (value, href)
29342 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29345 this.el.select('a.small-box-footer',true).first().attr('href', href);
29350 setContent: function (value)
29352 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29355 initEvents: function()
29369 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29372 * @class Roo.bootstrap.dash.TabBox
29373 * @extends Roo.bootstrap.Component
29374 * @children Roo.bootstrap.dash.TabPane
29375 * Bootstrap TabBox class
29376 * @cfg {String} title Title of the TabBox
29377 * @cfg {String} icon Icon of the TabBox
29378 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29379 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29382 * Create a new TabBox
29383 * @param {Object} config The config object
29387 Roo.bootstrap.dash.TabBox = function(config){
29388 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29393 * When a pane is added
29394 * @param {Roo.bootstrap.dash.TabPane} pane
29398 * @event activatepane
29399 * When a pane is activated
29400 * @param {Roo.bootstrap.dash.TabPane} pane
29402 "activatepane" : true
29410 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29415 tabScrollable : false,
29417 getChildContainer : function()
29419 return this.el.select('.tab-content', true).first();
29422 getAutoCreate : function(){
29426 cls: 'pull-left header',
29434 cls: 'fa ' + this.icon
29440 cls: 'nav nav-tabs pull-right',
29446 if(this.tabScrollable){
29453 cls: 'nav nav-tabs pull-right',
29464 cls: 'nav-tabs-custom',
29469 cls: 'tab-content no-padding',
29477 initEvents : function()
29479 //Roo.log('add add pane handler');
29480 this.on('addpane', this.onAddPane, this);
29483 * Updates the box title
29484 * @param {String} html to set the title to.
29486 setTitle : function(value)
29488 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29490 onAddPane : function(pane)
29492 this.panes.push(pane);
29493 //Roo.log('addpane');
29495 // tabs are rendere left to right..
29496 if(!this.showtabs){
29500 var ctr = this.el.select('.nav-tabs', true).first();
29503 var existing = ctr.select('.nav-tab',true);
29504 var qty = existing.getCount();;
29507 var tab = ctr.createChild({
29509 cls : 'nav-tab' + (qty ? '' : ' active'),
29517 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29520 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29522 pane.el.addClass('active');
29527 onTabClick : function(ev,un,ob,pane)
29529 //Roo.log('tab - prev default');
29530 ev.preventDefault();
29533 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29534 pane.tab.addClass('active');
29535 //Roo.log(pane.title);
29536 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29537 // technically we should have a deactivate event.. but maybe add later.
29538 // and it should not de-activate the selected tab...
29539 this.fireEvent('activatepane', pane);
29540 pane.el.addClass('active');
29541 pane.fireEvent('activate');
29546 getActivePane : function()
29549 Roo.each(this.panes, function(p) {
29550 if(p.el.hasClass('active')){
29571 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29573 * @class Roo.bootstrap.TabPane
29574 * @extends Roo.bootstrap.Component
29575 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29576 * Bootstrap TabPane class
29577 * @cfg {Boolean} active (false | true) Default false
29578 * @cfg {String} title title of panel
29582 * Create a new TabPane
29583 * @param {Object} config The config object
29586 Roo.bootstrap.dash.TabPane = function(config){
29587 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29593 * When a pane is activated
29594 * @param {Roo.bootstrap.dash.TabPane} pane
29601 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29606 // the tabBox that this is attached to.
29609 getAutoCreate : function()
29617 cfg.cls += ' active';
29622 initEvents : function()
29624 //Roo.log('trigger add pane handler');
29625 this.parent().fireEvent('addpane', this)
29629 * Updates the tab title
29630 * @param {String} html to set the title to.
29632 setTitle: function(str)
29638 this.tab.select('a', true).first().dom.innerHTML = str;
29657 * @class Roo.bootstrap.Tooltip
29658 * Bootstrap Tooltip class
29659 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29660 * to determine which dom element triggers the tooltip.
29662 * It needs to add support for additional attributes like tooltip-position
29665 * Create a new Toolti
29666 * @param {Object} config The config object
29669 Roo.bootstrap.Tooltip = function(config){
29670 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29672 this.alignment = Roo.bootstrap.Tooltip.alignment;
29674 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29675 this.alignment = config.alignment;
29680 Roo.apply(Roo.bootstrap.Tooltip, {
29682 * @function init initialize tooltip monitoring.
29686 currentTip : false,
29687 currentRegion : false,
29693 Roo.get(document).on('mouseover', this.enter ,this);
29694 Roo.get(document).on('mouseout', this.leave, this);
29697 this.currentTip = new Roo.bootstrap.Tooltip();
29700 enter : function(ev)
29702 var dom = ev.getTarget();
29704 //Roo.log(['enter',dom]);
29705 var el = Roo.fly(dom);
29706 if (this.currentEl) {
29708 //Roo.log(this.currentEl);
29709 //Roo.log(this.currentEl.contains(dom));
29710 if (this.currentEl == el) {
29713 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29719 if (this.currentTip.el) {
29720 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29724 if(!el || el.dom == document){
29730 if (!el.attr('tooltip')) {
29731 pel = el.findParent("[tooltip]");
29733 bindEl = Roo.get(pel);
29739 // you can not look for children, as if el is the body.. then everythign is the child..
29740 if (!pel && !el.attr('tooltip')) { //
29741 if (!el.select("[tooltip]").elements.length) {
29744 // is the mouse over this child...?
29745 bindEl = el.select("[tooltip]").first();
29746 var xy = ev.getXY();
29747 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29748 //Roo.log("not in region.");
29751 //Roo.log("child element over..");
29754 this.currentEl = el;
29755 this.currentTip.bind(bindEl);
29756 this.currentRegion = Roo.lib.Region.getRegion(dom);
29757 this.currentTip.enter();
29760 leave : function(ev)
29762 var dom = ev.getTarget();
29763 //Roo.log(['leave',dom]);
29764 if (!this.currentEl) {
29769 if (dom != this.currentEl.dom) {
29772 var xy = ev.getXY();
29773 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29776 // only activate leave if mouse cursor is outside... bounding box..
29781 if (this.currentTip) {
29782 this.currentTip.leave();
29784 //Roo.log('clear currentEl');
29785 this.currentEl = false;
29790 'left' : ['r-l', [-2,0], 'right'],
29791 'right' : ['l-r', [2,0], 'left'],
29792 'bottom' : ['t-b', [0,2], 'top'],
29793 'top' : [ 'b-t', [0,-2], 'bottom']
29799 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29804 delay : null, // can be { show : 300 , hide: 500}
29808 hoverState : null, //???
29810 placement : 'bottom',
29814 getAutoCreate : function(){
29821 cls : 'tooltip-arrow arrow'
29824 cls : 'tooltip-inner'
29831 bind : function(el)
29836 initEvents : function()
29838 this.arrowEl = this.el.select('.arrow', true).first();
29839 this.innerEl = this.el.select('.tooltip-inner', true).first();
29842 enter : function () {
29844 if (this.timeout != null) {
29845 clearTimeout(this.timeout);
29848 this.hoverState = 'in';
29849 //Roo.log("enter - show");
29850 if (!this.delay || !this.delay.show) {
29855 this.timeout = setTimeout(function () {
29856 if (_t.hoverState == 'in') {
29859 }, this.delay.show);
29863 clearTimeout(this.timeout);
29865 this.hoverState = 'out';
29866 if (!this.delay || !this.delay.hide) {
29872 this.timeout = setTimeout(function () {
29873 //Roo.log("leave - timeout");
29875 if (_t.hoverState == 'out') {
29877 Roo.bootstrap.Tooltip.currentEl = false;
29882 show : function (msg)
29885 this.render(document.body);
29888 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29890 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29892 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29894 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29895 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29897 var placement = typeof this.placement == 'function' ?
29898 this.placement.call(this, this.el, on_el) :
29901 var autoToken = /\s?auto?\s?/i;
29902 var autoPlace = autoToken.test(placement);
29904 placement = placement.replace(autoToken, '') || 'top';
29908 //this.el.setXY([0,0]);
29910 //this.el.dom.style.display='block';
29912 //this.el.appendTo(on_el);
29914 var p = this.getPosition();
29915 var box = this.el.getBox();
29921 var align = this.alignment[placement];
29923 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29925 if(placement == 'top' || placement == 'bottom'){
29927 placement = 'right';
29930 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29931 placement = 'left';
29934 var scroll = Roo.select('body', true).first().getScroll();
29936 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29940 align = this.alignment[placement];
29942 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29946 var elems = document.getElementsByTagName('div');
29947 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29948 for (var i = 0; i < elems.length; i++) {
29949 var zindex = Number.parseInt(
29950 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29953 if (zindex > highest) {
29960 this.el.dom.style.zIndex = highest;
29962 this.el.alignTo(this.bindEl, align[0],align[1]);
29963 //var arrow = this.el.select('.arrow',true).first();
29964 //arrow.set(align[2],
29966 this.el.addClass(placement);
29967 this.el.addClass("bs-tooltip-"+ placement);
29969 this.el.addClass('in fade show');
29971 this.hoverState = null;
29973 if (this.el.hasClass('fade')) {
29988 //this.el.setXY([0,0]);
29989 this.el.removeClass(['show', 'in']);
30005 * @class Roo.bootstrap.LocationPicker
30006 * @extends Roo.bootstrap.Component
30007 * Bootstrap LocationPicker class
30008 * @cfg {Number} latitude Position when init default 0
30009 * @cfg {Number} longitude Position when init default 0
30010 * @cfg {Number} zoom default 15
30011 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30012 * @cfg {Boolean} mapTypeControl default false
30013 * @cfg {Boolean} disableDoubleClickZoom default false
30014 * @cfg {Boolean} scrollwheel default true
30015 * @cfg {Boolean} streetViewControl default false
30016 * @cfg {Number} radius default 0
30017 * @cfg {String} locationName
30018 * @cfg {Boolean} draggable default true
30019 * @cfg {Boolean} enableAutocomplete default false
30020 * @cfg {Boolean} enableReverseGeocode default true
30021 * @cfg {String} markerTitle
30024 * Create a new LocationPicker
30025 * @param {Object} config The config object
30029 Roo.bootstrap.LocationPicker = function(config){
30031 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30036 * Fires when the picker initialized.
30037 * @param {Roo.bootstrap.LocationPicker} this
30038 * @param {Google Location} location
30042 * @event positionchanged
30043 * Fires when the picker position changed.
30044 * @param {Roo.bootstrap.LocationPicker} this
30045 * @param {Google Location} location
30047 positionchanged : true,
30050 * Fires when the map resize.
30051 * @param {Roo.bootstrap.LocationPicker} this
30056 * Fires when the map show.
30057 * @param {Roo.bootstrap.LocationPicker} this
30062 * Fires when the map hide.
30063 * @param {Roo.bootstrap.LocationPicker} this
30068 * Fires when click the map.
30069 * @param {Roo.bootstrap.LocationPicker} this
30070 * @param {Map event} e
30074 * @event mapRightClick
30075 * Fires when right click the map.
30076 * @param {Roo.bootstrap.LocationPicker} this
30077 * @param {Map event} e
30079 mapRightClick : true,
30081 * @event markerClick
30082 * Fires when click the marker.
30083 * @param {Roo.bootstrap.LocationPicker} this
30084 * @param {Map event} e
30086 markerClick : true,
30088 * @event markerRightClick
30089 * Fires when right click the marker.
30090 * @param {Roo.bootstrap.LocationPicker} this
30091 * @param {Map event} e
30093 markerRightClick : true,
30095 * @event OverlayViewDraw
30096 * Fires when OverlayView Draw
30097 * @param {Roo.bootstrap.LocationPicker} this
30099 OverlayViewDraw : true,
30101 * @event OverlayViewOnAdd
30102 * Fires when OverlayView Draw
30103 * @param {Roo.bootstrap.LocationPicker} this
30105 OverlayViewOnAdd : true,
30107 * @event OverlayViewOnRemove
30108 * Fires when OverlayView Draw
30109 * @param {Roo.bootstrap.LocationPicker} this
30111 OverlayViewOnRemove : true,
30113 * @event OverlayViewShow
30114 * Fires when OverlayView Draw
30115 * @param {Roo.bootstrap.LocationPicker} this
30116 * @param {Pixel} cpx
30118 OverlayViewShow : true,
30120 * @event OverlayViewHide
30121 * Fires when OverlayView Draw
30122 * @param {Roo.bootstrap.LocationPicker} this
30124 OverlayViewHide : true,
30126 * @event loadexception
30127 * Fires when load google lib failed.
30128 * @param {Roo.bootstrap.LocationPicker} this
30130 loadexception : true
30135 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30137 gMapContext: false,
30143 mapTypeControl: false,
30144 disableDoubleClickZoom: false,
30146 streetViewControl: false,
30150 enableAutocomplete: false,
30151 enableReverseGeocode: true,
30154 getAutoCreate: function()
30159 cls: 'roo-location-picker'
30165 initEvents: function(ct, position)
30167 if(!this.el.getWidth() || this.isApplied()){
30171 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30176 initial: function()
30178 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30179 this.fireEvent('loadexception', this);
30183 if(!this.mapTypeId){
30184 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30187 this.gMapContext = this.GMapContext();
30189 this.initOverlayView();
30191 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30195 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30196 _this.setPosition(_this.gMapContext.marker.position);
30199 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30200 _this.fireEvent('mapClick', this, event);
30204 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30205 _this.fireEvent('mapRightClick', this, event);
30209 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30210 _this.fireEvent('markerClick', this, event);
30214 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30215 _this.fireEvent('markerRightClick', this, event);
30219 this.setPosition(this.gMapContext.location);
30221 this.fireEvent('initial', this, this.gMapContext.location);
30224 initOverlayView: function()
30228 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30232 _this.fireEvent('OverlayViewDraw', _this);
30237 _this.fireEvent('OverlayViewOnAdd', _this);
30240 onRemove: function()
30242 _this.fireEvent('OverlayViewOnRemove', _this);
30245 show: function(cpx)
30247 _this.fireEvent('OverlayViewShow', _this, cpx);
30252 _this.fireEvent('OverlayViewHide', _this);
30258 fromLatLngToContainerPixel: function(event)
30260 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30263 isApplied: function()
30265 return this.getGmapContext() == false ? false : true;
30268 getGmapContext: function()
30270 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30273 GMapContext: function()
30275 var position = new google.maps.LatLng(this.latitude, this.longitude);
30277 var _map = new google.maps.Map(this.el.dom, {
30280 mapTypeId: this.mapTypeId,
30281 mapTypeControl: this.mapTypeControl,
30282 disableDoubleClickZoom: this.disableDoubleClickZoom,
30283 scrollwheel: this.scrollwheel,
30284 streetViewControl: this.streetViewControl,
30285 locationName: this.locationName,
30286 draggable: this.draggable,
30287 enableAutocomplete: this.enableAutocomplete,
30288 enableReverseGeocode: this.enableReverseGeocode
30291 var _marker = new google.maps.Marker({
30292 position: position,
30294 title: this.markerTitle,
30295 draggable: this.draggable
30302 location: position,
30303 radius: this.radius,
30304 locationName: this.locationName,
30305 addressComponents: {
30306 formatted_address: null,
30307 addressLine1: null,
30308 addressLine2: null,
30310 streetNumber: null,
30314 stateOrProvince: null
30317 domContainer: this.el.dom,
30318 geodecoder: new google.maps.Geocoder()
30322 drawCircle: function(center, radius, options)
30324 if (this.gMapContext.circle != null) {
30325 this.gMapContext.circle.setMap(null);
30329 options = Roo.apply({}, options, {
30330 strokeColor: "#0000FF",
30331 strokeOpacity: .35,
30333 fillColor: "#0000FF",
30337 options.map = this.gMapContext.map;
30338 options.radius = radius;
30339 options.center = center;
30340 this.gMapContext.circle = new google.maps.Circle(options);
30341 return this.gMapContext.circle;
30347 setPosition: function(location)
30349 this.gMapContext.location = location;
30350 this.gMapContext.marker.setPosition(location);
30351 this.gMapContext.map.panTo(location);
30352 this.drawCircle(location, this.gMapContext.radius, {});
30356 if (this.gMapContext.settings.enableReverseGeocode) {
30357 this.gMapContext.geodecoder.geocode({
30358 latLng: this.gMapContext.location
30359 }, function(results, status) {
30361 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30362 _this.gMapContext.locationName = results[0].formatted_address;
30363 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30365 _this.fireEvent('positionchanged', this, location);
30372 this.fireEvent('positionchanged', this, location);
30377 google.maps.event.trigger(this.gMapContext.map, "resize");
30379 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30381 this.fireEvent('resize', this);
30384 setPositionByLatLng: function(latitude, longitude)
30386 this.setPosition(new google.maps.LatLng(latitude, longitude));
30389 getCurrentPosition: function()
30392 latitude: this.gMapContext.location.lat(),
30393 longitude: this.gMapContext.location.lng()
30397 getAddressName: function()
30399 return this.gMapContext.locationName;
30402 getAddressComponents: function()
30404 return this.gMapContext.addressComponents;
30407 address_component_from_google_geocode: function(address_components)
30411 for (var i = 0; i < address_components.length; i++) {
30412 var component = address_components[i];
30413 if (component.types.indexOf("postal_code") >= 0) {
30414 result.postalCode = component.short_name;
30415 } else if (component.types.indexOf("street_number") >= 0) {
30416 result.streetNumber = component.short_name;
30417 } else if (component.types.indexOf("route") >= 0) {
30418 result.streetName = component.short_name;
30419 } else if (component.types.indexOf("neighborhood") >= 0) {
30420 result.city = component.short_name;
30421 } else if (component.types.indexOf("locality") >= 0) {
30422 result.city = component.short_name;
30423 } else if (component.types.indexOf("sublocality") >= 0) {
30424 result.district = component.short_name;
30425 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30426 result.stateOrProvince = component.short_name;
30427 } else if (component.types.indexOf("country") >= 0) {
30428 result.country = component.short_name;
30432 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30433 result.addressLine2 = "";
30437 setZoomLevel: function(zoom)
30439 this.gMapContext.map.setZoom(zoom);
30452 this.fireEvent('show', this);
30463 this.fireEvent('hide', this);
30468 Roo.apply(Roo.bootstrap.LocationPicker, {
30470 OverlayView : function(map, options)
30472 options = options || {};
30479 * @class Roo.bootstrap.Alert
30480 * @extends Roo.bootstrap.Component
30481 * Bootstrap Alert class - shows an alert area box
30483 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30484 Enter a valid email address
30487 * @cfg {String} title The title of alert
30488 * @cfg {String} html The content of alert
30489 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30490 * @cfg {String} fa font-awesomeicon
30491 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30492 * @cfg {Boolean} close true to show a x closer
30496 * Create a new alert
30497 * @param {Object} config The config object
30501 Roo.bootstrap.Alert = function(config){
30502 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30506 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30512 faicon: false, // BC
30516 getAutoCreate : function()
30528 style : this.close ? '' : 'display:none'
30532 cls : 'roo-alert-icon'
30537 cls : 'roo-alert-title',
30542 cls : 'roo-alert-text',
30549 cfg.cn[0].cls += ' fa ' + this.faicon;
30552 cfg.cn[0].cls += ' fa ' + this.fa;
30556 cfg.cls += ' alert-' + this.weight;
30562 initEvents: function()
30564 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30565 this.titleEl = this.el.select('.roo-alert-title',true).first();
30566 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30567 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30568 if (this.seconds > 0) {
30569 this.hide.defer(this.seconds, this);
30573 * Set the Title Message HTML
30574 * @param {String} html
30576 setTitle : function(str)
30578 this.titleEl.dom.innerHTML = str;
30582 * Set the Body Message HTML
30583 * @param {String} html
30585 setHtml : function(str)
30587 this.htmlEl.dom.innerHTML = str;
30590 * Set the Weight of the alert
30591 * @param {String} (success|info|warning|danger) weight
30594 setWeight : function(weight)
30597 this.el.removeClass('alert-' + this.weight);
30600 this.weight = weight;
30602 this.el.addClass('alert-' + this.weight);
30605 * Set the Icon of the alert
30606 * @param {String} see fontawsome names (name without the 'fa-' bit)
30608 setIcon : function(icon)
30611 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30614 this.faicon = icon;
30616 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30641 * @class Roo.bootstrap.UploadCropbox
30642 * @extends Roo.bootstrap.Component
30643 * Bootstrap UploadCropbox class
30644 * @cfg {String} emptyText show when image has been loaded
30645 * @cfg {String} rotateNotify show when image too small to rotate
30646 * @cfg {Number} errorTimeout default 3000
30647 * @cfg {Number} minWidth default 300
30648 * @cfg {Number} minHeight default 300
30649 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30650 * @cfg {Boolean} isDocument (true|false) default false
30651 * @cfg {String} url action url
30652 * @cfg {String} paramName default 'imageUpload'
30653 * @cfg {String} method default POST
30654 * @cfg {Boolean} loadMask (true|false) default true
30655 * @cfg {Boolean} loadingText default 'Loading...'
30658 * Create a new UploadCropbox
30659 * @param {Object} config The config object
30662 Roo.bootstrap.UploadCropbox = function(config){
30663 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30667 * @event beforeselectfile
30668 * Fire before select file
30669 * @param {Roo.bootstrap.UploadCropbox} this
30671 "beforeselectfile" : true,
30674 * Fire after initEvent
30675 * @param {Roo.bootstrap.UploadCropbox} this
30680 * Fire after initEvent
30681 * @param {Roo.bootstrap.UploadCropbox} this
30682 * @param {String} data
30687 * Fire when preparing the file data
30688 * @param {Roo.bootstrap.UploadCropbox} this
30689 * @param {Object} file
30694 * Fire when get exception
30695 * @param {Roo.bootstrap.UploadCropbox} this
30696 * @param {XMLHttpRequest} xhr
30698 "exception" : true,
30700 * @event beforeloadcanvas
30701 * Fire before load the canvas
30702 * @param {Roo.bootstrap.UploadCropbox} this
30703 * @param {String} src
30705 "beforeloadcanvas" : true,
30708 * Fire when trash image
30709 * @param {Roo.bootstrap.UploadCropbox} this
30714 * Fire when download the image
30715 * @param {Roo.bootstrap.UploadCropbox} this
30719 * @event footerbuttonclick
30720 * Fire when footerbuttonclick
30721 * @param {Roo.bootstrap.UploadCropbox} this
30722 * @param {String} type
30724 "footerbuttonclick" : true,
30728 * @param {Roo.bootstrap.UploadCropbox} this
30733 * Fire when rotate the image
30734 * @param {Roo.bootstrap.UploadCropbox} this
30735 * @param {String} pos
30740 * Fire when inspect the file
30741 * @param {Roo.bootstrap.UploadCropbox} this
30742 * @param {Object} file
30747 * Fire when xhr upload the file
30748 * @param {Roo.bootstrap.UploadCropbox} this
30749 * @param {Object} data
30754 * Fire when arrange the file data
30755 * @param {Roo.bootstrap.UploadCropbox} this
30756 * @param {Object} formData
30761 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30764 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30766 emptyText : 'Click to upload image',
30767 rotateNotify : 'Image is too small to rotate',
30768 errorTimeout : 3000,
30782 cropType : 'image/jpeg',
30784 canvasLoaded : false,
30785 isDocument : false,
30787 paramName : 'imageUpload',
30789 loadingText : 'Loading...',
30792 getAutoCreate : function()
30796 cls : 'roo-upload-cropbox',
30800 cls : 'roo-upload-cropbox-selector',
30805 cls : 'roo-upload-cropbox-body',
30806 style : 'cursor:pointer',
30810 cls : 'roo-upload-cropbox-preview'
30814 cls : 'roo-upload-cropbox-thumb'
30818 cls : 'roo-upload-cropbox-empty-notify',
30819 html : this.emptyText
30823 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30824 html : this.rotateNotify
30830 cls : 'roo-upload-cropbox-footer',
30833 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30843 onRender : function(ct, position)
30845 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30847 if (this.buttons.length) {
30849 Roo.each(this.buttons, function(bb) {
30851 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30853 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30859 this.maskEl = this.el;
30863 initEvents : function()
30865 this.urlAPI = (window.createObjectURL && window) ||
30866 (window.URL && URL.revokeObjectURL && URL) ||
30867 (window.webkitURL && webkitURL);
30869 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30870 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30872 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30873 this.selectorEl.hide();
30875 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30876 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30878 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30879 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880 this.thumbEl.hide();
30882 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30883 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30885 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30886 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30887 this.errorEl.hide();
30889 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30890 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30891 this.footerEl.hide();
30893 this.setThumbBoxSize();
30899 this.fireEvent('initial', this);
30906 window.addEventListener("resize", function() { _this.resize(); } );
30908 this.bodyEl.on('click', this.beforeSelectFile, this);
30911 this.bodyEl.on('touchstart', this.onTouchStart, this);
30912 this.bodyEl.on('touchmove', this.onTouchMove, this);
30913 this.bodyEl.on('touchend', this.onTouchEnd, this);
30917 this.bodyEl.on('mousedown', this.onMouseDown, this);
30918 this.bodyEl.on('mousemove', this.onMouseMove, this);
30919 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30920 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30921 Roo.get(document).on('mouseup', this.onMouseUp, this);
30924 this.selectorEl.on('change', this.onFileSelected, this);
30930 this.baseScale = 1;
30932 this.baseRotate = 1;
30933 this.dragable = false;
30934 this.pinching = false;
30937 this.cropData = false;
30938 this.notifyEl.dom.innerHTML = this.emptyText;
30940 this.selectorEl.dom.value = '';
30944 resize : function()
30946 if(this.fireEvent('resize', this) != false){
30947 this.setThumbBoxPosition();
30948 this.setCanvasPosition();
30952 onFooterButtonClick : function(e, el, o, type)
30955 case 'rotate-left' :
30956 this.onRotateLeft(e);
30958 case 'rotate-right' :
30959 this.onRotateRight(e);
30962 this.beforeSelectFile(e);
30977 this.fireEvent('footerbuttonclick', this, type);
30980 beforeSelectFile : function(e)
30982 e.preventDefault();
30984 if(this.fireEvent('beforeselectfile', this) != false){
30985 this.selectorEl.dom.click();
30989 onFileSelected : function(e)
30991 e.preventDefault();
30993 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30997 var file = this.selectorEl.dom.files[0];
30999 if(this.fireEvent('inspect', this, file) != false){
31000 this.prepare(file);
31005 trash : function(e)
31007 this.fireEvent('trash', this);
31010 download : function(e)
31012 this.fireEvent('download', this);
31015 loadCanvas : function(src)
31017 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31021 this.imageEl = document.createElement('img');
31025 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31027 this.imageEl.src = src;
31031 onLoadCanvas : function()
31033 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31034 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31036 this.bodyEl.un('click', this.beforeSelectFile, this);
31038 this.notifyEl.hide();
31039 this.thumbEl.show();
31040 this.footerEl.show();
31042 this.baseRotateLevel();
31044 if(this.isDocument){
31045 this.setThumbBoxSize();
31048 this.setThumbBoxPosition();
31050 this.baseScaleLevel();
31056 this.canvasLoaded = true;
31059 this.maskEl.unmask();
31064 setCanvasPosition : function()
31066 if(!this.canvasEl){
31070 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31071 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31073 this.previewEl.setLeft(pw);
31074 this.previewEl.setTop(ph);
31078 onMouseDown : function(e)
31082 this.dragable = true;
31083 this.pinching = false;
31085 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31086 this.dragable = false;
31090 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31091 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31095 onMouseMove : function(e)
31099 if(!this.canvasLoaded){
31103 if (!this.dragable){
31107 var minX = Math.ceil(this.thumbEl.getLeft(true));
31108 var minY = Math.ceil(this.thumbEl.getTop(true));
31110 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31111 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31113 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31114 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31116 x = x - this.mouseX;
31117 y = y - this.mouseY;
31119 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31120 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31122 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31123 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31125 this.previewEl.setLeft(bgX);
31126 this.previewEl.setTop(bgY);
31128 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31129 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31132 onMouseUp : function(e)
31136 this.dragable = false;
31139 onMouseWheel : function(e)
31143 this.startScale = this.scale;
31145 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31147 if(!this.zoomable()){
31148 this.scale = this.startScale;
31157 zoomable : function()
31159 var minScale = this.thumbEl.getWidth() / this.minWidth;
31161 if(this.minWidth < this.minHeight){
31162 minScale = this.thumbEl.getHeight() / this.minHeight;
31165 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31166 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31170 (this.rotate == 0 || this.rotate == 180) &&
31172 width > this.imageEl.OriginWidth ||
31173 height > this.imageEl.OriginHeight ||
31174 (width < this.minWidth && height < this.minHeight)
31182 (this.rotate == 90 || this.rotate == 270) &&
31184 width > this.imageEl.OriginWidth ||
31185 height > this.imageEl.OriginHeight ||
31186 (width < this.minHeight && height < this.minWidth)
31193 !this.isDocument &&
31194 (this.rotate == 0 || this.rotate == 180) &&
31196 width < this.minWidth ||
31197 width > this.imageEl.OriginWidth ||
31198 height < this.minHeight ||
31199 height > this.imageEl.OriginHeight
31206 !this.isDocument &&
31207 (this.rotate == 90 || this.rotate == 270) &&
31209 width < this.minHeight ||
31210 width > this.imageEl.OriginWidth ||
31211 height < this.minWidth ||
31212 height > this.imageEl.OriginHeight
31222 onRotateLeft : function(e)
31224 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31226 var minScale = this.thumbEl.getWidth() / this.minWidth;
31228 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31229 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31231 this.startScale = this.scale;
31233 while (this.getScaleLevel() < minScale){
31235 this.scale = this.scale + 1;
31237 if(!this.zoomable()){
31242 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31243 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31248 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31255 this.scale = this.startScale;
31257 this.onRotateFail();
31262 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31264 if(this.isDocument){
31265 this.setThumbBoxSize();
31266 this.setThumbBoxPosition();
31267 this.setCanvasPosition();
31272 this.fireEvent('rotate', this, 'left');
31276 onRotateRight : function(e)
31278 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31280 var minScale = this.thumbEl.getWidth() / this.minWidth;
31282 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31283 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31285 this.startScale = this.scale;
31287 while (this.getScaleLevel() < minScale){
31289 this.scale = this.scale + 1;
31291 if(!this.zoomable()){
31296 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31297 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31302 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31309 this.scale = this.startScale;
31311 this.onRotateFail();
31316 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31318 if(this.isDocument){
31319 this.setThumbBoxSize();
31320 this.setThumbBoxPosition();
31321 this.setCanvasPosition();
31326 this.fireEvent('rotate', this, 'right');
31329 onRotateFail : function()
31331 this.errorEl.show(true);
31335 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31340 this.previewEl.dom.innerHTML = '';
31342 var canvasEl = document.createElement("canvas");
31344 var contextEl = canvasEl.getContext("2d");
31346 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31347 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31348 var center = this.imageEl.OriginWidth / 2;
31350 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31351 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31352 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31353 center = this.imageEl.OriginHeight / 2;
31356 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31358 contextEl.translate(center, center);
31359 contextEl.rotate(this.rotate * Math.PI / 180);
31361 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31363 this.canvasEl = document.createElement("canvas");
31365 this.contextEl = this.canvasEl.getContext("2d");
31367 switch (this.rotate) {
31370 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31371 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31373 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31378 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31379 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31381 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31382 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31386 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31391 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31392 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31394 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31395 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31399 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);
31404 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31405 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31407 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31408 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31412 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31419 this.previewEl.appendChild(this.canvasEl);
31421 this.setCanvasPosition();
31426 if(!this.canvasLoaded){
31430 var imageCanvas = document.createElement("canvas");
31432 var imageContext = imageCanvas.getContext("2d");
31434 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31435 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31437 var center = imageCanvas.width / 2;
31439 imageContext.translate(center, center);
31441 imageContext.rotate(this.rotate * Math.PI / 180);
31443 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31445 var canvas = document.createElement("canvas");
31447 var context = canvas.getContext("2d");
31449 canvas.width = this.minWidth;
31450 canvas.height = this.minHeight;
31452 switch (this.rotate) {
31455 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31456 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31458 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31459 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31461 var targetWidth = this.minWidth - 2 * x;
31462 var targetHeight = this.minHeight - 2 * y;
31466 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31467 scale = targetWidth / width;
31470 if(x > 0 && y == 0){
31471 scale = targetHeight / height;
31474 if(x > 0 && y > 0){
31475 scale = targetWidth / width;
31477 if(width < height){
31478 scale = targetHeight / height;
31482 context.scale(scale, scale);
31484 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31485 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31487 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31488 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31490 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31495 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31496 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31498 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31499 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31501 var targetWidth = this.minWidth - 2 * x;
31502 var targetHeight = this.minHeight - 2 * y;
31506 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31507 scale = targetWidth / width;
31510 if(x > 0 && y == 0){
31511 scale = targetHeight / height;
31514 if(x > 0 && y > 0){
31515 scale = targetWidth / width;
31517 if(width < height){
31518 scale = targetHeight / height;
31522 context.scale(scale, scale);
31524 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31525 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31527 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31528 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31530 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31532 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31537 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31538 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31540 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31541 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31543 var targetWidth = this.minWidth - 2 * x;
31544 var targetHeight = this.minHeight - 2 * y;
31548 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31549 scale = targetWidth / width;
31552 if(x > 0 && y == 0){
31553 scale = targetHeight / height;
31556 if(x > 0 && y > 0){
31557 scale = targetWidth / width;
31559 if(width < height){
31560 scale = targetHeight / height;
31564 context.scale(scale, scale);
31566 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31567 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31569 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31570 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31572 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31573 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31575 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31580 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31581 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31583 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31584 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31586 var targetWidth = this.minWidth - 2 * x;
31587 var targetHeight = this.minHeight - 2 * y;
31591 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31592 scale = targetWidth / width;
31595 if(x > 0 && y == 0){
31596 scale = targetHeight / height;
31599 if(x > 0 && y > 0){
31600 scale = targetWidth / width;
31602 if(width < height){
31603 scale = targetHeight / height;
31607 context.scale(scale, scale);
31609 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31610 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31612 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31613 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31615 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31617 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31624 this.cropData = canvas.toDataURL(this.cropType);
31626 if(this.fireEvent('crop', this, this.cropData) !== false){
31627 this.process(this.file, this.cropData);
31634 setThumbBoxSize : function()
31638 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31639 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31640 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31642 this.minWidth = width;
31643 this.minHeight = height;
31645 if(this.rotate == 90 || this.rotate == 270){
31646 this.minWidth = height;
31647 this.minHeight = width;
31652 width = Math.ceil(this.minWidth * height / this.minHeight);
31654 if(this.minWidth > this.minHeight){
31656 height = Math.ceil(this.minHeight * width / this.minWidth);
31659 this.thumbEl.setStyle({
31660 width : width + 'px',
31661 height : height + 'px'
31668 setThumbBoxPosition : function()
31670 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31671 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31673 this.thumbEl.setLeft(x);
31674 this.thumbEl.setTop(y);
31678 baseRotateLevel : function()
31680 this.baseRotate = 1;
31683 typeof(this.exif) != 'undefined' &&
31684 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31685 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31687 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31690 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31694 baseScaleLevel : function()
31698 if(this.isDocument){
31700 if(this.baseRotate == 6 || this.baseRotate == 8){
31702 height = this.thumbEl.getHeight();
31703 this.baseScale = height / this.imageEl.OriginWidth;
31705 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31706 width = this.thumbEl.getWidth();
31707 this.baseScale = width / this.imageEl.OriginHeight;
31713 height = this.thumbEl.getHeight();
31714 this.baseScale = height / this.imageEl.OriginHeight;
31716 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31717 width = this.thumbEl.getWidth();
31718 this.baseScale = width / this.imageEl.OriginWidth;
31724 if(this.baseRotate == 6 || this.baseRotate == 8){
31726 width = this.thumbEl.getHeight();
31727 this.baseScale = width / this.imageEl.OriginHeight;
31729 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31730 height = this.thumbEl.getWidth();
31731 this.baseScale = height / this.imageEl.OriginHeight;
31734 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31735 height = this.thumbEl.getWidth();
31736 this.baseScale = height / this.imageEl.OriginHeight;
31738 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31739 width = this.thumbEl.getHeight();
31740 this.baseScale = width / this.imageEl.OriginWidth;
31747 width = this.thumbEl.getWidth();
31748 this.baseScale = width / this.imageEl.OriginWidth;
31750 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31751 height = this.thumbEl.getHeight();
31752 this.baseScale = height / this.imageEl.OriginHeight;
31755 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31757 height = this.thumbEl.getHeight();
31758 this.baseScale = height / this.imageEl.OriginHeight;
31760 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31761 width = this.thumbEl.getWidth();
31762 this.baseScale = width / this.imageEl.OriginWidth;
31770 getScaleLevel : function()
31772 return this.baseScale * Math.pow(1.1, this.scale);
31775 onTouchStart : function(e)
31777 if(!this.canvasLoaded){
31778 this.beforeSelectFile(e);
31782 var touches = e.browserEvent.touches;
31788 if(touches.length == 1){
31789 this.onMouseDown(e);
31793 if(touches.length != 2){
31799 for(var i = 0, finger; finger = touches[i]; i++){
31800 coords.push(finger.pageX, finger.pageY);
31803 var x = Math.pow(coords[0] - coords[2], 2);
31804 var y = Math.pow(coords[1] - coords[3], 2);
31806 this.startDistance = Math.sqrt(x + y);
31808 this.startScale = this.scale;
31810 this.pinching = true;
31811 this.dragable = false;
31815 onTouchMove : function(e)
31817 if(!this.pinching && !this.dragable){
31821 var touches = e.browserEvent.touches;
31828 this.onMouseMove(e);
31834 for(var i = 0, finger; finger = touches[i]; i++){
31835 coords.push(finger.pageX, finger.pageY);
31838 var x = Math.pow(coords[0] - coords[2], 2);
31839 var y = Math.pow(coords[1] - coords[3], 2);
31841 this.endDistance = Math.sqrt(x + y);
31843 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31845 if(!this.zoomable()){
31846 this.scale = this.startScale;
31854 onTouchEnd : function(e)
31856 this.pinching = false;
31857 this.dragable = false;
31861 process : function(file, crop)
31864 this.maskEl.mask(this.loadingText);
31867 this.xhr = new XMLHttpRequest();
31869 file.xhr = this.xhr;
31871 this.xhr.open(this.method, this.url, true);
31874 "Accept": "application/json",
31875 "Cache-Control": "no-cache",
31876 "X-Requested-With": "XMLHttpRequest"
31879 for (var headerName in headers) {
31880 var headerValue = headers[headerName];
31882 this.xhr.setRequestHeader(headerName, headerValue);
31888 this.xhr.onload = function()
31890 _this.xhrOnLoad(_this.xhr);
31893 this.xhr.onerror = function()
31895 _this.xhrOnError(_this.xhr);
31898 var formData = new FormData();
31900 formData.append('returnHTML', 'NO');
31903 formData.append('crop', crop);
31906 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31907 formData.append(this.paramName, file, file.name);
31910 if(typeof(file.filename) != 'undefined'){
31911 formData.append('filename', file.filename);
31914 if(typeof(file.mimetype) != 'undefined'){
31915 formData.append('mimetype', file.mimetype);
31918 if(this.fireEvent('arrange', this, formData) != false){
31919 this.xhr.send(formData);
31923 xhrOnLoad : function(xhr)
31926 this.maskEl.unmask();
31929 if (xhr.readyState !== 4) {
31930 this.fireEvent('exception', this, xhr);
31934 var response = Roo.decode(xhr.responseText);
31936 if(!response.success){
31937 this.fireEvent('exception', this, xhr);
31941 var response = Roo.decode(xhr.responseText);
31943 this.fireEvent('upload', this, response);
31947 xhrOnError : function()
31950 this.maskEl.unmask();
31953 Roo.log('xhr on error');
31955 var response = Roo.decode(xhr.responseText);
31961 prepare : function(file)
31964 this.maskEl.mask(this.loadingText);
31970 if(typeof(file) === 'string'){
31971 this.loadCanvas(file);
31975 if(!file || !this.urlAPI){
31980 this.cropType = file.type;
31984 if(this.fireEvent('prepare', this, this.file) != false){
31986 var reader = new FileReader();
31988 reader.onload = function (e) {
31989 if (e.target.error) {
31990 Roo.log(e.target.error);
31994 var buffer = e.target.result,
31995 dataView = new DataView(buffer),
31997 maxOffset = dataView.byteLength - 4,
32001 if (dataView.getUint16(0) === 0xffd8) {
32002 while (offset < maxOffset) {
32003 markerBytes = dataView.getUint16(offset);
32005 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32006 markerLength = dataView.getUint16(offset + 2) + 2;
32007 if (offset + markerLength > dataView.byteLength) {
32008 Roo.log('Invalid meta data: Invalid segment size.');
32012 if(markerBytes == 0xffe1){
32013 _this.parseExifData(
32020 offset += markerLength;
32030 var url = _this.urlAPI.createObjectURL(_this.file);
32032 _this.loadCanvas(url);
32037 reader.readAsArrayBuffer(this.file);
32043 parseExifData : function(dataView, offset, length)
32045 var tiffOffset = offset + 10,
32049 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32050 // No Exif data, might be XMP data instead
32054 // Check for the ASCII code for "Exif" (0x45786966):
32055 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32056 // No Exif data, might be XMP data instead
32059 if (tiffOffset + 8 > dataView.byteLength) {
32060 Roo.log('Invalid Exif data: Invalid segment size.');
32063 // Check for the two null bytes:
32064 if (dataView.getUint16(offset + 8) !== 0x0000) {
32065 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32068 // Check the byte alignment:
32069 switch (dataView.getUint16(tiffOffset)) {
32071 littleEndian = true;
32074 littleEndian = false;
32077 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32080 // Check for the TIFF tag marker (0x002A):
32081 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32082 Roo.log('Invalid Exif data: Missing TIFF marker.');
32085 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32086 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32088 this.parseExifTags(
32091 tiffOffset + dirOffset,
32096 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32101 if (dirOffset + 6 > dataView.byteLength) {
32102 Roo.log('Invalid Exif data: Invalid directory offset.');
32105 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32106 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32107 if (dirEndOffset + 4 > dataView.byteLength) {
32108 Roo.log('Invalid Exif data: Invalid directory size.');
32111 for (i = 0; i < tagsNumber; i += 1) {
32115 dirOffset + 2 + 12 * i, // tag offset
32119 // Return the offset to the next directory:
32120 return dataView.getUint32(dirEndOffset, littleEndian);
32123 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32125 var tag = dataView.getUint16(offset, littleEndian);
32127 this.exif[tag] = this.getExifValue(
32131 dataView.getUint16(offset + 2, littleEndian), // tag type
32132 dataView.getUint32(offset + 4, littleEndian), // tag length
32137 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32139 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32148 Roo.log('Invalid Exif data: Invalid tag type.');
32152 tagSize = tagType.size * length;
32153 // Determine if the value is contained in the dataOffset bytes,
32154 // or if the value at the dataOffset is a pointer to the actual data:
32155 dataOffset = tagSize > 4 ?
32156 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32157 if (dataOffset + tagSize > dataView.byteLength) {
32158 Roo.log('Invalid Exif data: Invalid data offset.');
32161 if (length === 1) {
32162 return tagType.getValue(dataView, dataOffset, littleEndian);
32165 for (i = 0; i < length; i += 1) {
32166 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32169 if (tagType.ascii) {
32171 // Concatenate the chars:
32172 for (i = 0; i < values.length; i += 1) {
32174 // Ignore the terminating NULL byte(s):
32175 if (c === '\u0000') {
32187 Roo.apply(Roo.bootstrap.UploadCropbox, {
32189 'Orientation': 0x0112
32193 1: 0, //'top-left',
32195 3: 180, //'bottom-right',
32196 // 4: 'bottom-left',
32198 6: 90, //'right-top',
32199 // 7: 'right-bottom',
32200 8: 270 //'left-bottom'
32204 // byte, 8-bit unsigned int:
32206 getValue: function (dataView, dataOffset) {
32207 return dataView.getUint8(dataOffset);
32211 // ascii, 8-bit byte:
32213 getValue: function (dataView, dataOffset) {
32214 return String.fromCharCode(dataView.getUint8(dataOffset));
32219 // short, 16 bit int:
32221 getValue: function (dataView, dataOffset, littleEndian) {
32222 return dataView.getUint16(dataOffset, littleEndian);
32226 // long, 32 bit int:
32228 getValue: function (dataView, dataOffset, littleEndian) {
32229 return dataView.getUint32(dataOffset, littleEndian);
32233 // rational = two long values, first is numerator, second is denominator:
32235 getValue: function (dataView, dataOffset, littleEndian) {
32236 return dataView.getUint32(dataOffset, littleEndian) /
32237 dataView.getUint32(dataOffset + 4, littleEndian);
32241 // slong, 32 bit signed int:
32243 getValue: function (dataView, dataOffset, littleEndian) {
32244 return dataView.getInt32(dataOffset, littleEndian);
32248 // srational, two slongs, first is numerator, second is denominator:
32250 getValue: function (dataView, dataOffset, littleEndian) {
32251 return dataView.getInt32(dataOffset, littleEndian) /
32252 dataView.getInt32(dataOffset + 4, littleEndian);
32262 cls : 'btn-group roo-upload-cropbox-rotate-left',
32263 action : 'rotate-left',
32267 cls : 'btn btn-default',
32268 html : '<i class="fa fa-undo"></i>'
32274 cls : 'btn-group roo-upload-cropbox-picture',
32275 action : 'picture',
32279 cls : 'btn btn-default',
32280 html : '<i class="fa fa-picture-o"></i>'
32286 cls : 'btn-group roo-upload-cropbox-rotate-right',
32287 action : 'rotate-right',
32291 cls : 'btn btn-default',
32292 html : '<i class="fa fa-repeat"></i>'
32300 cls : 'btn-group roo-upload-cropbox-rotate-left',
32301 action : 'rotate-left',
32305 cls : 'btn btn-default',
32306 html : '<i class="fa fa-undo"></i>'
32312 cls : 'btn-group roo-upload-cropbox-download',
32313 action : 'download',
32317 cls : 'btn btn-default',
32318 html : '<i class="fa fa-download"></i>'
32324 cls : 'btn-group roo-upload-cropbox-crop',
32329 cls : 'btn btn-default',
32330 html : '<i class="fa fa-crop"></i>'
32336 cls : 'btn-group roo-upload-cropbox-trash',
32341 cls : 'btn btn-default',
32342 html : '<i class="fa fa-trash"></i>'
32348 cls : 'btn-group roo-upload-cropbox-rotate-right',
32349 action : 'rotate-right',
32353 cls : 'btn btn-default',
32354 html : '<i class="fa fa-repeat"></i>'
32362 cls : 'btn-group roo-upload-cropbox-rotate-left',
32363 action : 'rotate-left',
32367 cls : 'btn btn-default',
32368 html : '<i class="fa fa-undo"></i>'
32374 cls : 'btn-group roo-upload-cropbox-rotate-right',
32375 action : 'rotate-right',
32379 cls : 'btn btn-default',
32380 html : '<i class="fa fa-repeat"></i>'
32393 * @class Roo.bootstrap.DocumentManager
32394 * @extends Roo.bootstrap.Component
32395 * Bootstrap DocumentManager class
32396 * @cfg {String} paramName default 'imageUpload'
32397 * @cfg {String} toolTipName default 'filename'
32398 * @cfg {String} method default POST
32399 * @cfg {String} url action url
32400 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32401 * @cfg {Boolean} multiple multiple upload default true
32402 * @cfg {Number} thumbSize default 300
32403 * @cfg {String} fieldLabel
32404 * @cfg {Number} labelWidth default 4
32405 * @cfg {String} labelAlign (left|top) default left
32406 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32407 * @cfg {Number} labellg set the width of label (1-12)
32408 * @cfg {Number} labelmd set the width of label (1-12)
32409 * @cfg {Number} labelsm set the width of label (1-12)
32410 * @cfg {Number} labelxs set the width of label (1-12)
32413 * Create a new DocumentManager
32414 * @param {Object} config The config object
32417 Roo.bootstrap.DocumentManager = function(config){
32418 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32421 this.delegates = [];
32426 * Fire when initial the DocumentManager
32427 * @param {Roo.bootstrap.DocumentManager} this
32432 * inspect selected file
32433 * @param {Roo.bootstrap.DocumentManager} this
32434 * @param {File} file
32439 * Fire when xhr load exception
32440 * @param {Roo.bootstrap.DocumentManager} this
32441 * @param {XMLHttpRequest} xhr
32443 "exception" : true,
32445 * @event afterupload
32446 * Fire when xhr load exception
32447 * @param {Roo.bootstrap.DocumentManager} this
32448 * @param {XMLHttpRequest} xhr
32450 "afterupload" : true,
32453 * prepare the form data
32454 * @param {Roo.bootstrap.DocumentManager} this
32455 * @param {Object} formData
32460 * Fire when remove the file
32461 * @param {Roo.bootstrap.DocumentManager} this
32462 * @param {Object} file
32467 * Fire after refresh the file
32468 * @param {Roo.bootstrap.DocumentManager} this
32473 * Fire after click the image
32474 * @param {Roo.bootstrap.DocumentManager} this
32475 * @param {Object} file
32480 * Fire when upload a image and editable set to true
32481 * @param {Roo.bootstrap.DocumentManager} this
32482 * @param {Object} file
32486 * @event beforeselectfile
32487 * Fire before select file
32488 * @param {Roo.bootstrap.DocumentManager} this
32490 "beforeselectfile" : true,
32493 * Fire before process file
32494 * @param {Roo.bootstrap.DocumentManager} this
32495 * @param {Object} file
32499 * @event previewrendered
32500 * Fire when preview rendered
32501 * @param {Roo.bootstrap.DocumentManager} this
32502 * @param {Object} file
32504 "previewrendered" : true,
32507 "previewResize" : true
32512 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32521 paramName : 'imageUpload',
32522 toolTipName : 'filename',
32525 labelAlign : 'left',
32535 getAutoCreate : function()
32537 var managerWidget = {
32539 cls : 'roo-document-manager',
32543 cls : 'roo-document-manager-selector',
32548 cls : 'roo-document-manager-uploader',
32552 cls : 'roo-document-manager-upload-btn',
32553 html : '<i class="fa fa-plus"></i>'
32564 cls : 'column col-md-12',
32569 if(this.fieldLabel.length){
32574 cls : 'column col-md-12',
32575 html : this.fieldLabel
32579 cls : 'column col-md-12',
32584 if(this.labelAlign == 'left'){
32589 html : this.fieldLabel
32598 if(this.labelWidth > 12){
32599 content[0].style = "width: " + this.labelWidth + 'px';
32602 if(this.labelWidth < 13 && this.labelmd == 0){
32603 this.labelmd = this.labelWidth;
32606 if(this.labellg > 0){
32607 content[0].cls += ' col-lg-' + this.labellg;
32608 content[1].cls += ' col-lg-' + (12 - this.labellg);
32611 if(this.labelmd > 0){
32612 content[0].cls += ' col-md-' + this.labelmd;
32613 content[1].cls += ' col-md-' + (12 - this.labelmd);
32616 if(this.labelsm > 0){
32617 content[0].cls += ' col-sm-' + this.labelsm;
32618 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32621 if(this.labelxs > 0){
32622 content[0].cls += ' col-xs-' + this.labelxs;
32623 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32631 cls : 'row clearfix',
32639 initEvents : function()
32641 this.managerEl = this.el.select('.roo-document-manager', true).first();
32642 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32644 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32645 this.selectorEl.hide();
32648 this.selectorEl.attr('multiple', 'multiple');
32651 this.selectorEl.on('change', this.onFileSelected, this);
32653 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32654 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32656 this.uploader.on('click', this.onUploaderClick, this);
32658 this.renderProgressDialog();
32662 window.addEventListener("resize", function() { _this.refresh(); } );
32664 this.fireEvent('initial', this);
32667 renderProgressDialog : function()
32671 this.progressDialog = new Roo.bootstrap.Modal({
32672 cls : 'roo-document-manager-progress-dialog',
32673 allow_close : false,
32684 btnclick : function() {
32685 _this.uploadCancel();
32691 this.progressDialog.render(Roo.get(document.body));
32693 this.progress = new Roo.bootstrap.Progress({
32694 cls : 'roo-document-manager-progress',
32699 this.progress.render(this.progressDialog.getChildContainer());
32701 this.progressBar = new Roo.bootstrap.ProgressBar({
32702 cls : 'roo-document-manager-progress-bar',
32705 aria_valuemax : 12,
32709 this.progressBar.render(this.progress.getChildContainer());
32712 onUploaderClick : function(e)
32714 e.preventDefault();
32716 if(this.fireEvent('beforeselectfile', this) != false){
32717 this.selectorEl.dom.click();
32722 onFileSelected : function(e)
32724 e.preventDefault();
32726 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32730 Roo.each(this.selectorEl.dom.files, function(file){
32731 if(this.fireEvent('inspect', this, file) != false){
32732 this.files.push(file);
32742 this.selectorEl.dom.value = '';
32744 if(!this.files || !this.files.length){
32748 if(this.boxes > 0 && this.files.length > this.boxes){
32749 this.files = this.files.slice(0, this.boxes);
32752 this.uploader.show();
32754 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32755 this.uploader.hide();
32764 Roo.each(this.files, function(file){
32766 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32767 var f = this.renderPreview(file);
32772 if(file.type.indexOf('image') != -1){
32773 this.delegates.push(
32775 _this.process(file);
32776 }).createDelegate(this)
32784 _this.process(file);
32785 }).createDelegate(this)
32790 this.files = files;
32792 this.delegates = this.delegates.concat(docs);
32794 if(!this.delegates.length){
32799 this.progressBar.aria_valuemax = this.delegates.length;
32806 arrange : function()
32808 if(!this.delegates.length){
32809 this.progressDialog.hide();
32814 var delegate = this.delegates.shift();
32816 this.progressDialog.show();
32818 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32820 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32825 refresh : function()
32827 this.uploader.show();
32829 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32830 this.uploader.hide();
32833 Roo.isTouch ? this.closable(false) : this.closable(true);
32835 this.fireEvent('refresh', this);
32838 onRemove : function(e, el, o)
32840 e.preventDefault();
32842 this.fireEvent('remove', this, o);
32846 remove : function(o)
32850 Roo.each(this.files, function(file){
32851 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32860 this.files = files;
32867 Roo.each(this.files, function(file){
32872 file.target.remove();
32881 onClick : function(e, el, o)
32883 e.preventDefault();
32885 this.fireEvent('click', this, o);
32889 closable : function(closable)
32891 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32893 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32905 xhrOnLoad : function(xhr)
32907 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32911 if (xhr.readyState !== 4) {
32913 this.fireEvent('exception', this, xhr);
32917 var response = Roo.decode(xhr.responseText);
32919 if(!response.success){
32921 this.fireEvent('exception', this, xhr);
32925 var file = this.renderPreview(response.data);
32927 this.files.push(file);
32931 this.fireEvent('afterupload', this, xhr);
32935 xhrOnError : function(xhr)
32937 Roo.log('xhr on error');
32939 var response = Roo.decode(xhr.responseText);
32946 process : function(file)
32948 if(this.fireEvent('process', this, file) !== false){
32949 if(this.editable && file.type.indexOf('image') != -1){
32950 this.fireEvent('edit', this, file);
32954 this.uploadStart(file, false);
32961 uploadStart : function(file, crop)
32963 this.xhr = new XMLHttpRequest();
32965 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32970 file.xhr = this.xhr;
32972 this.managerEl.createChild({
32974 cls : 'roo-document-manager-loading',
32978 tooltip : file.name,
32979 cls : 'roo-document-manager-thumb',
32980 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32986 this.xhr.open(this.method, this.url, true);
32989 "Accept": "application/json",
32990 "Cache-Control": "no-cache",
32991 "X-Requested-With": "XMLHttpRequest"
32994 for (var headerName in headers) {
32995 var headerValue = headers[headerName];
32997 this.xhr.setRequestHeader(headerName, headerValue);
33003 this.xhr.onload = function()
33005 _this.xhrOnLoad(_this.xhr);
33008 this.xhr.onerror = function()
33010 _this.xhrOnError(_this.xhr);
33013 var formData = new FormData();
33015 formData.append('returnHTML', 'NO');
33018 formData.append('crop', crop);
33021 formData.append(this.paramName, file, file.name);
33028 if(this.fireEvent('prepare', this, formData, options) != false){
33030 if(options.manually){
33034 this.xhr.send(formData);
33038 this.uploadCancel();
33041 uploadCancel : function()
33047 this.delegates = [];
33049 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33056 renderPreview : function(file)
33058 if(typeof(file.target) != 'undefined' && file.target){
33062 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33064 var previewEl = this.managerEl.createChild({
33066 cls : 'roo-document-manager-preview',
33070 tooltip : file[this.toolTipName],
33071 cls : 'roo-document-manager-thumb',
33072 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33077 html : '<i class="fa fa-times-circle"></i>'
33082 var close = previewEl.select('button.close', true).first();
33084 close.on('click', this.onRemove, this, file);
33086 file.target = previewEl;
33088 var image = previewEl.select('img', true).first();
33092 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33094 image.on('click', this.onClick, this, file);
33096 this.fireEvent('previewrendered', this, file);
33102 onPreviewLoad : function(file, image)
33104 if(typeof(file.target) == 'undefined' || !file.target){
33108 var width = image.dom.naturalWidth || image.dom.width;
33109 var height = image.dom.naturalHeight || image.dom.height;
33111 if(!this.previewResize) {
33115 if(width > height){
33116 file.target.addClass('wide');
33120 file.target.addClass('tall');
33125 uploadFromSource : function(file, crop)
33127 this.xhr = new XMLHttpRequest();
33129 this.managerEl.createChild({
33131 cls : 'roo-document-manager-loading',
33135 tooltip : file.name,
33136 cls : 'roo-document-manager-thumb',
33137 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33143 this.xhr.open(this.method, this.url, true);
33146 "Accept": "application/json",
33147 "Cache-Control": "no-cache",
33148 "X-Requested-With": "XMLHttpRequest"
33151 for (var headerName in headers) {
33152 var headerValue = headers[headerName];
33154 this.xhr.setRequestHeader(headerName, headerValue);
33160 this.xhr.onload = function()
33162 _this.xhrOnLoad(_this.xhr);
33165 this.xhr.onerror = function()
33167 _this.xhrOnError(_this.xhr);
33170 var formData = new FormData();
33172 formData.append('returnHTML', 'NO');
33174 formData.append('crop', crop);
33176 if(typeof(file.filename) != 'undefined'){
33177 formData.append('filename', file.filename);
33180 if(typeof(file.mimetype) != 'undefined'){
33181 formData.append('mimetype', file.mimetype);
33186 if(this.fireEvent('prepare', this, formData) != false){
33187 this.xhr.send(formData);
33197 * @class Roo.bootstrap.DocumentViewer
33198 * @extends Roo.bootstrap.Component
33199 * Bootstrap DocumentViewer class
33200 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33201 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33204 * Create a new DocumentViewer
33205 * @param {Object} config The config object
33208 Roo.bootstrap.DocumentViewer = function(config){
33209 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33214 * Fire after initEvent
33215 * @param {Roo.bootstrap.DocumentViewer} this
33221 * @param {Roo.bootstrap.DocumentViewer} this
33226 * Fire after download button
33227 * @param {Roo.bootstrap.DocumentViewer} this
33232 * Fire after trash button
33233 * @param {Roo.bootstrap.DocumentViewer} this
33240 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33242 showDownload : true,
33246 getAutoCreate : function()
33250 cls : 'roo-document-viewer',
33254 cls : 'roo-document-viewer-body',
33258 cls : 'roo-document-viewer-thumb',
33262 cls : 'roo-document-viewer-image'
33270 cls : 'roo-document-viewer-footer',
33273 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33277 cls : 'btn-group roo-document-viewer-download',
33281 cls : 'btn btn-default',
33282 html : '<i class="fa fa-download"></i>'
33288 cls : 'btn-group roo-document-viewer-trash',
33292 cls : 'btn btn-default',
33293 html : '<i class="fa fa-trash"></i>'
33306 initEvents : function()
33308 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33309 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33311 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33312 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33314 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33315 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33317 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33318 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33320 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33321 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33323 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33324 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33326 this.bodyEl.on('click', this.onClick, this);
33327 this.downloadBtn.on('click', this.onDownload, this);
33328 this.trashBtn.on('click', this.onTrash, this);
33330 this.downloadBtn.hide();
33331 this.trashBtn.hide();
33333 if(this.showDownload){
33334 this.downloadBtn.show();
33337 if(this.showTrash){
33338 this.trashBtn.show();
33341 if(!this.showDownload && !this.showTrash) {
33342 this.footerEl.hide();
33347 initial : function()
33349 this.fireEvent('initial', this);
33353 onClick : function(e)
33355 e.preventDefault();
33357 this.fireEvent('click', this);
33360 onDownload : function(e)
33362 e.preventDefault();
33364 this.fireEvent('download', this);
33367 onTrash : function(e)
33369 e.preventDefault();
33371 this.fireEvent('trash', this);
33383 * @class Roo.bootstrap.form.FieldLabel
33384 * @extends Roo.bootstrap.Component
33385 * Bootstrap FieldLabel class
33386 * @cfg {String} html contents of the element
33387 * @cfg {String} tag tag of the element default label
33388 * @cfg {String} cls class of the element
33389 * @cfg {String} target label target
33390 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33391 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33392 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33393 * @cfg {String} iconTooltip default "This field is required"
33394 * @cfg {String} indicatorpos (left|right) default left
33397 * Create a new FieldLabel
33398 * @param {Object} config The config object
33401 Roo.bootstrap.form.FieldLabel = function(config){
33402 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33407 * Fires after the field has been marked as invalid.
33408 * @param {Roo.form.FieldLabel} this
33409 * @param {String} msg The validation message
33414 * Fires after the field has been validated with no errors.
33415 * @param {Roo.form.FieldLabel} this
33421 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33428 invalidClass : 'has-warning',
33429 validClass : 'has-success',
33430 iconTooltip : 'This field is required',
33431 indicatorpos : 'left',
33433 getAutoCreate : function(){
33436 if (!this.allowBlank) {
33442 cls : 'roo-bootstrap-field-label ' + this.cls,
33447 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33448 tooltip : this.iconTooltip
33457 if(this.indicatorpos == 'right'){
33460 cls : 'roo-bootstrap-field-label ' + this.cls,
33469 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33470 tooltip : this.iconTooltip
33479 initEvents: function()
33481 Roo.bootstrap.Element.superclass.initEvents.call(this);
33483 this.indicator = this.indicatorEl();
33485 if(this.indicator){
33486 this.indicator.removeClass('visible');
33487 this.indicator.addClass('invisible');
33490 Roo.bootstrap.form.FieldLabel.register(this);
33493 indicatorEl : function()
33495 var indicator = this.el.select('i.roo-required-indicator',true).first();
33506 * Mark this field as valid
33508 markValid : function()
33510 if(this.indicator){
33511 this.indicator.removeClass('visible');
33512 this.indicator.addClass('invisible');
33514 if (Roo.bootstrap.version == 3) {
33515 this.el.removeClass(this.invalidClass);
33516 this.el.addClass(this.validClass);
33518 this.el.removeClass('is-invalid');
33519 this.el.addClass('is-valid');
33523 this.fireEvent('valid', this);
33527 * Mark this field as invalid
33528 * @param {String} msg The validation message
33530 markInvalid : function(msg)
33532 if(this.indicator){
33533 this.indicator.removeClass('invisible');
33534 this.indicator.addClass('visible');
33536 if (Roo.bootstrap.version == 3) {
33537 this.el.removeClass(this.validClass);
33538 this.el.addClass(this.invalidClass);
33540 this.el.removeClass('is-valid');
33541 this.el.addClass('is-invalid');
33545 this.fireEvent('invalid', this, msg);
33551 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33556 * register a FieldLabel Group
33557 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33559 register : function(label)
33561 if(this.groups.hasOwnProperty(label.target)){
33565 this.groups[label.target] = label;
33569 * fetch a FieldLabel Group based on the target
33570 * @param {string} target
33571 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33573 get: function(target) {
33574 if (typeof(this.groups[target]) == 'undefined') {
33578 return this.groups[target] ;
33587 * page DateSplitField.
33593 * @class Roo.bootstrap.form.DateSplitField
33594 * @extends Roo.bootstrap.Component
33595 * Bootstrap DateSplitField class
33596 * @cfg {string} fieldLabel - the label associated
33597 * @cfg {Number} labelWidth set the width of label (0-12)
33598 * @cfg {String} labelAlign (top|left)
33599 * @cfg {Boolean} dayAllowBlank (true|false) default false
33600 * @cfg {Boolean} monthAllowBlank (true|false) default false
33601 * @cfg {Boolean} yearAllowBlank (true|false) default false
33602 * @cfg {string} dayPlaceholder
33603 * @cfg {string} monthPlaceholder
33604 * @cfg {string} yearPlaceholder
33605 * @cfg {string} dayFormat default 'd'
33606 * @cfg {string} monthFormat default 'm'
33607 * @cfg {string} yearFormat default 'Y'
33608 * @cfg {Number} labellg set the width of label (1-12)
33609 * @cfg {Number} labelmd set the width of label (1-12)
33610 * @cfg {Number} labelsm set the width of label (1-12)
33611 * @cfg {Number} labelxs set the width of label (1-12)
33615 * Create a new DateSplitField
33616 * @param {Object} config The config object
33619 Roo.bootstrap.form.DateSplitField = function(config){
33620 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33626 * getting the data of years
33627 * @param {Roo.bootstrap.form.DateSplitField} this
33628 * @param {Object} years
33633 * getting the data of days
33634 * @param {Roo.bootstrap.form.DateSplitField} this
33635 * @param {Object} days
33640 * Fires after the field has been marked as invalid.
33641 * @param {Roo.form.Field} this
33642 * @param {String} msg The validation message
33647 * Fires after the field has been validated with no errors.
33648 * @param {Roo.form.Field} this
33654 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33657 labelAlign : 'top',
33659 dayAllowBlank : false,
33660 monthAllowBlank : false,
33661 yearAllowBlank : false,
33662 dayPlaceholder : '',
33663 monthPlaceholder : '',
33664 yearPlaceholder : '',
33668 isFormField : true,
33674 getAutoCreate : function()
33678 cls : 'row roo-date-split-field-group',
33683 cls : 'form-hidden-field roo-date-split-field-group-value',
33689 var labelCls = 'col-md-12';
33690 var contentCls = 'col-md-4';
33692 if(this.fieldLabel){
33696 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33700 html : this.fieldLabel
33705 if(this.labelAlign == 'left'){
33707 if(this.labelWidth > 12){
33708 label.style = "width: " + this.labelWidth + 'px';
33711 if(this.labelWidth < 13 && this.labelmd == 0){
33712 this.labelmd = this.labelWidth;
33715 if(this.labellg > 0){
33716 labelCls = ' col-lg-' + this.labellg;
33717 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33720 if(this.labelmd > 0){
33721 labelCls = ' col-md-' + this.labelmd;
33722 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33725 if(this.labelsm > 0){
33726 labelCls = ' col-sm-' + this.labelsm;
33727 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33730 if(this.labelxs > 0){
33731 labelCls = ' col-xs-' + this.labelxs;
33732 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33736 label.cls += ' ' + labelCls;
33738 cfg.cn.push(label);
33741 Roo.each(['day', 'month', 'year'], function(t){
33744 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33751 inputEl: function ()
33753 return this.el.select('.roo-date-split-field-group-value', true).first();
33756 onRender : function(ct, position)
33760 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33762 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33764 this.dayField = new Roo.bootstrap.form.ComboBox({
33765 allowBlank : this.dayAllowBlank,
33766 alwaysQuery : true,
33767 displayField : 'value',
33770 forceSelection : true,
33772 placeholder : this.dayPlaceholder,
33773 selectOnFocus : true,
33774 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33775 triggerAction : 'all',
33777 valueField : 'value',
33778 store : new Roo.data.SimpleStore({
33779 data : (function() {
33781 _this.fireEvent('days', _this, days);
33784 fields : [ 'value' ]
33787 select : function (_self, record, index)
33789 _this.setValue(_this.getValue());
33794 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33796 this.monthField = new Roo.bootstrap.form.MonthField({
33797 after : '<i class=\"fa fa-calendar\"></i>',
33798 allowBlank : this.monthAllowBlank,
33799 placeholder : this.monthPlaceholder,
33802 render : function (_self)
33804 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33805 e.preventDefault();
33809 select : function (_self, oldvalue, newvalue)
33811 _this.setValue(_this.getValue());
33816 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33818 this.yearField = new Roo.bootstrap.form.ComboBox({
33819 allowBlank : this.yearAllowBlank,
33820 alwaysQuery : true,
33821 displayField : 'value',
33824 forceSelection : true,
33826 placeholder : this.yearPlaceholder,
33827 selectOnFocus : true,
33828 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33829 triggerAction : 'all',
33831 valueField : 'value',
33832 store : new Roo.data.SimpleStore({
33833 data : (function() {
33835 _this.fireEvent('years', _this, years);
33838 fields : [ 'value' ]
33841 select : function (_self, record, index)
33843 _this.setValue(_this.getValue());
33848 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33851 setValue : function(v, format)
33853 this.inputEl.dom.value = v;
33855 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33857 var d = Date.parseDate(v, f);
33864 this.setDay(d.format(this.dayFormat));
33865 this.setMonth(d.format(this.monthFormat));
33866 this.setYear(d.format(this.yearFormat));
33873 setDay : function(v)
33875 this.dayField.setValue(v);
33876 this.inputEl.dom.value = this.getValue();
33881 setMonth : function(v)
33883 this.monthField.setValue(v, true);
33884 this.inputEl.dom.value = this.getValue();
33889 setYear : function(v)
33891 this.yearField.setValue(v);
33892 this.inputEl.dom.value = this.getValue();
33897 getDay : function()
33899 return this.dayField.getValue();
33902 getMonth : function()
33904 return this.monthField.getValue();
33907 getYear : function()
33909 return this.yearField.getValue();
33912 getValue : function()
33914 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33916 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33926 this.inputEl.dom.value = '';
33931 validate : function()
33933 var d = this.dayField.validate();
33934 var m = this.monthField.validate();
33935 var y = this.yearField.validate();
33940 (!this.dayAllowBlank && !d) ||
33941 (!this.monthAllowBlank && !m) ||
33942 (!this.yearAllowBlank && !y)
33947 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33956 this.markInvalid();
33961 markValid : function()
33964 var label = this.el.select('label', true).first();
33965 var icon = this.el.select('i.fa-star', true).first();
33971 this.fireEvent('valid', this);
33975 * Mark this field as invalid
33976 * @param {String} msg The validation message
33978 markInvalid : function(msg)
33981 var label = this.el.select('label', true).first();
33982 var icon = this.el.select('i.fa-star', true).first();
33984 if(label && !icon){
33985 this.el.select('.roo-date-split-field-label', true).createChild({
33987 cls : 'text-danger fa fa-lg fa-star',
33988 tooltip : 'This field is required',
33989 style : 'margin-right:5px;'
33993 this.fireEvent('invalid', this, msg);
33996 clearInvalid : function()
33998 var label = this.el.select('label', true).first();
33999 var icon = this.el.select('i.fa-star', true).first();
34005 this.fireEvent('valid', this);
34008 getName: function()
34018 * @class Roo.bootstrap.LayoutMasonry
34019 * @extends Roo.bootstrap.Component
34020 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34021 * Bootstrap Layout Masonry class
34024 * http://masonry.desandro.com
34026 * The idea is to render all the bricks based on vertical width...
34028 * The original code extends 'outlayer' - we might need to use that....
34031 * Create a new Element
34032 * @param {Object} config The config object
34035 Roo.bootstrap.LayoutMasonry = function(config){
34037 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34041 Roo.bootstrap.LayoutMasonry.register(this);
34047 * Fire after layout the items
34048 * @param {Roo.bootstrap.LayoutMasonry} this
34049 * @param {Roo.EventObject} e
34056 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34059 * @cfg {Boolean} isLayoutInstant = no animation?
34061 isLayoutInstant : false, // needed?
34064 * @cfg {Number} boxWidth width of the columns
34069 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34074 * @cfg {Number} padWidth padding below box..
34079 * @cfg {Number} gutter gutter width..
34084 * @cfg {Number} maxCols maximum number of columns
34090 * @cfg {Boolean} isAutoInitial defalut true
34092 isAutoInitial : true,
34097 * @cfg {Boolean} isHorizontal defalut false
34099 isHorizontal : false,
34101 currentSize : null,
34107 bricks: null, //CompositeElement
34111 _isLayoutInited : false,
34113 // isAlternative : false, // only use for vertical layout...
34116 * @cfg {Number} alternativePadWidth padding below box..
34118 alternativePadWidth : 50,
34120 selectedBrick : [],
34122 getAutoCreate : function(){
34124 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34128 cls: 'blog-masonary-wrapper ' + this.cls,
34130 cls : 'mas-boxes masonary'
34137 getChildContainer: function( )
34139 if (this.boxesEl) {
34140 return this.boxesEl;
34143 this.boxesEl = this.el.select('.mas-boxes').first();
34145 return this.boxesEl;
34149 initEvents : function()
34153 if(this.isAutoInitial){
34154 Roo.log('hook children rendered');
34155 this.on('childrenrendered', function() {
34156 Roo.log('children rendered');
34162 initial : function()
34164 this.selectedBrick = [];
34166 this.currentSize = this.el.getBox(true);
34168 Roo.EventManager.onWindowResize(this.resize, this);
34170 if(!this.isAutoInitial){
34178 //this.layout.defer(500,this);
34182 resize : function()
34184 var cs = this.el.getBox(true);
34187 this.currentSize.width == cs.width &&
34188 this.currentSize.x == cs.x &&
34189 this.currentSize.height == cs.height &&
34190 this.currentSize.y == cs.y
34192 Roo.log("no change in with or X or Y");
34196 this.currentSize = cs;
34202 layout : function()
34204 this._resetLayout();
34206 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34208 this.layoutItems( isInstant );
34210 this._isLayoutInited = true;
34212 this.fireEvent('layout', this);
34216 _resetLayout : function()
34218 if(this.isHorizontal){
34219 this.horizontalMeasureColumns();
34223 this.verticalMeasureColumns();
34227 verticalMeasureColumns : function()
34229 this.getContainerWidth();
34231 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34232 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34236 var boxWidth = this.boxWidth + this.padWidth;
34238 if(this.containerWidth < this.boxWidth){
34239 boxWidth = this.containerWidth
34242 var containerWidth = this.containerWidth;
34244 var cols = Math.floor(containerWidth / boxWidth);
34246 this.cols = Math.max( cols, 1 );
34248 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34250 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34252 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34254 this.colWidth = boxWidth + avail - this.padWidth;
34256 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34257 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34260 horizontalMeasureColumns : function()
34262 this.getContainerWidth();
34264 var boxWidth = this.boxWidth;
34266 if(this.containerWidth < boxWidth){
34267 boxWidth = this.containerWidth;
34270 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34272 this.el.setHeight(boxWidth);
34276 getContainerWidth : function()
34278 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34281 layoutItems : function( isInstant )
34283 Roo.log(this.bricks);
34285 var items = Roo.apply([], this.bricks);
34287 if(this.isHorizontal){
34288 this._horizontalLayoutItems( items , isInstant );
34292 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34293 // this._verticalAlternativeLayoutItems( items , isInstant );
34297 this._verticalLayoutItems( items , isInstant );
34301 _verticalLayoutItems : function ( items , isInstant)
34303 if ( !items || !items.length ) {
34308 ['xs', 'xs', 'xs', 'tall'],
34309 ['xs', 'xs', 'tall'],
34310 ['xs', 'xs', 'sm'],
34311 ['xs', 'xs', 'xs'],
34317 ['sm', 'xs', 'xs'],
34321 ['tall', 'xs', 'xs', 'xs'],
34322 ['tall', 'xs', 'xs'],
34334 Roo.each(items, function(item, k){
34336 switch (item.size) {
34337 // these layouts take up a full box,
34348 boxes.push([item]);
34371 var filterPattern = function(box, length)
34379 var pattern = box.slice(0, length);
34383 Roo.each(pattern, function(i){
34384 format.push(i.size);
34387 Roo.each(standard, function(s){
34389 if(String(s) != String(format)){
34398 if(!match && length == 1){
34403 filterPattern(box, length - 1);
34407 queue.push(pattern);
34409 box = box.slice(length, box.length);
34411 filterPattern(box, 4);
34417 Roo.each(boxes, function(box, k){
34423 if(box.length == 1){
34428 filterPattern(box, 4);
34432 this._processVerticalLayoutQueue( queue, isInstant );
34436 // _verticalAlternativeLayoutItems : function( items , isInstant )
34438 // if ( !items || !items.length ) {
34442 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34446 _horizontalLayoutItems : function ( items , isInstant)
34448 if ( !items || !items.length || items.length < 3) {
34454 var eItems = items.slice(0, 3);
34456 items = items.slice(3, items.length);
34459 ['xs', 'xs', 'xs', 'wide'],
34460 ['xs', 'xs', 'wide'],
34461 ['xs', 'xs', 'sm'],
34462 ['xs', 'xs', 'xs'],
34468 ['sm', 'xs', 'xs'],
34472 ['wide', 'xs', 'xs', 'xs'],
34473 ['wide', 'xs', 'xs'],
34486 Roo.each(items, function(item, k){
34488 switch (item.size) {
34499 boxes.push([item]);
34523 var filterPattern = function(box, length)
34531 var pattern = box.slice(0, length);
34535 Roo.each(pattern, function(i){
34536 format.push(i.size);
34539 Roo.each(standard, function(s){
34541 if(String(s) != String(format)){
34550 if(!match && length == 1){
34555 filterPattern(box, length - 1);
34559 queue.push(pattern);
34561 box = box.slice(length, box.length);
34563 filterPattern(box, 4);
34569 Roo.each(boxes, function(box, k){
34575 if(box.length == 1){
34580 filterPattern(box, 4);
34587 var pos = this.el.getBox(true);
34591 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34593 var hit_end = false;
34595 Roo.each(queue, function(box){
34599 Roo.each(box, function(b){
34601 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34611 Roo.each(box, function(b){
34613 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34616 mx = Math.max(mx, b.x);
34620 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34624 Roo.each(box, function(b){
34626 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34640 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34643 /** Sets position of item in DOM
34644 * @param {Element} item
34645 * @param {Number} x - horizontal position
34646 * @param {Number} y - vertical position
34647 * @param {Boolean} isInstant - disables transitions
34649 _processVerticalLayoutQueue : function( queue, isInstant )
34651 var pos = this.el.getBox(true);
34656 for (var i = 0; i < this.cols; i++){
34660 Roo.each(queue, function(box, k){
34662 var col = k % this.cols;
34664 Roo.each(box, function(b,kk){
34666 b.el.position('absolute');
34668 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34669 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34671 if(b.size == 'md-left' || b.size == 'md-right'){
34672 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34673 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34676 b.el.setWidth(width);
34677 b.el.setHeight(height);
34679 b.el.select('iframe',true).setSize(width,height);
34683 for (var i = 0; i < this.cols; i++){
34685 if(maxY[i] < maxY[col]){
34690 col = Math.min(col, i);
34694 x = pos.x + col * (this.colWidth + this.padWidth);
34698 var positions = [];
34700 switch (box.length){
34702 positions = this.getVerticalOneBoxColPositions(x, y, box);
34705 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34708 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34711 positions = this.getVerticalFourBoxColPositions(x, y, box);
34717 Roo.each(box, function(b,kk){
34719 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34721 var sz = b.el.getSize();
34723 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34731 for (var i = 0; i < this.cols; i++){
34732 mY = Math.max(mY, maxY[i]);
34735 this.el.setHeight(mY - pos.y);
34739 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34741 // var pos = this.el.getBox(true);
34744 // var maxX = pos.right;
34746 // var maxHeight = 0;
34748 // Roo.each(items, function(item, k){
34752 // item.el.position('absolute');
34754 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34756 // item.el.setWidth(width);
34758 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34760 // item.el.setHeight(height);
34763 // item.el.setXY([x, y], isInstant ? false : true);
34765 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34768 // y = y + height + this.alternativePadWidth;
34770 // maxHeight = maxHeight + height + this.alternativePadWidth;
34774 // this.el.setHeight(maxHeight);
34778 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34780 var pos = this.el.getBox(true);
34785 var maxX = pos.right;
34787 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34789 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34791 Roo.each(queue, function(box, k){
34793 Roo.each(box, function(b, kk){
34795 b.el.position('absolute');
34797 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34798 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34800 if(b.size == 'md-left' || b.size == 'md-right'){
34801 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34802 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34805 b.el.setWidth(width);
34806 b.el.setHeight(height);
34814 var positions = [];
34816 switch (box.length){
34818 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34821 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34824 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34827 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34833 Roo.each(box, function(b,kk){
34835 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34837 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34845 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34847 Roo.each(eItems, function(b,k){
34849 b.size = (k == 0) ? 'sm' : 'xs';
34850 b.x = (k == 0) ? 2 : 1;
34851 b.y = (k == 0) ? 2 : 1;
34853 b.el.position('absolute');
34855 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34857 b.el.setWidth(width);
34859 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34861 b.el.setHeight(height);
34865 var positions = [];
34868 x : maxX - this.unitWidth * 2 - this.gutter,
34873 x : maxX - this.unitWidth,
34874 y : minY + (this.unitWidth + this.gutter) * 2
34878 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34882 Roo.each(eItems, function(b,k){
34884 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34890 getVerticalOneBoxColPositions : function(x, y, box)
34894 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34896 if(box[0].size == 'md-left'){
34900 if(box[0].size == 'md-right'){
34905 x : x + (this.unitWidth + this.gutter) * rand,
34912 getVerticalTwoBoxColPositions : function(x, y, box)
34916 if(box[0].size == 'xs'){
34920 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34924 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34938 x : x + (this.unitWidth + this.gutter) * 2,
34939 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34946 getVerticalThreeBoxColPositions : function(x, y, box)
34950 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34958 x : x + (this.unitWidth + this.gutter) * 1,
34963 x : x + (this.unitWidth + this.gutter) * 2,
34971 if(box[0].size == 'xs' && box[1].size == 'xs'){
34980 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34984 x : x + (this.unitWidth + this.gutter) * 1,
34998 x : x + (this.unitWidth + this.gutter) * 2,
35003 x : x + (this.unitWidth + this.gutter) * 2,
35004 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35011 getVerticalFourBoxColPositions : function(x, y, box)
35015 if(box[0].size == 'xs'){
35024 y : y + (this.unitHeight + this.gutter) * 1
35029 y : y + (this.unitHeight + this.gutter) * 2
35033 x : x + (this.unitWidth + this.gutter) * 1,
35047 x : x + (this.unitWidth + this.gutter) * 2,
35052 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35053 y : y + (this.unitHeight + this.gutter) * 1
35057 x : x + (this.unitWidth + this.gutter) * 2,
35058 y : y + (this.unitWidth + this.gutter) * 2
35065 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35069 if(box[0].size == 'md-left'){
35071 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35078 if(box[0].size == 'md-right'){
35080 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35081 y : minY + (this.unitWidth + this.gutter) * 1
35087 var rand = Math.floor(Math.random() * (4 - box[0].y));
35090 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35091 y : minY + (this.unitWidth + this.gutter) * rand
35098 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35102 if(box[0].size == 'xs'){
35105 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35110 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35111 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35119 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35124 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35125 y : minY + (this.unitWidth + this.gutter) * 2
35132 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35136 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35139 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35144 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35145 y : minY + (this.unitWidth + this.gutter) * 1
35149 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35150 y : minY + (this.unitWidth + this.gutter) * 2
35157 if(box[0].size == 'xs' && box[1].size == 'xs'){
35160 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35165 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35170 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35171 y : minY + (this.unitWidth + this.gutter) * 1
35179 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35184 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35185 y : minY + (this.unitWidth + this.gutter) * 2
35189 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35190 y : minY + (this.unitWidth + this.gutter) * 2
35197 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35201 if(box[0].size == 'xs'){
35204 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35209 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35214 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),
35219 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35220 y : minY + (this.unitWidth + this.gutter) * 1
35228 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35233 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35234 y : minY + (this.unitWidth + this.gutter) * 2
35238 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35239 y : minY + (this.unitWidth + this.gutter) * 2
35243 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),
35244 y : minY + (this.unitWidth + this.gutter) * 2
35252 * remove a Masonry Brick
35253 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35255 removeBrick : function(brick_id)
35261 for (var i = 0; i<this.bricks.length; i++) {
35262 if (this.bricks[i].id == brick_id) {
35263 this.bricks.splice(i,1);
35264 this.el.dom.removeChild(Roo.get(brick_id).dom);
35271 * adds a Masonry Brick
35272 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35274 addBrick : function(cfg)
35276 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35277 //this.register(cn);
35278 cn.parentId = this.id;
35279 cn.render(this.el);
35284 * register a Masonry Brick
35285 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35288 register : function(brick)
35290 this.bricks.push(brick);
35291 brick.masonryId = this.id;
35295 * clear all the Masonry Brick
35297 clearAll : function()
35300 //this.getChildContainer().dom.innerHTML = "";
35301 this.el.dom.innerHTML = '';
35304 getSelected : function()
35306 if (!this.selectedBrick) {
35310 return this.selectedBrick;
35314 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35318 * register a Masonry Layout
35319 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35322 register : function(layout)
35324 this.groups[layout.id] = layout;
35327 * fetch a Masonry Layout based on the masonry layout ID
35328 * @param {string} the masonry layout to add
35329 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35332 get: function(layout_id) {
35333 if (typeof(this.groups[layout_id]) == 'undefined') {
35336 return this.groups[layout_id] ;
35348 * http://masonry.desandro.com
35350 * The idea is to render all the bricks based on vertical width...
35352 * The original code extends 'outlayer' - we might need to use that....
35358 * @class Roo.bootstrap.LayoutMasonryAuto
35359 * @extends Roo.bootstrap.Component
35360 * Bootstrap Layout Masonry class
35363 * Create a new Element
35364 * @param {Object} config The config object
35367 Roo.bootstrap.LayoutMasonryAuto = function(config){
35368 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35371 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35374 * @cfg {Boolean} isFitWidth - resize the width..
35376 isFitWidth : false, // options..
35378 * @cfg {Boolean} isOriginLeft = left align?
35380 isOriginLeft : true,
35382 * @cfg {Boolean} isOriginTop = top align?
35384 isOriginTop : false,
35386 * @cfg {Boolean} isLayoutInstant = no animation?
35388 isLayoutInstant : false, // needed?
35390 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35392 isResizingContainer : true,
35394 * @cfg {Number} columnWidth width of the columns
35400 * @cfg {Number} maxCols maximum number of columns
35405 * @cfg {Number} padHeight padding below box..
35411 * @cfg {Boolean} isAutoInitial defalut true
35414 isAutoInitial : true,
35420 initialColumnWidth : 0,
35421 currentSize : null,
35423 colYs : null, // array.
35430 bricks: null, //CompositeElement
35431 cols : 0, // array?
35432 // element : null, // wrapped now this.el
35433 _isLayoutInited : null,
35436 getAutoCreate : function(){
35440 cls: 'blog-masonary-wrapper ' + this.cls,
35442 cls : 'mas-boxes masonary'
35449 getChildContainer: function( )
35451 if (this.boxesEl) {
35452 return this.boxesEl;
35455 this.boxesEl = this.el.select('.mas-boxes').first();
35457 return this.boxesEl;
35461 initEvents : function()
35465 if(this.isAutoInitial){
35466 Roo.log('hook children rendered');
35467 this.on('childrenrendered', function() {
35468 Roo.log('children rendered');
35475 initial : function()
35477 this.reloadItems();
35479 this.currentSize = this.el.getBox(true);
35481 /// was window resize... - let's see if this works..
35482 Roo.EventManager.onWindowResize(this.resize, this);
35484 if(!this.isAutoInitial){
35489 this.layout.defer(500,this);
35492 reloadItems: function()
35494 this.bricks = this.el.select('.masonry-brick', true);
35496 this.bricks.each(function(b) {
35497 //Roo.log(b.getSize());
35498 if (!b.attr('originalwidth')) {
35499 b.attr('originalwidth', b.getSize().width);
35504 Roo.log(this.bricks.elements.length);
35507 resize : function()
35510 var cs = this.el.getBox(true);
35512 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35513 Roo.log("no change in with or X");
35516 this.currentSize = cs;
35520 layout : function()
35523 this._resetLayout();
35524 //this._manageStamps();
35526 // don't animate first layout
35527 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35528 this.layoutItems( isInstant );
35530 // flag for initalized
35531 this._isLayoutInited = true;
35534 layoutItems : function( isInstant )
35536 //var items = this._getItemsForLayout( this.items );
35537 // original code supports filtering layout items.. we just ignore it..
35539 this._layoutItems( this.bricks , isInstant );
35541 this._postLayout();
35543 _layoutItems : function ( items , isInstant)
35545 //this.fireEvent( 'layout', this, items );
35548 if ( !items || !items.elements.length ) {
35549 // no items, emit event with empty array
35554 items.each(function(item) {
35555 Roo.log("layout item");
35557 // get x/y object from method
35558 var position = this._getItemLayoutPosition( item );
35560 position.item = item;
35561 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35562 queue.push( position );
35565 this._processLayoutQueue( queue );
35567 /** Sets position of item in DOM
35568 * @param {Element} item
35569 * @param {Number} x - horizontal position
35570 * @param {Number} y - vertical position
35571 * @param {Boolean} isInstant - disables transitions
35573 _processLayoutQueue : function( queue )
35575 for ( var i=0, len = queue.length; i < len; i++ ) {
35576 var obj = queue[i];
35577 obj.item.position('absolute');
35578 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35584 * Any logic you want to do after each layout,
35585 * i.e. size the container
35587 _postLayout : function()
35589 this.resizeContainer();
35592 resizeContainer : function()
35594 if ( !this.isResizingContainer ) {
35597 var size = this._getContainerSize();
35599 this.el.setSize(size.width,size.height);
35600 this.boxesEl.setSize(size.width,size.height);
35606 _resetLayout : function()
35608 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35609 this.colWidth = this.el.getWidth();
35610 //this.gutter = this.el.getWidth();
35612 this.measureColumns();
35618 this.colYs.push( 0 );
35624 measureColumns : function()
35626 this.getContainerWidth();
35627 // if columnWidth is 0, default to outerWidth of first item
35628 if ( !this.columnWidth ) {
35629 var firstItem = this.bricks.first();
35630 Roo.log(firstItem);
35631 this.columnWidth = this.containerWidth;
35632 if (firstItem && firstItem.attr('originalwidth') ) {
35633 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35635 // columnWidth fall back to item of first element
35636 Roo.log("set column width?");
35637 this.initialColumnWidth = this.columnWidth ;
35639 // if first elem has no width, default to size of container
35644 if (this.initialColumnWidth) {
35645 this.columnWidth = this.initialColumnWidth;
35650 // column width is fixed at the top - however if container width get's smaller we should
35653 // this bit calcs how man columns..
35655 var columnWidth = this.columnWidth += this.gutter;
35657 // calculate columns
35658 var containerWidth = this.containerWidth + this.gutter;
35660 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35661 // fix rounding errors, typically with gutters
35662 var excess = columnWidth - containerWidth % columnWidth;
35665 // if overshoot is less than a pixel, round up, otherwise floor it
35666 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35667 cols = Math[ mathMethod ]( cols );
35668 this.cols = Math.max( cols, 1 );
35669 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35671 // padding positioning..
35672 var totalColWidth = this.cols * this.columnWidth;
35673 var padavail = this.containerWidth - totalColWidth;
35674 // so for 2 columns - we need 3 'pads'
35676 var padNeeded = (1+this.cols) * this.padWidth;
35678 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35680 this.columnWidth += padExtra
35681 //this.padWidth = Math.floor(padavail / ( this.cols));
35683 // adjust colum width so that padding is fixed??
35685 // we have 3 columns ... total = width * 3
35686 // we have X left over... that should be used by
35688 //if (this.expandC) {
35696 getContainerWidth : function()
35698 /* // container is parent if fit width
35699 var container = this.isFitWidth ? this.element.parentNode : this.element;
35700 // check that this.size and size are there
35701 // IE8 triggers resize on body size change, so they might not be
35703 var size = getSize( container ); //FIXME
35704 this.containerWidth = size && size.innerWidth; //FIXME
35707 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35711 _getItemLayoutPosition : function( item ) // what is item?
35713 // we resize the item to our columnWidth..
35715 item.setWidth(this.columnWidth);
35716 item.autoBoxAdjust = false;
35718 var sz = item.getSize();
35720 // how many columns does this brick span
35721 var remainder = this.containerWidth % this.columnWidth;
35723 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35724 // round if off by 1 pixel, otherwise use ceil
35725 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35726 colSpan = Math.min( colSpan, this.cols );
35728 // normally this should be '1' as we dont' currently allow multi width columns..
35730 var colGroup = this._getColGroup( colSpan );
35731 // get the minimum Y value from the columns
35732 var minimumY = Math.min.apply( Math, colGroup );
35733 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35735 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35737 // position the brick
35739 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35740 y: this.currentSize.y + minimumY + this.padHeight
35744 // apply setHeight to necessary columns
35745 var setHeight = minimumY + sz.height + this.padHeight;
35746 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35748 var setSpan = this.cols + 1 - colGroup.length;
35749 for ( var i = 0; i < setSpan; i++ ) {
35750 this.colYs[ shortColIndex + i ] = setHeight ;
35757 * @param {Number} colSpan - number of columns the element spans
35758 * @returns {Array} colGroup
35760 _getColGroup : function( colSpan )
35762 if ( colSpan < 2 ) {
35763 // if brick spans only one column, use all the column Ys
35768 // how many different places could this brick fit horizontally
35769 var groupCount = this.cols + 1 - colSpan;
35770 // for each group potential horizontal position
35771 for ( var i = 0; i < groupCount; i++ ) {
35772 // make an array of colY values for that one group
35773 var groupColYs = this.colYs.slice( i, i + colSpan );
35774 // and get the max value of the array
35775 colGroup[i] = Math.max.apply( Math, groupColYs );
35780 _manageStamp : function( stamp )
35782 var stampSize = stamp.getSize();
35783 var offset = stamp.getBox();
35784 // get the columns that this stamp affects
35785 var firstX = this.isOriginLeft ? offset.x : offset.right;
35786 var lastX = firstX + stampSize.width;
35787 var firstCol = Math.floor( firstX / this.columnWidth );
35788 firstCol = Math.max( 0, firstCol );
35790 var lastCol = Math.floor( lastX / this.columnWidth );
35791 // lastCol should not go over if multiple of columnWidth #425
35792 lastCol -= lastX % this.columnWidth ? 0 : 1;
35793 lastCol = Math.min( this.cols - 1, lastCol );
35795 // set colYs to bottom of the stamp
35796 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35799 for ( var i = firstCol; i <= lastCol; i++ ) {
35800 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35805 _getContainerSize : function()
35807 this.maxY = Math.max.apply( Math, this.colYs );
35812 if ( this.isFitWidth ) {
35813 size.width = this._getContainerFitWidth();
35819 _getContainerFitWidth : function()
35821 var unusedCols = 0;
35822 // count unused columns
35825 if ( this.colYs[i] !== 0 ) {
35830 // fit container to columns that have been used
35831 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35834 needsResizeLayout : function()
35836 var previousWidth = this.containerWidth;
35837 this.getContainerWidth();
35838 return previousWidth !== this.containerWidth;
35853 * @class Roo.bootstrap.MasonryBrick
35854 * @extends Roo.bootstrap.Component
35855 * Bootstrap MasonryBrick class
35858 * Create a new MasonryBrick
35859 * @param {Object} config The config object
35862 Roo.bootstrap.MasonryBrick = function(config){
35864 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35866 Roo.bootstrap.MasonryBrick.register(this);
35872 * When a MasonryBrick is clcik
35873 * @param {Roo.bootstrap.MasonryBrick} this
35874 * @param {Roo.EventObject} e
35880 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35883 * @cfg {String} title
35887 * @cfg {String} html
35891 * @cfg {String} bgimage
35895 * @cfg {String} videourl
35899 * @cfg {String} cls
35903 * @cfg {String} href
35907 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35912 * @cfg {String} placetitle (center|bottom)
35917 * @cfg {Boolean} isFitContainer defalut true
35919 isFitContainer : true,
35922 * @cfg {Boolean} preventDefault defalut false
35924 preventDefault : false,
35927 * @cfg {Boolean} inverse defalut false
35929 maskInverse : false,
35931 getAutoCreate : function()
35933 if(!this.isFitContainer){
35934 return this.getSplitAutoCreate();
35937 var cls = 'masonry-brick masonry-brick-full';
35939 if(this.href.length){
35940 cls += ' masonry-brick-link';
35943 if(this.bgimage.length){
35944 cls += ' masonry-brick-image';
35947 if(this.maskInverse){
35948 cls += ' mask-inverse';
35951 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35952 cls += ' enable-mask';
35956 cls += ' masonry-' + this.size + '-brick';
35959 if(this.placetitle.length){
35961 switch (this.placetitle) {
35963 cls += ' masonry-center-title';
35966 cls += ' masonry-bottom-title';
35973 if(!this.html.length && !this.bgimage.length){
35974 cls += ' masonry-center-title';
35977 if(!this.html.length && this.bgimage.length){
35978 cls += ' masonry-bottom-title';
35983 cls += ' ' + this.cls;
35987 tag: (this.href.length) ? 'a' : 'div',
35992 cls: 'masonry-brick-mask'
35996 cls: 'masonry-brick-paragraph',
36002 if(this.href.length){
36003 cfg.href = this.href;
36006 var cn = cfg.cn[1].cn;
36008 if(this.title.length){
36011 cls: 'masonry-brick-title',
36016 if(this.html.length){
36019 cls: 'masonry-brick-text',
36024 if (!this.title.length && !this.html.length) {
36025 cfg.cn[1].cls += ' hide';
36028 if(this.bgimage.length){
36031 cls: 'masonry-brick-image-view',
36036 if(this.videourl.length){
36037 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36038 // youtube support only?
36041 cls: 'masonry-brick-image-view',
36044 allowfullscreen : true
36052 getSplitAutoCreate : function()
36054 var cls = 'masonry-brick masonry-brick-split';
36056 if(this.href.length){
36057 cls += ' masonry-brick-link';
36060 if(this.bgimage.length){
36061 cls += ' masonry-brick-image';
36065 cls += ' masonry-' + this.size + '-brick';
36068 switch (this.placetitle) {
36070 cls += ' masonry-center-title';
36073 cls += ' masonry-bottom-title';
36076 if(!this.bgimage.length){
36077 cls += ' masonry-center-title';
36080 if(this.bgimage.length){
36081 cls += ' masonry-bottom-title';
36087 cls += ' ' + this.cls;
36091 tag: (this.href.length) ? 'a' : 'div',
36096 cls: 'masonry-brick-split-head',
36100 cls: 'masonry-brick-paragraph',
36107 cls: 'masonry-brick-split-body',
36113 if(this.href.length){
36114 cfg.href = this.href;
36117 if(this.title.length){
36118 cfg.cn[0].cn[0].cn.push({
36120 cls: 'masonry-brick-title',
36125 if(this.html.length){
36126 cfg.cn[1].cn.push({
36128 cls: 'masonry-brick-text',
36133 if(this.bgimage.length){
36134 cfg.cn[0].cn.push({
36136 cls: 'masonry-brick-image-view',
36141 if(this.videourl.length){
36142 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36143 // youtube support only?
36144 cfg.cn[0].cn.cn.push({
36146 cls: 'masonry-brick-image-view',
36149 allowfullscreen : true
36156 initEvents: function()
36158 switch (this.size) {
36191 this.el.on('touchstart', this.onTouchStart, this);
36192 this.el.on('touchmove', this.onTouchMove, this);
36193 this.el.on('touchend', this.onTouchEnd, this);
36194 this.el.on('contextmenu', this.onContextMenu, this);
36196 this.el.on('mouseenter' ,this.enter, this);
36197 this.el.on('mouseleave', this.leave, this);
36198 this.el.on('click', this.onClick, this);
36201 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36202 this.parent().bricks.push(this);
36207 onClick: function(e, el)
36209 var time = this.endTimer - this.startTimer;
36210 // Roo.log(e.preventDefault());
36213 e.preventDefault();
36218 if(!this.preventDefault){
36222 e.preventDefault();
36224 if (this.activeClass != '') {
36225 this.selectBrick();
36228 this.fireEvent('click', this, e);
36231 enter: function(e, el)
36233 e.preventDefault();
36235 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36239 if(this.bgimage.length && this.html.length){
36240 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36244 leave: function(e, el)
36246 e.preventDefault();
36248 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36252 if(this.bgimage.length && this.html.length){
36253 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36257 onTouchStart: function(e, el)
36259 // e.preventDefault();
36261 this.touchmoved = false;
36263 if(!this.isFitContainer){
36267 if(!this.bgimage.length || !this.html.length){
36271 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36273 this.timer = new Date().getTime();
36277 onTouchMove: function(e, el)
36279 this.touchmoved = true;
36282 onContextMenu : function(e,el)
36284 e.preventDefault();
36285 e.stopPropagation();
36289 onTouchEnd: function(e, el)
36291 // e.preventDefault();
36293 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36300 if(!this.bgimage.length || !this.html.length){
36302 if(this.href.length){
36303 window.location.href = this.href;
36309 if(!this.isFitContainer){
36313 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36315 window.location.href = this.href;
36318 //selection on single brick only
36319 selectBrick : function() {
36321 if (!this.parentId) {
36325 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36326 var index = m.selectedBrick.indexOf(this.id);
36329 m.selectedBrick.splice(index,1);
36330 this.el.removeClass(this.activeClass);
36334 for(var i = 0; i < m.selectedBrick.length; i++) {
36335 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36336 b.el.removeClass(b.activeClass);
36339 m.selectedBrick = [];
36341 m.selectedBrick.push(this.id);
36342 this.el.addClass(this.activeClass);
36346 isSelected : function(){
36347 return this.el.hasClass(this.activeClass);
36352 Roo.apply(Roo.bootstrap.MasonryBrick, {
36355 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36357 * register a Masonry Brick
36358 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36361 register : function(brick)
36363 //this.groups[brick.id] = brick;
36364 this.groups.add(brick.id, brick);
36367 * fetch a masonry brick based on the masonry brick ID
36368 * @param {string} the masonry brick to add
36369 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36372 get: function(brick_id)
36374 // if (typeof(this.groups[brick_id]) == 'undefined') {
36377 // return this.groups[brick_id] ;
36379 if(this.groups.key(brick_id)) {
36380 return this.groups.key(brick_id);
36398 * @class Roo.bootstrap.Brick
36399 * @extends Roo.bootstrap.Component
36400 * Bootstrap Brick class
36403 * Create a new Brick
36404 * @param {Object} config The config object
36407 Roo.bootstrap.Brick = function(config){
36408 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36414 * When a Brick is click
36415 * @param {Roo.bootstrap.Brick} this
36416 * @param {Roo.EventObject} e
36422 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36425 * @cfg {String} title
36429 * @cfg {String} html
36433 * @cfg {String} bgimage
36437 * @cfg {String} cls
36441 * @cfg {String} href
36445 * @cfg {String} video
36449 * @cfg {Boolean} square
36453 getAutoCreate : function()
36455 var cls = 'roo-brick';
36457 if(this.href.length){
36458 cls += ' roo-brick-link';
36461 if(this.bgimage.length){
36462 cls += ' roo-brick-image';
36465 if(!this.html.length && !this.bgimage.length){
36466 cls += ' roo-brick-center-title';
36469 if(!this.html.length && this.bgimage.length){
36470 cls += ' roo-brick-bottom-title';
36474 cls += ' ' + this.cls;
36478 tag: (this.href.length) ? 'a' : 'div',
36483 cls: 'roo-brick-paragraph',
36489 if(this.href.length){
36490 cfg.href = this.href;
36493 var cn = cfg.cn[0].cn;
36495 if(this.title.length){
36498 cls: 'roo-brick-title',
36503 if(this.html.length){
36506 cls: 'roo-brick-text',
36513 if(this.bgimage.length){
36516 cls: 'roo-brick-image-view',
36524 initEvents: function()
36526 if(this.title.length || this.html.length){
36527 this.el.on('mouseenter' ,this.enter, this);
36528 this.el.on('mouseleave', this.leave, this);
36531 Roo.EventManager.onWindowResize(this.resize, this);
36533 if(this.bgimage.length){
36534 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36535 this.imageEl.on('load', this.onImageLoad, this);
36542 onImageLoad : function()
36547 resize : function()
36549 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36551 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36553 if(this.bgimage.length){
36554 var image = this.el.select('.roo-brick-image-view', true).first();
36556 image.setWidth(paragraph.getWidth());
36559 image.setHeight(paragraph.getWidth());
36562 this.el.setHeight(image.getHeight());
36563 paragraph.setHeight(image.getHeight());
36569 enter: function(e, el)
36571 e.preventDefault();
36573 if(this.bgimage.length){
36574 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36575 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36579 leave: function(e, el)
36581 e.preventDefault();
36583 if(this.bgimage.length){
36584 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36585 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36600 * @class Roo.bootstrap.form.NumberField
36601 * @extends Roo.bootstrap.form.Input
36602 * Bootstrap NumberField class
36608 * Create a new NumberField
36609 * @param {Object} config The config object
36612 Roo.bootstrap.form.NumberField = function(config){
36613 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36616 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36619 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36621 allowDecimals : true,
36623 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36625 decimalSeparator : ".",
36627 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36629 decimalPrecision : 2,
36631 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36633 allowNegative : true,
36636 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36640 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36642 minValue : Number.NEGATIVE_INFINITY,
36644 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36646 maxValue : Number.MAX_VALUE,
36648 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36650 minText : "The minimum value for this field is {0}",
36652 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36654 maxText : "The maximum value for this field is {0}",
36656 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36657 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36659 nanText : "{0} is not a valid number",
36661 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36663 thousandsDelimiter : false,
36665 * @cfg {String} valueAlign alignment of value
36667 valueAlign : "left",
36669 getAutoCreate : function()
36671 var hiddenInput = {
36675 cls: 'hidden-number-input'
36679 hiddenInput.name = this.name;
36684 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36686 this.name = hiddenInput.name;
36688 if(cfg.cn.length > 0) {
36689 cfg.cn.push(hiddenInput);
36696 initEvents : function()
36698 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36700 var allowed = "0123456789";
36702 if(this.allowDecimals){
36703 allowed += this.decimalSeparator;
36706 if(this.allowNegative){
36710 if(this.thousandsDelimiter) {
36714 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36716 var keyPress = function(e){
36718 var k = e.getKey();
36720 var c = e.getCharCode();
36723 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36724 allowed.indexOf(String.fromCharCode(c)) === -1
36730 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36734 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36739 this.el.on("keypress", keyPress, this);
36742 validateValue : function(value)
36745 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36749 var num = this.parseValue(value);
36752 this.markInvalid(String.format(this.nanText, value));
36756 if(num < this.minValue){
36757 this.markInvalid(String.format(this.minText, this.minValue));
36761 if(num > this.maxValue){
36762 this.markInvalid(String.format(this.maxText, this.maxValue));
36769 getValue : function()
36771 var v = this.hiddenEl().getValue();
36773 return this.fixPrecision(this.parseValue(v));
36776 parseValue : function(value)
36778 if(this.thousandsDelimiter) {
36780 r = new RegExp(",", "g");
36781 value = value.replace(r, "");
36784 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36785 return isNaN(value) ? '' : value;
36788 fixPrecision : function(value)
36790 if(this.thousandsDelimiter) {
36792 r = new RegExp(",", "g");
36793 value = value.replace(r, "");
36796 var nan = isNaN(value);
36798 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36799 return nan ? '' : value;
36801 return parseFloat(value).toFixed(this.decimalPrecision);
36804 setValue : function(v)
36806 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36812 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36814 this.inputEl().dom.value = (v == '') ? '' :
36815 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36817 if(!this.allowZero && v === '0') {
36818 this.hiddenEl().dom.value = '';
36819 this.inputEl().dom.value = '';
36826 decimalPrecisionFcn : function(v)
36828 return Math.floor(v);
36831 beforeBlur : function()
36833 var v = this.parseValue(this.getRawValue());
36835 if(v || v === 0 || v === ''){
36840 hiddenEl : function()
36842 return this.el.select('input.hidden-number-input',true).first();
36854 * @class Roo.bootstrap.DocumentSlider
36855 * @extends Roo.bootstrap.Component
36856 * Bootstrap DocumentSlider class
36859 * Create a new DocumentViewer
36860 * @param {Object} config The config object
36863 Roo.bootstrap.DocumentSlider = function(config){
36864 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36871 * Fire after initEvent
36872 * @param {Roo.bootstrap.DocumentSlider} this
36877 * Fire after update
36878 * @param {Roo.bootstrap.DocumentSlider} this
36884 * @param {Roo.bootstrap.DocumentSlider} this
36890 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36896 getAutoCreate : function()
36900 cls : 'roo-document-slider',
36904 cls : 'roo-document-slider-header',
36908 cls : 'roo-document-slider-header-title'
36914 cls : 'roo-document-slider-body',
36918 cls : 'roo-document-slider-prev',
36922 cls : 'fa fa-chevron-left'
36928 cls : 'roo-document-slider-thumb',
36932 cls : 'roo-document-slider-image'
36938 cls : 'roo-document-slider-next',
36942 cls : 'fa fa-chevron-right'
36954 initEvents : function()
36956 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36957 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36959 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36960 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36962 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36963 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36965 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36966 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36968 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36969 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36971 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36972 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36974 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36975 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36977 this.thumbEl.on('click', this.onClick, this);
36979 this.prevIndicator.on('click', this.prev, this);
36981 this.nextIndicator.on('click', this.next, this);
36985 initial : function()
36987 if(this.files.length){
36988 this.indicator = 1;
36992 this.fireEvent('initial', this);
36995 update : function()
36997 this.imageEl.attr('src', this.files[this.indicator - 1]);
36999 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37001 this.prevIndicator.show();
37003 if(this.indicator == 1){
37004 this.prevIndicator.hide();
37007 this.nextIndicator.show();
37009 if(this.indicator == this.files.length){
37010 this.nextIndicator.hide();
37013 this.thumbEl.scrollTo('top');
37015 this.fireEvent('update', this);
37018 onClick : function(e)
37020 e.preventDefault();
37022 this.fireEvent('click', this);
37027 e.preventDefault();
37029 this.indicator = Math.max(1, this.indicator - 1);
37036 e.preventDefault();
37038 this.indicator = Math.min(this.files.length, this.indicator + 1);
37052 * @class Roo.bootstrap.form.RadioSet
37053 * @extends Roo.bootstrap.form.Input
37054 * @children Roo.bootstrap.form.Radio
37055 * Bootstrap RadioSet class
37056 * @cfg {String} indicatorpos (left|right) default left
37057 * @cfg {Boolean} inline (true|false) inline the element (default true)
37058 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37060 * Create a new RadioSet
37061 * @param {Object} config The config object
37064 Roo.bootstrap.form.RadioSet = function(config){
37066 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37070 Roo.bootstrap.form.RadioSet.register(this);
37075 * Fires when the element is checked or unchecked.
37076 * @param {Roo.bootstrap.form.RadioSet} this This radio
37077 * @param {Roo.bootstrap.form.Radio} item The checked item
37082 * Fires when the element is click.
37083 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37084 * @param {Roo.bootstrap.form.Radio} item The checked item
37085 * @param {Roo.EventObject} e The event object
37092 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37100 indicatorpos : 'left',
37102 getAutoCreate : function()
37106 cls : 'roo-radio-set-label',
37110 html : this.fieldLabel
37114 if (Roo.bootstrap.version == 3) {
37117 if(this.indicatorpos == 'left'){
37120 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37121 tooltip : 'This field is required'
37126 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37127 tooltip : 'This field is required'
37133 cls : 'roo-radio-set-items'
37136 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37138 if (align === 'left' && this.fieldLabel.length) {
37141 cls : "roo-radio-set-right",
37147 if(this.labelWidth > 12){
37148 label.style = "width: " + this.labelWidth + 'px';
37151 if(this.labelWidth < 13 && this.labelmd == 0){
37152 this.labelmd = this.labelWidth;
37155 if(this.labellg > 0){
37156 label.cls += ' col-lg-' + this.labellg;
37157 items.cls += ' col-lg-' + (12 - this.labellg);
37160 if(this.labelmd > 0){
37161 label.cls += ' col-md-' + this.labelmd;
37162 items.cls += ' col-md-' + (12 - this.labelmd);
37165 if(this.labelsm > 0){
37166 label.cls += ' col-sm-' + this.labelsm;
37167 items.cls += ' col-sm-' + (12 - this.labelsm);
37170 if(this.labelxs > 0){
37171 label.cls += ' col-xs-' + this.labelxs;
37172 items.cls += ' col-xs-' + (12 - this.labelxs);
37178 cls : 'roo-radio-set',
37182 cls : 'roo-radio-set-input',
37185 value : this.value ? this.value : ''
37192 if(this.weight.length){
37193 cfg.cls += ' roo-radio-' + this.weight;
37197 cfg.cls += ' roo-radio-set-inline';
37201 ['xs','sm','md','lg'].map(function(size){
37202 if (settings[size]) {
37203 cfg.cls += ' col-' + size + '-' + settings[size];
37211 initEvents : function()
37213 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37214 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37216 if(!this.fieldLabel.length){
37217 this.labelEl.hide();
37220 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37221 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37223 this.indicator = this.indicatorEl();
37225 if(this.indicator){
37226 this.indicator.addClass('invisible');
37229 this.originalValue = this.getValue();
37233 inputEl: function ()
37235 return this.el.select('.roo-radio-set-input', true).first();
37238 getChildContainer : function()
37240 return this.itemsEl;
37243 register : function(item)
37245 this.radioes.push(item);
37249 validate : function()
37251 if(this.getVisibilityEl().hasClass('hidden')){
37257 Roo.each(this.radioes, function(i){
37266 if(this.allowBlank) {
37270 if(this.disabled || valid){
37275 this.markInvalid();
37280 markValid : function()
37282 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37283 this.indicatorEl().removeClass('visible');
37284 this.indicatorEl().addClass('invisible');
37288 if (Roo.bootstrap.version == 3) {
37289 this.el.removeClass([this.invalidClass, this.validClass]);
37290 this.el.addClass(this.validClass);
37292 this.el.removeClass(['is-invalid','is-valid']);
37293 this.el.addClass(['is-valid']);
37295 this.fireEvent('valid', this);
37298 markInvalid : function(msg)
37300 if(this.allowBlank || this.disabled){
37304 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37305 this.indicatorEl().removeClass('invisible');
37306 this.indicatorEl().addClass('visible');
37308 if (Roo.bootstrap.version == 3) {
37309 this.el.removeClass([this.invalidClass, this.validClass]);
37310 this.el.addClass(this.invalidClass);
37312 this.el.removeClass(['is-invalid','is-valid']);
37313 this.el.addClass(['is-invalid']);
37316 this.fireEvent('invalid', this, msg);
37320 setValue : function(v, suppressEvent)
37322 if(this.value === v){
37329 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37332 Roo.each(this.radioes, function(i){
37334 i.el.removeClass('checked');
37337 Roo.each(this.radioes, function(i){
37339 if(i.value === v || i.value.toString() === v.toString()){
37341 i.el.addClass('checked');
37343 if(suppressEvent !== true){
37344 this.fireEvent('check', this, i);
37355 clearInvalid : function(){
37357 if(!this.el || this.preventMark){
37361 this.el.removeClass([this.invalidClass]);
37363 this.fireEvent('valid', this);
37368 Roo.apply(Roo.bootstrap.form.RadioSet, {
37372 register : function(set)
37374 this.groups[set.name] = set;
37377 get: function(name)
37379 if (typeof(this.groups[name]) == 'undefined') {
37383 return this.groups[name] ;
37389 * Ext JS Library 1.1.1
37390 * Copyright(c) 2006-2007, Ext JS, LLC.
37392 * Originally Released Under LGPL - original licence link has changed is not relivant.
37395 * <script type="text/javascript">
37400 * @class Roo.bootstrap.SplitBar
37401 * @extends Roo.util.Observable
37402 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37406 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37407 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37408 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37409 split.minSize = 100;
37410 split.maxSize = 600;
37411 split.animate = true;
37412 split.on('moved', splitterMoved);
37415 * Create a new SplitBar
37416 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37417 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37418 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37419 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37420 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37421 position of the SplitBar).
37423 Roo.bootstrap.SplitBar = function(cfg){
37428 // dragElement : elm
37429 // resizingElement: el,
37431 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37432 // placement : Roo.bootstrap.SplitBar.LEFT ,
37433 // existingProxy ???
37436 this.el = Roo.get(cfg.dragElement, true);
37437 this.el.dom.unselectable = "on";
37439 this.resizingEl = Roo.get(cfg.resizingElement, true);
37443 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37444 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37447 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37450 * The minimum size of the resizing element. (Defaults to 0)
37456 * The maximum size of the resizing element. (Defaults to 2000)
37459 this.maxSize = 2000;
37462 * Whether to animate the transition to the new size
37465 this.animate = false;
37468 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37471 this.useShim = false;
37476 if(!cfg.existingProxy){
37478 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37480 this.proxy = Roo.get(cfg.existingProxy).dom;
37483 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37486 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37489 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37492 this.dragSpecs = {};
37495 * @private The adapter to use to positon and resize elements
37497 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37498 this.adapter.init(this);
37500 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37502 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37503 this.el.addClass("roo-splitbar-h");
37506 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37507 this.el.addClass("roo-splitbar-v");
37513 * Fires when the splitter is moved (alias for {@link #event-moved})
37514 * @param {Roo.bootstrap.SplitBar} this
37515 * @param {Number} newSize the new width or height
37520 * Fires when the splitter is moved
37521 * @param {Roo.bootstrap.SplitBar} this
37522 * @param {Number} newSize the new width or height
37526 * @event beforeresize
37527 * Fires before the splitter is dragged
37528 * @param {Roo.bootstrap.SplitBar} this
37530 "beforeresize" : true,
37532 "beforeapply" : true
37535 Roo.util.Observable.call(this);
37538 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37539 onStartProxyDrag : function(x, y){
37540 this.fireEvent("beforeresize", this);
37542 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37544 o.enableDisplayMode("block");
37545 // all splitbars share the same overlay
37546 Roo.bootstrap.SplitBar.prototype.overlay = o;
37548 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37549 this.overlay.show();
37550 Roo.get(this.proxy).setDisplayed("block");
37551 var size = this.adapter.getElementSize(this);
37552 this.activeMinSize = this.getMinimumSize();;
37553 this.activeMaxSize = this.getMaximumSize();;
37554 var c1 = size - this.activeMinSize;
37555 var c2 = Math.max(this.activeMaxSize - size, 0);
37556 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37557 this.dd.resetConstraints();
37558 this.dd.setXConstraint(
37559 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37560 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37562 this.dd.setYConstraint(0, 0);
37564 this.dd.resetConstraints();
37565 this.dd.setXConstraint(0, 0);
37566 this.dd.setYConstraint(
37567 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37568 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37571 this.dragSpecs.startSize = size;
37572 this.dragSpecs.startPoint = [x, y];
37573 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37577 * @private Called after the drag operation by the DDProxy
37579 onEndProxyDrag : function(e){
37580 Roo.get(this.proxy).setDisplayed(false);
37581 var endPoint = Roo.lib.Event.getXY(e);
37583 this.overlay.hide();
37586 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37587 newSize = this.dragSpecs.startSize +
37588 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37589 endPoint[0] - this.dragSpecs.startPoint[0] :
37590 this.dragSpecs.startPoint[0] - endPoint[0]
37593 newSize = this.dragSpecs.startSize +
37594 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37595 endPoint[1] - this.dragSpecs.startPoint[1] :
37596 this.dragSpecs.startPoint[1] - endPoint[1]
37599 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37600 if(newSize != this.dragSpecs.startSize){
37601 if(this.fireEvent('beforeapply', this, newSize) !== false){
37602 this.adapter.setElementSize(this, newSize);
37603 this.fireEvent("moved", this, newSize);
37604 this.fireEvent("resize", this, newSize);
37610 * Get the adapter this SplitBar uses
37611 * @return The adapter object
37613 getAdapter : function(){
37614 return this.adapter;
37618 * Set the adapter this SplitBar uses
37619 * @param {Object} adapter A SplitBar adapter object
37621 setAdapter : function(adapter){
37622 this.adapter = adapter;
37623 this.adapter.init(this);
37627 * Gets the minimum size for the resizing element
37628 * @return {Number} The minimum size
37630 getMinimumSize : function(){
37631 return this.minSize;
37635 * Sets the minimum size for the resizing element
37636 * @param {Number} minSize The minimum size
37638 setMinimumSize : function(minSize){
37639 this.minSize = minSize;
37643 * Gets the maximum size for the resizing element
37644 * @return {Number} The maximum size
37646 getMaximumSize : function(){
37647 return this.maxSize;
37651 * Sets the maximum size for the resizing element
37652 * @param {Number} maxSize The maximum size
37654 setMaximumSize : function(maxSize){
37655 this.maxSize = maxSize;
37659 * Sets the initialize size for the resizing element
37660 * @param {Number} size The initial size
37662 setCurrentSize : function(size){
37663 var oldAnimate = this.animate;
37664 this.animate = false;
37665 this.adapter.setElementSize(this, size);
37666 this.animate = oldAnimate;
37670 * Destroy this splitbar.
37671 * @param {Boolean} removeEl True to remove the element
37673 destroy : function(removeEl){
37675 this.shim.remove();
37678 this.proxy.parentNode.removeChild(this.proxy);
37686 * @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.
37688 Roo.bootstrap.SplitBar.createProxy = function(dir){
37689 var proxy = new Roo.Element(document.createElement("div"));
37690 proxy.unselectable();
37691 var cls = 'roo-splitbar-proxy';
37692 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37693 document.body.appendChild(proxy.dom);
37698 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37699 * Default Adapter. It assumes the splitter and resizing element are not positioned
37700 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37702 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37705 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37706 // do nothing for now
37707 init : function(s){
37711 * Called before drag operations to get the current size of the resizing element.
37712 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37714 getElementSize : function(s){
37715 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37716 return s.resizingEl.getWidth();
37718 return s.resizingEl.getHeight();
37723 * Called after drag operations to set the size of the resizing element.
37724 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37725 * @param {Number} newSize The new size to set
37726 * @param {Function} onComplete A function to be invoked when resizing is complete
37728 setElementSize : function(s, newSize, onComplete){
37729 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37731 s.resizingEl.setWidth(newSize);
37733 onComplete(s, newSize);
37736 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37741 s.resizingEl.setHeight(newSize);
37743 onComplete(s, newSize);
37746 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37753 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37754 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37755 * Adapter that moves the splitter element to align with the resized sizing element.
37756 * Used with an absolute positioned SplitBar.
37757 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37758 * document.body, make sure you assign an id to the body element.
37760 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37761 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37762 this.container = Roo.get(container);
37765 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37766 init : function(s){
37767 this.basic.init(s);
37770 getElementSize : function(s){
37771 return this.basic.getElementSize(s);
37774 setElementSize : function(s, newSize, onComplete){
37775 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37778 moveSplitter : function(s){
37779 var yes = Roo.bootstrap.SplitBar;
37780 switch(s.placement){
37782 s.el.setX(s.resizingEl.getRight());
37785 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37788 s.el.setY(s.resizingEl.getBottom());
37791 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37798 * Orientation constant - Create a vertical SplitBar
37802 Roo.bootstrap.SplitBar.VERTICAL = 1;
37805 * Orientation constant - Create a horizontal SplitBar
37809 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37812 * Placement constant - The resizing element is to the left of the splitter element
37816 Roo.bootstrap.SplitBar.LEFT = 1;
37819 * Placement constant - The resizing element is to the right of the splitter element
37823 Roo.bootstrap.SplitBar.RIGHT = 2;
37826 * Placement constant - The resizing element is positioned above the splitter element
37830 Roo.bootstrap.SplitBar.TOP = 3;
37833 * Placement constant - The resizing element is positioned under splitter element
37837 Roo.bootstrap.SplitBar.BOTTOM = 4;
37840 * Ext JS Library 1.1.1
37841 * Copyright(c) 2006-2007, Ext JS, LLC.
37843 * Originally Released Under LGPL - original licence link has changed is not relivant.
37846 * <script type="text/javascript">
37850 * @class Roo.bootstrap.layout.Manager
37851 * @extends Roo.bootstrap.Component
37853 * Base class for layout managers.
37855 Roo.bootstrap.layout.Manager = function(config)
37857 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37863 /** false to disable window resize monitoring @type Boolean */
37864 this.monitorWindowResize = true;
37869 * Fires when a layout is performed.
37870 * @param {Roo.LayoutManager} this
37874 * @event regionresized
37875 * Fires when the user resizes a region.
37876 * @param {Roo.LayoutRegion} region The resized region
37877 * @param {Number} newSize The new size (width for east/west, height for north/south)
37879 "regionresized" : true,
37881 * @event regioncollapsed
37882 * Fires when a region is collapsed.
37883 * @param {Roo.LayoutRegion} region The collapsed region
37885 "regioncollapsed" : true,
37887 * @event regionexpanded
37888 * Fires when a region is expanded.
37889 * @param {Roo.LayoutRegion} region The expanded region
37891 "regionexpanded" : true
37893 this.updating = false;
37896 this.el = Roo.get(config.el);
37902 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37907 monitorWindowResize : true,
37913 onRender : function(ct, position)
37916 this.el = Roo.get(ct);
37919 //this.fireEvent('render',this);
37923 initEvents: function()
37927 // ie scrollbar fix
37928 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37929 document.body.scroll = "no";
37930 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37931 this.el.position('relative');
37933 this.id = this.el.id;
37934 this.el.addClass("roo-layout-container");
37935 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37936 if(this.el.dom != document.body ) {
37937 this.el.on('resize', this.layout,this);
37938 this.el.on('show', this.layout,this);
37944 * Returns true if this layout is currently being updated
37945 * @return {Boolean}
37947 isUpdating : function(){
37948 return this.updating;
37952 * Suspend the LayoutManager from doing auto-layouts while
37953 * making multiple add or remove calls
37955 beginUpdate : function(){
37956 this.updating = true;
37960 * Restore auto-layouts and optionally disable the manager from performing a layout
37961 * @param {Boolean} noLayout true to disable a layout update
37963 endUpdate : function(noLayout){
37964 this.updating = false;
37970 layout: function(){
37974 onRegionResized : function(region, newSize){
37975 this.fireEvent("regionresized", region, newSize);
37979 onRegionCollapsed : function(region){
37980 this.fireEvent("regioncollapsed", region);
37983 onRegionExpanded : function(region){
37984 this.fireEvent("regionexpanded", region);
37988 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37989 * performs box-model adjustments.
37990 * @return {Object} The size as an object {width: (the width), height: (the height)}
37992 getViewSize : function()
37995 if(this.el.dom != document.body){
37996 size = this.el.getSize();
37998 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38000 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38001 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38006 * Returns the Element this layout is bound to.
38007 * @return {Roo.Element}
38009 getEl : function(){
38014 * Returns the specified region.
38015 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38016 * @return {Roo.LayoutRegion}
38018 getRegion : function(target){
38019 return this.regions[target.toLowerCase()];
38022 onWindowResize : function(){
38023 if(this.monitorWindowResize){
38030 * Ext JS Library 1.1.1
38031 * Copyright(c) 2006-2007, Ext JS, LLC.
38033 * Originally Released Under LGPL - original licence link has changed is not relivant.
38036 * <script type="text/javascript">
38039 * @class Roo.bootstrap.layout.Border
38040 * @extends Roo.bootstrap.layout.Manager
38041 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38042 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38043 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38044 * please see: examples/bootstrap/nested.html<br><br>
38046 <b>The container the layout is rendered into can be either the body element or any other element.
38047 If it is not the body element, the container needs to either be an absolute positioned element,
38048 or you will need to add "position:relative" to the css of the container. You will also need to specify
38049 the container size if it is not the body element.</b>
38052 * Create a new Border
38053 * @param {Object} config Configuration options
38055 Roo.bootstrap.layout.Border = function(config){
38056 config = config || {};
38057 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38061 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38062 if(config[region]){
38063 config[region].region = region;
38064 this.addRegion(config[region]);
38070 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38072 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38075 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38078 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38081 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38084 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38087 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38093 parent : false, // this might point to a 'nest' or a ???
38096 * Creates and adds a new region if it doesn't already exist.
38097 * @param {String} target The target region key (north, south, east, west or center).
38098 * @param {Object} config The regions config object
38099 * @return {BorderLayoutRegion} The new region
38101 addRegion : function(config)
38103 if(!this.regions[config.region]){
38104 var r = this.factory(config);
38105 this.bindRegion(r);
38107 return this.regions[config.region];
38111 bindRegion : function(r){
38112 this.regions[r.config.region] = r;
38114 r.on("visibilitychange", this.layout, this);
38115 r.on("paneladded", this.layout, this);
38116 r.on("panelremoved", this.layout, this);
38117 r.on("invalidated", this.layout, this);
38118 r.on("resized", this.onRegionResized, this);
38119 r.on("collapsed", this.onRegionCollapsed, this);
38120 r.on("expanded", this.onRegionExpanded, this);
38124 * Performs a layout update.
38126 layout : function()
38128 if(this.updating) {
38132 // render all the rebions if they have not been done alreayd?
38133 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38134 if(this.regions[region] && !this.regions[region].bodyEl){
38135 this.regions[region].onRender(this.el)
38139 var size = this.getViewSize();
38140 var w = size.width;
38141 var h = size.height;
38146 //var x = 0, y = 0;
38148 var rs = this.regions;
38149 var north = rs["north"];
38150 var south = rs["south"];
38151 var west = rs["west"];
38152 var east = rs["east"];
38153 var center = rs["center"];
38154 //if(this.hideOnLayout){ // not supported anymore
38155 //c.el.setStyle("display", "none");
38157 if(north && north.isVisible()){
38158 var b = north.getBox();
38159 var m = north.getMargins();
38160 b.width = w - (m.left+m.right);
38163 centerY = b.height + b.y + m.bottom;
38164 centerH -= centerY;
38165 north.updateBox(this.safeBox(b));
38167 if(south && south.isVisible()){
38168 var b = south.getBox();
38169 var m = south.getMargins();
38170 b.width = w - (m.left+m.right);
38172 var totalHeight = (b.height + m.top + m.bottom);
38173 b.y = h - totalHeight + m.top;
38174 centerH -= totalHeight;
38175 south.updateBox(this.safeBox(b));
38177 if(west && west.isVisible()){
38178 var b = west.getBox();
38179 var m = west.getMargins();
38180 b.height = centerH - (m.top+m.bottom);
38182 b.y = centerY + m.top;
38183 var totalWidth = (b.width + m.left + m.right);
38184 centerX += totalWidth;
38185 centerW -= totalWidth;
38186 west.updateBox(this.safeBox(b));
38188 if(east && east.isVisible()){
38189 var b = east.getBox();
38190 var m = east.getMargins();
38191 b.height = centerH - (m.top+m.bottom);
38192 var totalWidth = (b.width + m.left + m.right);
38193 b.x = w - totalWidth + m.left;
38194 b.y = centerY + m.top;
38195 centerW -= totalWidth;
38196 east.updateBox(this.safeBox(b));
38199 var m = center.getMargins();
38201 x: centerX + m.left,
38202 y: centerY + m.top,
38203 width: centerW - (m.left+m.right),
38204 height: centerH - (m.top+m.bottom)
38206 //if(this.hideOnLayout){
38207 //center.el.setStyle("display", "block");
38209 center.updateBox(this.safeBox(centerBox));
38212 this.fireEvent("layout", this);
38216 safeBox : function(box){
38217 box.width = Math.max(0, box.width);
38218 box.height = Math.max(0, box.height);
38223 * Adds a ContentPanel (or subclass) to this layout.
38224 * @param {String} target The target region key (north, south, east, west or center).
38225 * @param {Roo.ContentPanel} panel The panel to add
38226 * @return {Roo.ContentPanel} The added panel
38228 add : function(target, panel){
38230 target = target.toLowerCase();
38231 return this.regions[target].add(panel);
38235 * Remove a ContentPanel (or subclass) to this layout.
38236 * @param {String} target The target region key (north, south, east, west or center).
38237 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38238 * @return {Roo.ContentPanel} The removed panel
38240 remove : function(target, panel){
38241 target = target.toLowerCase();
38242 return this.regions[target].remove(panel);
38246 * Searches all regions for a panel with the specified id
38247 * @param {String} panelId
38248 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38250 findPanel : function(panelId){
38251 var rs = this.regions;
38252 for(var target in rs){
38253 if(typeof rs[target] != "function"){
38254 var p = rs[target].getPanel(panelId);
38264 * Searches all regions for a panel with the specified id and activates (shows) it.
38265 * @param {String/ContentPanel} panelId The panels id or the panel itself
38266 * @return {Roo.ContentPanel} The shown panel or null
38268 showPanel : function(panelId) {
38269 var rs = this.regions;
38270 for(var target in rs){
38271 var r = rs[target];
38272 if(typeof r != "function"){
38273 if(r.hasPanel(panelId)){
38274 return r.showPanel(panelId);
38282 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38283 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38286 restoreState : function(provider){
38288 provider = Roo.state.Manager;
38290 var sm = new Roo.LayoutStateManager();
38291 sm.init(this, provider);
38297 * Adds a xtype elements to the layout.
38301 xtype : 'ContentPanel',
38308 xtype : 'NestedLayoutPanel',
38314 items : [ ... list of content panels or nested layout panels.. ]
38318 * @param {Object} cfg Xtype definition of item to add.
38320 addxtype : function(cfg)
38322 // basically accepts a pannel...
38323 // can accept a layout region..!?!?
38324 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38327 // theory? children can only be panels??
38329 //if (!cfg.xtype.match(/Panel$/)) {
38334 if (typeof(cfg.region) == 'undefined') {
38335 Roo.log("Failed to add Panel, region was not set");
38339 var region = cfg.region;
38345 xitems = cfg.items;
38350 if ( region == 'center') {
38351 Roo.log("Center: " + cfg.title);
38357 case 'Content': // ContentPanel (el, cfg)
38358 case 'Scroll': // ContentPanel (el, cfg)
38360 cfg.autoCreate = cfg.autoCreate || true;
38361 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38363 // var el = this.el.createChild();
38364 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38367 this.add(region, ret);
38371 case 'TreePanel': // our new panel!
38372 cfg.el = this.el.createChild();
38373 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38374 this.add(region, ret);
38379 // create a new Layout (which is a Border Layout...
38381 var clayout = cfg.layout;
38382 clayout.el = this.el.createChild();
38383 clayout.items = clayout.items || [];
38387 // replace this exitems with the clayout ones..
38388 xitems = clayout.items;
38390 // force background off if it's in center...
38391 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38392 cfg.background = false;
38394 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38397 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38398 //console.log('adding nested layout panel ' + cfg.toSource());
38399 this.add(region, ret);
38400 nb = {}; /// find first...
38405 // needs grid and region
38407 //var el = this.getRegion(region).el.createChild();
38409 *var el = this.el.createChild();
38410 // create the grid first...
38411 cfg.grid.container = el;
38412 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38415 if (region == 'center' && this.active ) {
38416 cfg.background = false;
38419 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38421 this.add(region, ret);
38423 if (cfg.background) {
38424 // render grid on panel activation (if panel background)
38425 ret.on('activate', function(gp) {
38426 if (!gp.grid.rendered) {
38427 // gp.grid.render(el);
38431 // cfg.grid.render(el);
38437 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38438 // it was the old xcomponent building that caused this before.
38439 // espeically if border is the top element in the tree.
38449 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38451 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38452 this.add(region, ret);
38456 throw "Can not add '" + cfg.xtype + "' to Border";
38462 this.beginUpdate();
38466 Roo.each(xitems, function(i) {
38467 region = nb && i.region ? i.region : false;
38469 var add = ret.addxtype(i);
38472 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38473 if (!i.background) {
38474 abn[region] = nb[region] ;
38481 // make the last non-background panel active..
38482 //if (nb) { Roo.log(abn); }
38485 for(var r in abn) {
38486 region = this.getRegion(r);
38488 // tried using nb[r], but it does not work..
38490 region.showPanel(abn[r]);
38501 factory : function(cfg)
38504 var validRegions = Roo.bootstrap.layout.Border.regions;
38506 var target = cfg.region;
38509 var r = Roo.bootstrap.layout;
38513 return new r.North(cfg);
38515 return new r.South(cfg);
38517 return new r.East(cfg);
38519 return new r.West(cfg);
38521 return new r.Center(cfg);
38523 throw 'Layout region "'+target+'" not supported.';
38530 * Ext JS Library 1.1.1
38531 * Copyright(c) 2006-2007, Ext JS, LLC.
38533 * Originally Released Under LGPL - original licence link has changed is not relivant.
38536 * <script type="text/javascript">
38540 * @class Roo.bootstrap.layout.Basic
38541 * @extends Roo.util.Observable
38542 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38543 * and does not have a titlebar, tabs or any other features. All it does is size and position
38544 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38545 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38546 * @cfg {string} region the region that it inhabits..
38547 * @cfg {bool} skipConfig skip config?
38551 Roo.bootstrap.layout.Basic = function(config){
38553 this.mgr = config.mgr;
38555 this.position = config.region;
38557 var skipConfig = config.skipConfig;
38561 * @scope Roo.BasicLayoutRegion
38565 * @event beforeremove
38566 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38567 * @param {Roo.LayoutRegion} this
38568 * @param {Roo.ContentPanel} panel The panel
38569 * @param {Object} e The cancel event object
38571 "beforeremove" : true,
38573 * @event invalidated
38574 * Fires when the layout for this region is changed.
38575 * @param {Roo.LayoutRegion} this
38577 "invalidated" : true,
38579 * @event visibilitychange
38580 * Fires when this region is shown or hidden
38581 * @param {Roo.LayoutRegion} this
38582 * @param {Boolean} visibility true or false
38584 "visibilitychange" : true,
38586 * @event paneladded
38587 * Fires when a panel is added.
38588 * @param {Roo.LayoutRegion} this
38589 * @param {Roo.ContentPanel} panel The panel
38591 "paneladded" : true,
38593 * @event panelremoved
38594 * Fires when a panel is removed.
38595 * @param {Roo.LayoutRegion} this
38596 * @param {Roo.ContentPanel} panel The panel
38598 "panelremoved" : true,
38600 * @event beforecollapse
38601 * Fires when this region before collapse.
38602 * @param {Roo.LayoutRegion} this
38604 "beforecollapse" : true,
38607 * Fires when this region is collapsed.
38608 * @param {Roo.LayoutRegion} this
38610 "collapsed" : true,
38613 * Fires when this region is expanded.
38614 * @param {Roo.LayoutRegion} this
38619 * Fires when this region is slid into view.
38620 * @param {Roo.LayoutRegion} this
38622 "slideshow" : true,
38625 * Fires when this region slides out of view.
38626 * @param {Roo.LayoutRegion} this
38628 "slidehide" : true,
38630 * @event panelactivated
38631 * Fires when a panel is activated.
38632 * @param {Roo.LayoutRegion} this
38633 * @param {Roo.ContentPanel} panel The activated panel
38635 "panelactivated" : true,
38638 * Fires when the user resizes this region.
38639 * @param {Roo.LayoutRegion} this
38640 * @param {Number} newSize The new size (width for east/west, height for north/south)
38644 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38645 this.panels = new Roo.util.MixedCollection();
38646 this.panels.getKey = this.getPanelId.createDelegate(this);
38648 this.activePanel = null;
38649 // ensure listeners are added...
38651 if (config.listeners || config.events) {
38652 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38653 listeners : config.listeners || {},
38654 events : config.events || {}
38658 if(skipConfig !== true){
38659 this.applyConfig(config);
38663 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38665 getPanelId : function(p){
38669 applyConfig : function(config){
38670 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38671 this.config = config;
38676 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38677 * the width, for horizontal (north, south) the height.
38678 * @param {Number} newSize The new width or height
38680 resizeTo : function(newSize){
38681 var el = this.el ? this.el :
38682 (this.activePanel ? this.activePanel.getEl() : null);
38684 switch(this.position){
38687 el.setWidth(newSize);
38688 this.fireEvent("resized", this, newSize);
38692 el.setHeight(newSize);
38693 this.fireEvent("resized", this, newSize);
38699 getBox : function(){
38700 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38703 getMargins : function(){
38704 return this.margins;
38707 updateBox : function(box){
38709 var el = this.activePanel.getEl();
38710 el.dom.style.left = box.x + "px";
38711 el.dom.style.top = box.y + "px";
38712 this.activePanel.setSize(box.width, box.height);
38716 * Returns the container element for this region.
38717 * @return {Roo.Element}
38719 getEl : function(){
38720 return this.activePanel;
38724 * Returns true if this region is currently visible.
38725 * @return {Boolean}
38727 isVisible : function(){
38728 return this.activePanel ? true : false;
38731 setActivePanel : function(panel){
38732 panel = this.getPanel(panel);
38733 if(this.activePanel && this.activePanel != panel){
38734 this.activePanel.setActiveState(false);
38735 this.activePanel.getEl().setLeftTop(-10000,-10000);
38737 this.activePanel = panel;
38738 panel.setActiveState(true);
38740 panel.setSize(this.box.width, this.box.height);
38742 this.fireEvent("panelactivated", this, panel);
38743 this.fireEvent("invalidated");
38747 * Show the specified panel.
38748 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38749 * @return {Roo.ContentPanel} The shown panel or null
38751 showPanel : function(panel){
38752 panel = this.getPanel(panel);
38754 this.setActivePanel(panel);
38760 * Get the active panel for this region.
38761 * @return {Roo.ContentPanel} The active panel or null
38763 getActivePanel : function(){
38764 return this.activePanel;
38768 * Add the passed ContentPanel(s)
38769 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38770 * @return {Roo.ContentPanel} The panel added (if only one was added)
38772 add : function(panel){
38773 if(arguments.length > 1){
38774 for(var i = 0, len = arguments.length; i < len; i++) {
38775 this.add(arguments[i]);
38779 if(this.hasPanel(panel)){
38780 this.showPanel(panel);
38783 var el = panel.getEl();
38784 if(el.dom.parentNode != this.mgr.el.dom){
38785 this.mgr.el.dom.appendChild(el.dom);
38787 if(panel.setRegion){
38788 panel.setRegion(this);
38790 this.panels.add(panel);
38791 el.setStyle("position", "absolute");
38792 if(!panel.background){
38793 this.setActivePanel(panel);
38794 if(this.config.initialSize && this.panels.getCount()==1){
38795 this.resizeTo(this.config.initialSize);
38798 this.fireEvent("paneladded", this, panel);
38803 * Returns true if the panel is in this region.
38804 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38805 * @return {Boolean}
38807 hasPanel : function(panel){
38808 if(typeof panel == "object"){ // must be panel obj
38809 panel = panel.getId();
38811 return this.getPanel(panel) ? true : false;
38815 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38816 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38817 * @param {Boolean} preservePanel Overrides the config preservePanel option
38818 * @return {Roo.ContentPanel} The panel that was removed
38820 remove : function(panel, preservePanel){
38821 panel = this.getPanel(panel);
38826 this.fireEvent("beforeremove", this, panel, e);
38827 if(e.cancel === true){
38830 var panelId = panel.getId();
38831 this.panels.removeKey(panelId);
38836 * Returns the panel specified or null if it's not in this region.
38837 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38838 * @return {Roo.ContentPanel}
38840 getPanel : function(id){
38841 if(typeof id == "object"){ // must be panel obj
38844 return this.panels.get(id);
38848 * Returns this regions position (north/south/east/west/center).
38851 getPosition: function(){
38852 return this.position;
38856 * Ext JS Library 1.1.1
38857 * Copyright(c) 2006-2007, Ext JS, LLC.
38859 * Originally Released Under LGPL - original licence link has changed is not relivant.
38862 * <script type="text/javascript">
38866 * @class Roo.bootstrap.layout.Region
38867 * @extends Roo.bootstrap.layout.Basic
38868 * This class represents a region in a layout manager.
38870 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38871 * @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})
38872 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38873 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38874 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38875 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38876 * @cfg {String} title The title for the region (overrides panel titles)
38877 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38878 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38879 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38880 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38881 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38882 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38883 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38884 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38885 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38886 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38888 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38889 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38890 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38891 * @cfg {Number} width For East/West panels
38892 * @cfg {Number} height For North/South panels
38893 * @cfg {Boolean} split To show the splitter
38894 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38896 * @cfg {string} cls Extra CSS classes to add to region
38898 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38899 * @cfg {string} region the region that it inhabits..
38902 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38903 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38905 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38906 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38907 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38909 Roo.bootstrap.layout.Region = function(config)
38911 this.applyConfig(config);
38913 var mgr = config.mgr;
38914 var pos = config.region;
38915 config.skipConfig = true;
38916 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38919 this.onRender(mgr.el);
38922 this.visible = true;
38923 this.collapsed = false;
38924 this.unrendered_panels = [];
38927 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38929 position: '', // set by wrapper (eg. north/south etc..)
38930 unrendered_panels : null, // unrendered panels.
38932 tabPosition : false,
38934 mgr: false, // points to 'Border'
38937 createBody : function(){
38938 /** This region's body element
38939 * @type Roo.Element */
38940 this.bodyEl = this.el.createChild({
38942 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38946 onRender: function(ctr, pos)
38948 var dh = Roo.DomHelper;
38949 /** This region's container element
38950 * @type Roo.Element */
38951 this.el = dh.append(ctr.dom, {
38953 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38955 /** This region's title element
38956 * @type Roo.Element */
38958 this.titleEl = dh.append(this.el.dom, {
38960 unselectable: "on",
38961 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38963 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38964 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38968 this.titleEl.enableDisplayMode();
38969 /** This region's title text element
38970 * @type HTMLElement */
38971 this.titleTextEl = this.titleEl.dom.firstChild;
38972 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38974 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38975 this.closeBtn.enableDisplayMode();
38976 this.closeBtn.on("click", this.closeClicked, this);
38977 this.closeBtn.hide();
38979 this.createBody(this.config);
38980 if(this.config.hideWhenEmpty){
38982 this.on("paneladded", this.validateVisibility, this);
38983 this.on("panelremoved", this.validateVisibility, this);
38985 if(this.autoScroll){
38986 this.bodyEl.setStyle("overflow", "auto");
38988 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38990 //if(c.titlebar !== false){
38991 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38992 this.titleEl.hide();
38994 this.titleEl.show();
38995 if(this.config.title){
38996 this.titleTextEl.innerHTML = this.config.title;
39000 if(this.config.collapsed){
39001 this.collapse(true);
39003 if(this.config.hidden){
39007 if (this.unrendered_panels && this.unrendered_panels.length) {
39008 for (var i =0;i< this.unrendered_panels.length; i++) {
39009 this.add(this.unrendered_panels[i]);
39011 this.unrendered_panels = null;
39017 applyConfig : function(c)
39020 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39021 var dh = Roo.DomHelper;
39022 if(c.titlebar !== false){
39023 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39024 this.collapseBtn.on("click", this.collapse, this);
39025 this.collapseBtn.enableDisplayMode();
39027 if(c.showPin === true || this.showPin){
39028 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39029 this.stickBtn.enableDisplayMode();
39030 this.stickBtn.on("click", this.expand, this);
39031 this.stickBtn.hide();
39036 /** This region's collapsed element
39037 * @type Roo.Element */
39040 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39041 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39044 if(c.floatable !== false){
39045 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39046 this.collapsedEl.on("click", this.collapseClick, this);
39049 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39050 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39051 id: "message", unselectable: "on", style:{"float":"left"}});
39052 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39054 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39055 this.expandBtn.on("click", this.expand, this);
39059 if(this.collapseBtn){
39060 this.collapseBtn.setVisible(c.collapsible == true);
39063 this.cmargins = c.cmargins || this.cmargins ||
39064 (this.position == "west" || this.position == "east" ?
39065 {top: 0, left: 2, right:2, bottom: 0} :
39066 {top: 2, left: 0, right:0, bottom: 2});
39068 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39071 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39073 this.autoScroll = c.autoScroll || false;
39078 this.duration = c.duration || .30;
39079 this.slideDuration = c.slideDuration || .45;
39084 * Returns true if this region is currently visible.
39085 * @return {Boolean}
39087 isVisible : function(){
39088 return this.visible;
39092 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39093 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39095 //setCollapsedTitle : function(title){
39096 // title = title || " ";
39097 // if(this.collapsedTitleTextEl){
39098 // this.collapsedTitleTextEl.innerHTML = title;
39102 getBox : function(){
39104 // if(!this.collapsed){
39105 b = this.el.getBox(false, true);
39107 // b = this.collapsedEl.getBox(false, true);
39112 getMargins : function(){
39113 return this.margins;
39114 //return this.collapsed ? this.cmargins : this.margins;
39117 highlight : function(){
39118 this.el.addClass("x-layout-panel-dragover");
39121 unhighlight : function(){
39122 this.el.removeClass("x-layout-panel-dragover");
39125 updateBox : function(box)
39127 if (!this.bodyEl) {
39128 return; // not rendered yet..
39132 if(!this.collapsed){
39133 this.el.dom.style.left = box.x + "px";
39134 this.el.dom.style.top = box.y + "px";
39135 this.updateBody(box.width, box.height);
39137 this.collapsedEl.dom.style.left = box.x + "px";
39138 this.collapsedEl.dom.style.top = box.y + "px";
39139 this.collapsedEl.setSize(box.width, box.height);
39142 this.tabs.autoSizeTabs();
39146 updateBody : function(w, h)
39149 this.el.setWidth(w);
39150 w -= this.el.getBorderWidth("rl");
39151 if(this.config.adjustments){
39152 w += this.config.adjustments[0];
39155 if(h !== null && h > 0){
39156 this.el.setHeight(h);
39157 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39158 h -= this.el.getBorderWidth("tb");
39159 if(this.config.adjustments){
39160 h += this.config.adjustments[1];
39162 this.bodyEl.setHeight(h);
39164 h = this.tabs.syncHeight(h);
39167 if(this.panelSize){
39168 w = w !== null ? w : this.panelSize.width;
39169 h = h !== null ? h : this.panelSize.height;
39171 if(this.activePanel){
39172 var el = this.activePanel.getEl();
39173 w = w !== null ? w : el.getWidth();
39174 h = h !== null ? h : el.getHeight();
39175 this.panelSize = {width: w, height: h};
39176 this.activePanel.setSize(w, h);
39178 if(Roo.isIE && this.tabs){
39179 this.tabs.el.repaint();
39184 * Returns the container element for this region.
39185 * @return {Roo.Element}
39187 getEl : function(){
39192 * Hides this region.
39195 //if(!this.collapsed){
39196 this.el.dom.style.left = "-2000px";
39199 // this.collapsedEl.dom.style.left = "-2000px";
39200 // this.collapsedEl.hide();
39202 this.visible = false;
39203 this.fireEvent("visibilitychange", this, false);
39207 * Shows this region if it was previously hidden.
39210 //if(!this.collapsed){
39213 // this.collapsedEl.show();
39215 this.visible = true;
39216 this.fireEvent("visibilitychange", this, true);
39219 closeClicked : function(){
39220 if(this.activePanel){
39221 this.remove(this.activePanel);
39225 collapseClick : function(e){
39227 e.stopPropagation();
39230 e.stopPropagation();
39236 * Collapses this region.
39237 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39240 collapse : function(skipAnim, skipCheck = false){
39241 if(this.collapsed) {
39245 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39247 this.collapsed = true;
39249 this.split.el.hide();
39251 if(this.config.animate && skipAnim !== true){
39252 this.fireEvent("invalidated", this);
39253 this.animateCollapse();
39255 this.el.setLocation(-20000,-20000);
39257 this.collapsedEl.show();
39258 this.fireEvent("collapsed", this);
39259 this.fireEvent("invalidated", this);
39265 animateCollapse : function(){
39270 * Expands this region if it was previously collapsed.
39271 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39272 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39275 expand : function(e, skipAnim){
39277 e.stopPropagation();
39279 if(!this.collapsed || this.el.hasActiveFx()) {
39283 this.afterSlideIn();
39286 this.collapsed = false;
39287 if(this.config.animate && skipAnim !== true){
39288 this.animateExpand();
39292 this.split.el.show();
39294 this.collapsedEl.setLocation(-2000,-2000);
39295 this.collapsedEl.hide();
39296 this.fireEvent("invalidated", this);
39297 this.fireEvent("expanded", this);
39301 animateExpand : function(){
39305 initTabs : function()
39307 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39309 var ts = new Roo.bootstrap.panel.Tabs({
39310 el: this.bodyEl.dom,
39312 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39313 disableTooltips: this.config.disableTabTips,
39314 toolbar : this.config.toolbar
39317 if(this.config.hideTabs){
39318 ts.stripWrap.setDisplayed(false);
39321 ts.resizeTabs = this.config.resizeTabs === true;
39322 ts.minTabWidth = this.config.minTabWidth || 40;
39323 ts.maxTabWidth = this.config.maxTabWidth || 250;
39324 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39325 ts.monitorResize = false;
39326 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39327 ts.bodyEl.addClass('roo-layout-tabs-body');
39328 this.panels.each(this.initPanelAsTab, this);
39331 initPanelAsTab : function(panel){
39332 var ti = this.tabs.addTab(
39336 this.config.closeOnTab && panel.isClosable(),
39339 if(panel.tabTip !== undefined){
39340 ti.setTooltip(panel.tabTip);
39342 ti.on("activate", function(){
39343 this.setActivePanel(panel);
39346 if(this.config.closeOnTab){
39347 ti.on("beforeclose", function(t, e){
39349 this.remove(panel);
39353 panel.tabItem = ti;
39358 updatePanelTitle : function(panel, title)
39360 if(this.activePanel == panel){
39361 this.updateTitle(title);
39364 var ti = this.tabs.getTab(panel.getEl().id);
39366 if(panel.tabTip !== undefined){
39367 ti.setTooltip(panel.tabTip);
39372 updateTitle : function(title){
39373 if(this.titleTextEl && !this.config.title){
39374 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39378 setActivePanel : function(panel)
39380 panel = this.getPanel(panel);
39381 if(this.activePanel && this.activePanel != panel){
39382 if(this.activePanel.setActiveState(false) === false){
39386 this.activePanel = panel;
39387 panel.setActiveState(true);
39388 if(this.panelSize){
39389 panel.setSize(this.panelSize.width, this.panelSize.height);
39392 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39394 this.updateTitle(panel.getTitle());
39396 this.fireEvent("invalidated", this);
39398 this.fireEvent("panelactivated", this, panel);
39402 * Shows the specified panel.
39403 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39404 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39406 showPanel : function(panel)
39408 panel = this.getPanel(panel);
39411 var tab = this.tabs.getTab(panel.getEl().id);
39412 if(tab.isHidden()){
39413 this.tabs.unhideTab(tab.id);
39417 this.setActivePanel(panel);
39424 * Get the active panel for this region.
39425 * @return {Roo.ContentPanel} The active panel or null
39427 getActivePanel : function(){
39428 return this.activePanel;
39431 validateVisibility : function(){
39432 if(this.panels.getCount() < 1){
39433 this.updateTitle(" ");
39434 this.closeBtn.hide();
39437 if(!this.isVisible()){
39444 * Adds the passed ContentPanel(s) to this region.
39445 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39446 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39448 add : function(panel)
39450 if(arguments.length > 1){
39451 for(var i = 0, len = arguments.length; i < len; i++) {
39452 this.add(arguments[i]);
39457 // if we have not been rendered yet, then we can not really do much of this..
39458 if (!this.bodyEl) {
39459 this.unrendered_panels.push(panel);
39466 if(this.hasPanel(panel)){
39467 this.showPanel(panel);
39470 panel.setRegion(this);
39471 this.panels.add(panel);
39472 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39473 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39474 // and hide them... ???
39475 this.bodyEl.dom.appendChild(panel.getEl().dom);
39476 if(panel.background !== true){
39477 this.setActivePanel(panel);
39479 this.fireEvent("paneladded", this, panel);
39486 this.initPanelAsTab(panel);
39490 if(panel.background !== true){
39491 this.tabs.activate(panel.getEl().id);
39493 this.fireEvent("paneladded", this, panel);
39498 * Hides the tab for the specified panel.
39499 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39501 hidePanel : function(panel){
39502 if(this.tabs && (panel = this.getPanel(panel))){
39503 this.tabs.hideTab(panel.getEl().id);
39508 * Unhides the tab for a previously hidden panel.
39509 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39511 unhidePanel : function(panel){
39512 if(this.tabs && (panel = this.getPanel(panel))){
39513 this.tabs.unhideTab(panel.getEl().id);
39517 clearPanels : function(){
39518 while(this.panels.getCount() > 0){
39519 this.remove(this.panels.first());
39524 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39525 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39526 * @param {Boolean} preservePanel Overrides the config preservePanel option
39527 * @return {Roo.ContentPanel} The panel that was removed
39529 remove : function(panel, preservePanel)
39531 panel = this.getPanel(panel);
39536 this.fireEvent("beforeremove", this, panel, e);
39537 if(e.cancel === true){
39540 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39541 var panelId = panel.getId();
39542 this.panels.removeKey(panelId);
39544 document.body.appendChild(panel.getEl().dom);
39547 this.tabs.removeTab(panel.getEl().id);
39548 }else if (!preservePanel){
39549 this.bodyEl.dom.removeChild(panel.getEl().dom);
39551 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39552 var p = this.panels.first();
39553 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39554 tempEl.appendChild(p.getEl().dom);
39555 this.bodyEl.update("");
39556 this.bodyEl.dom.appendChild(p.getEl().dom);
39558 this.updateTitle(p.getTitle());
39560 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39561 this.setActivePanel(p);
39563 panel.setRegion(null);
39564 if(this.activePanel == panel){
39565 this.activePanel = null;
39567 if(this.config.autoDestroy !== false && preservePanel !== true){
39568 try{panel.destroy();}catch(e){}
39570 this.fireEvent("panelremoved", this, panel);
39575 * Returns the TabPanel component used by this region
39576 * @return {Roo.TabPanel}
39578 getTabs : function(){
39582 createTool : function(parentEl, className){
39583 var btn = Roo.DomHelper.append(parentEl, {
39585 cls: "x-layout-tools-button",
39588 cls: "roo-layout-tools-button-inner " + className,
39592 btn.addClassOnOver("roo-layout-tools-button-over");
39597 * Ext JS Library 1.1.1
39598 * Copyright(c) 2006-2007, Ext JS, LLC.
39600 * Originally Released Under LGPL - original licence link has changed is not relivant.
39603 * <script type="text/javascript">
39609 * @class Roo.SplitLayoutRegion
39610 * @extends Roo.LayoutRegion
39611 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39613 Roo.bootstrap.layout.Split = function(config){
39614 this.cursor = config.cursor;
39615 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39618 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39620 splitTip : "Drag to resize.",
39621 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39622 useSplitTips : false,
39624 applyConfig : function(config){
39625 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39628 onRender : function(ctr,pos) {
39630 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39631 if(!this.config.split){
39636 var splitEl = Roo.DomHelper.append(ctr.dom, {
39638 id: this.el.id + "-split",
39639 cls: "roo-layout-split roo-layout-split-"+this.position,
39642 /** The SplitBar for this region
39643 * @type Roo.SplitBar */
39644 // does not exist yet...
39645 Roo.log([this.position, this.orientation]);
39647 this.split = new Roo.bootstrap.SplitBar({
39648 dragElement : splitEl,
39649 resizingElement: this.el,
39650 orientation : this.orientation
39653 this.split.on("moved", this.onSplitMove, this);
39654 this.split.useShim = this.config.useShim === true;
39655 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39656 if(this.useSplitTips){
39657 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39659 //if(config.collapsible){
39660 // this.split.el.on("dblclick", this.collapse, this);
39663 if(typeof this.config.minSize != "undefined"){
39664 this.split.minSize = this.config.minSize;
39666 if(typeof this.config.maxSize != "undefined"){
39667 this.split.maxSize = this.config.maxSize;
39669 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39670 this.hideSplitter();
39675 getHMaxSize : function(){
39676 var cmax = this.config.maxSize || 10000;
39677 var center = this.mgr.getRegion("center");
39678 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39681 getVMaxSize : function(){
39682 var cmax = this.config.maxSize || 10000;
39683 var center = this.mgr.getRegion("center");
39684 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39687 onSplitMove : function(split, newSize){
39688 this.fireEvent("resized", this, newSize);
39692 * Returns the {@link Roo.SplitBar} for this region.
39693 * @return {Roo.SplitBar}
39695 getSplitBar : function(){
39700 this.hideSplitter();
39701 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39704 hideSplitter : function(){
39706 this.split.el.setLocation(-2000,-2000);
39707 this.split.el.hide();
39713 this.split.el.show();
39715 Roo.bootstrap.layout.Split.superclass.show.call(this);
39718 beforeSlide: function(){
39719 if(Roo.isGecko){// firefox overflow auto bug workaround
39720 this.bodyEl.clip();
39722 this.tabs.bodyEl.clip();
39724 if(this.activePanel){
39725 this.activePanel.getEl().clip();
39727 if(this.activePanel.beforeSlide){
39728 this.activePanel.beforeSlide();
39734 afterSlide : function(){
39735 if(Roo.isGecko){// firefox overflow auto bug workaround
39736 this.bodyEl.unclip();
39738 this.tabs.bodyEl.unclip();
39740 if(this.activePanel){
39741 this.activePanel.getEl().unclip();
39742 if(this.activePanel.afterSlide){
39743 this.activePanel.afterSlide();
39749 initAutoHide : function(){
39750 if(this.autoHide !== false){
39751 if(!this.autoHideHd){
39752 var st = new Roo.util.DelayedTask(this.slideIn, this);
39753 this.autoHideHd = {
39754 "mouseout": function(e){
39755 if(!e.within(this.el, true)){
39759 "mouseover" : function(e){
39765 this.el.on(this.autoHideHd);
39769 clearAutoHide : function(){
39770 if(this.autoHide !== false){
39771 this.el.un("mouseout", this.autoHideHd.mouseout);
39772 this.el.un("mouseover", this.autoHideHd.mouseover);
39776 clearMonitor : function(){
39777 Roo.get(document).un("click", this.slideInIf, this);
39780 // these names are backwards but not changed for compat
39781 slideOut : function(){
39782 if(this.isSlid || this.el.hasActiveFx()){
39785 this.isSlid = true;
39786 if(this.collapseBtn){
39787 this.collapseBtn.hide();
39789 this.closeBtnState = this.closeBtn.getStyle('display');
39790 this.closeBtn.hide();
39792 this.stickBtn.show();
39795 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39796 this.beforeSlide();
39797 this.el.setStyle("z-index", 10001);
39798 this.el.slideIn(this.getSlideAnchor(), {
39799 callback: function(){
39801 this.initAutoHide();
39802 Roo.get(document).on("click", this.slideInIf, this);
39803 this.fireEvent("slideshow", this);
39810 afterSlideIn : function(){
39811 this.clearAutoHide();
39812 this.isSlid = false;
39813 this.clearMonitor();
39814 this.el.setStyle("z-index", "");
39815 if(this.collapseBtn){
39816 this.collapseBtn.show();
39818 this.closeBtn.setStyle('display', this.closeBtnState);
39820 this.stickBtn.hide();
39822 this.fireEvent("slidehide", this);
39825 slideIn : function(cb){
39826 if(!this.isSlid || this.el.hasActiveFx()){
39830 this.isSlid = false;
39831 this.beforeSlide();
39832 this.el.slideOut(this.getSlideAnchor(), {
39833 callback: function(){
39834 this.el.setLeftTop(-10000, -10000);
39836 this.afterSlideIn();
39844 slideInIf : function(e){
39845 if(!e.within(this.el)){
39850 animateCollapse : function(){
39851 this.beforeSlide();
39852 this.el.setStyle("z-index", 20000);
39853 var anchor = this.getSlideAnchor();
39854 this.el.slideOut(anchor, {
39855 callback : function(){
39856 this.el.setStyle("z-index", "");
39857 this.collapsedEl.slideIn(anchor, {duration:.3});
39859 this.el.setLocation(-10000,-10000);
39861 this.fireEvent("collapsed", this);
39868 animateExpand : function(){
39869 this.beforeSlide();
39870 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39871 this.el.setStyle("z-index", 20000);
39872 this.collapsedEl.hide({
39875 this.el.slideIn(this.getSlideAnchor(), {
39876 callback : function(){
39877 this.el.setStyle("z-index", "");
39880 this.split.el.show();
39882 this.fireEvent("invalidated", this);
39883 this.fireEvent("expanded", this);
39911 getAnchor : function(){
39912 return this.anchors[this.position];
39915 getCollapseAnchor : function(){
39916 return this.canchors[this.position];
39919 getSlideAnchor : function(){
39920 return this.sanchors[this.position];
39923 getAlignAdj : function(){
39924 var cm = this.cmargins;
39925 switch(this.position){
39941 getExpandAdj : function(){
39942 var c = this.collapsedEl, cm = this.cmargins;
39943 switch(this.position){
39945 return [-(cm.right+c.getWidth()+cm.left), 0];
39948 return [cm.right+c.getWidth()+cm.left, 0];
39951 return [0, -(cm.top+cm.bottom+c.getHeight())];
39954 return [0, cm.top+cm.bottom+c.getHeight()];
39960 * Ext JS Library 1.1.1
39961 * Copyright(c) 2006-2007, Ext JS, LLC.
39963 * Originally Released Under LGPL - original licence link has changed is not relivant.
39966 * <script type="text/javascript">
39969 * These classes are private internal classes
39971 Roo.bootstrap.layout.Center = function(config){
39972 config.region = "center";
39973 Roo.bootstrap.layout.Region.call(this, config);
39974 this.visible = true;
39975 this.minWidth = config.minWidth || 20;
39976 this.minHeight = config.minHeight || 20;
39979 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39981 // center panel can't be hidden
39985 // center panel can't be hidden
39988 getMinWidth: function(){
39989 return this.minWidth;
39992 getMinHeight: function(){
39993 return this.minHeight;
40007 Roo.bootstrap.layout.North = function(config)
40009 config.region = 'north';
40010 config.cursor = 'n-resize';
40012 Roo.bootstrap.layout.Split.call(this, config);
40016 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40017 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40018 this.split.el.addClass("roo-layout-split-v");
40020 //var size = config.initialSize || config.height;
40021 //if(this.el && typeof size != "undefined"){
40022 // this.el.setHeight(size);
40025 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40027 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40030 onRender : function(ctr, pos)
40032 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40033 var size = this.config.initialSize || this.config.height;
40034 if(this.el && typeof size != "undefined"){
40035 this.el.setHeight(size);
40040 getBox : function(){
40041 if(this.collapsed){
40042 return this.collapsedEl.getBox();
40044 var box = this.el.getBox();
40046 box.height += this.split.el.getHeight();
40051 updateBox : function(box){
40052 if(this.split && !this.collapsed){
40053 box.height -= this.split.el.getHeight();
40054 this.split.el.setLeft(box.x);
40055 this.split.el.setTop(box.y+box.height);
40056 this.split.el.setWidth(box.width);
40058 if(this.collapsed){
40059 this.updateBody(box.width, null);
40061 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40069 Roo.bootstrap.layout.South = function(config){
40070 config.region = 'south';
40071 config.cursor = 's-resize';
40072 Roo.bootstrap.layout.Split.call(this, config);
40074 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40075 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40076 this.split.el.addClass("roo-layout-split-v");
40081 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40082 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40084 onRender : function(ctr, pos)
40086 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40087 var size = this.config.initialSize || this.config.height;
40088 if(this.el && typeof size != "undefined"){
40089 this.el.setHeight(size);
40094 getBox : function(){
40095 if(this.collapsed){
40096 return this.collapsedEl.getBox();
40098 var box = this.el.getBox();
40100 var sh = this.split.el.getHeight();
40107 updateBox : function(box){
40108 if(this.split && !this.collapsed){
40109 var sh = this.split.el.getHeight();
40112 this.split.el.setLeft(box.x);
40113 this.split.el.setTop(box.y-sh);
40114 this.split.el.setWidth(box.width);
40116 if(this.collapsed){
40117 this.updateBody(box.width, null);
40119 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40123 Roo.bootstrap.layout.East = function(config){
40124 config.region = "east";
40125 config.cursor = "e-resize";
40126 Roo.bootstrap.layout.Split.call(this, config);
40128 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40129 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40130 this.split.el.addClass("roo-layout-split-h");
40134 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40135 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40137 onRender : function(ctr, pos)
40139 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40140 var size = this.config.initialSize || this.config.width;
40141 if(this.el && typeof size != "undefined"){
40142 this.el.setWidth(size);
40147 getBox : function(){
40148 if(this.collapsed){
40149 return this.collapsedEl.getBox();
40151 var box = this.el.getBox();
40153 var sw = this.split.el.getWidth();
40160 updateBox : function(box){
40161 if(this.split && !this.collapsed){
40162 var sw = this.split.el.getWidth();
40164 this.split.el.setLeft(box.x);
40165 this.split.el.setTop(box.y);
40166 this.split.el.setHeight(box.height);
40169 if(this.collapsed){
40170 this.updateBody(null, box.height);
40172 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40176 Roo.bootstrap.layout.West = function(config){
40177 config.region = "west";
40178 config.cursor = "w-resize";
40180 Roo.bootstrap.layout.Split.call(this, config);
40182 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40183 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40184 this.split.el.addClass("roo-layout-split-h");
40188 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40189 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40191 onRender: function(ctr, pos)
40193 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40194 var size = this.config.initialSize || this.config.width;
40195 if(typeof size != "undefined"){
40196 this.el.setWidth(size);
40200 getBox : function(){
40201 if(this.collapsed){
40202 return this.collapsedEl.getBox();
40204 var box = this.el.getBox();
40205 if (box.width == 0) {
40206 box.width = this.config.width; // kludge?
40209 box.width += this.split.el.getWidth();
40214 updateBox : function(box){
40215 if(this.split && !this.collapsed){
40216 var sw = this.split.el.getWidth();
40218 this.split.el.setLeft(box.x+box.width);
40219 this.split.el.setTop(box.y);
40220 this.split.el.setHeight(box.height);
40222 if(this.collapsed){
40223 this.updateBody(null, box.height);
40225 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40229 * Ext JS Library 1.1.1
40230 * Copyright(c) 2006-2007, Ext JS, LLC.
40232 * Originally Released Under LGPL - original licence link has changed is not relivant.
40235 * <script type="text/javascript">
40238 * @class Roo.bootstrap.paenl.Content
40239 * @extends Roo.util.Observable
40240 * @children Roo.bootstrap.Component
40241 * @parent builder Roo.bootstrap.layout.Border
40242 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40243 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40244 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40245 * @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
40246 * @cfg {Boolean} closable True if the panel can be closed/removed
40247 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40248 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40249 * @cfg {Toolbar} toolbar A toolbar for this panel
40250 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40251 * @cfg {String} title The title for this panel
40252 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40253 * @cfg {String} url Calls {@link #setUrl} with this value
40254 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40255 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40256 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40257 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40258 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40259 * @cfg {Boolean} badges render the badges
40260 * @cfg {String} cls extra classes to use
40261 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40264 * Create a new ContentPanel.
40265 * @param {String/Object} config A string to set only the title or a config object
40268 Roo.bootstrap.panel.Content = function( config){
40270 this.tpl = config.tpl || false;
40272 var el = config.el;
40273 var content = config.content;
40275 if(config.autoCreate){ // xtype is available if this is called from factory
40278 this.el = Roo.get(el);
40279 if(!this.el && config && config.autoCreate){
40280 if(typeof config.autoCreate == "object"){
40281 if(!config.autoCreate.id){
40282 config.autoCreate.id = config.id||el;
40284 this.el = Roo.DomHelper.append(document.body,
40285 config.autoCreate, true);
40289 cls: (config.cls || '') +
40290 (config.background ? ' bg-' + config.background : '') +
40291 " roo-layout-inactive-content",
40294 if (config.iframe) {
40298 style : 'border: 0px',
40299 src : 'about:blank'
40305 elcfg.html = config.html;
40309 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40310 if (config.iframe) {
40311 this.iframeEl = this.el.select('iframe',true).first();
40316 this.closable = false;
40317 this.loaded = false;
40318 this.active = false;
40321 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40323 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40325 this.wrapEl = this.el; //this.el.wrap();
40327 if (config.toolbar.items) {
40328 ti = config.toolbar.items ;
40329 delete config.toolbar.items ;
40333 this.toolbar.render(this.wrapEl, 'before');
40334 for(var i =0;i < ti.length;i++) {
40335 // Roo.log(['add child', items[i]]);
40336 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40338 this.toolbar.items = nitems;
40339 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40340 delete config.toolbar;
40344 // xtype created footer. - not sure if will work as we normally have to render first..
40345 if (this.footer && !this.footer.el && this.footer.xtype) {
40346 if (!this.wrapEl) {
40347 this.wrapEl = this.el.wrap();
40350 this.footer.container = this.wrapEl.createChild();
40352 this.footer = Roo.factory(this.footer, Roo);
40357 if(typeof config == "string"){
40358 this.title = config;
40360 Roo.apply(this, config);
40364 this.resizeEl = Roo.get(this.resizeEl, true);
40366 this.resizeEl = this.el;
40368 // handle view.xtype
40376 * Fires when this panel is activated.
40377 * @param {Roo.ContentPanel} this
40381 * @event deactivate
40382 * Fires when this panel is activated.
40383 * @param {Roo.ContentPanel} this
40385 "deactivate" : true,
40389 * Fires when this panel is resized if fitToFrame is true.
40390 * @param {Roo.ContentPanel} this
40391 * @param {Number} width The width after any component adjustments
40392 * @param {Number} height The height after any component adjustments
40398 * Fires when this tab is created
40399 * @param {Roo.ContentPanel} this
40405 * Fires when this content is scrolled
40406 * @param {Roo.ContentPanel} this
40407 * @param {Event} scrollEvent
40418 if(this.autoScroll && !this.iframe){
40419 this.resizeEl.setStyle("overflow", "auto");
40420 this.resizeEl.on('scroll', this.onScroll, this);
40422 // fix randome scrolling
40423 //this.el.on('scroll', function() {
40424 // Roo.log('fix random scolling');
40425 // this.scrollTo('top',0);
40428 content = content || this.content;
40430 this.setContent(content);
40432 if(config && config.url){
40433 this.setUrl(this.url, this.params, this.loadOnce);
40438 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40440 if (this.view && typeof(this.view.xtype) != 'undefined') {
40441 this.view.el = this.el.appendChild(document.createElement("div"));
40442 this.view = Roo.factory(this.view);
40443 this.view.render && this.view.render(false, '');
40447 this.fireEvent('render', this);
40450 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40460 /* Resize Element - use this to work out scroll etc. */
40463 setRegion : function(region){
40464 this.region = region;
40465 this.setActiveClass(region && !this.background);
40469 setActiveClass: function(state)
40472 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40473 this.el.setStyle('position','relative');
40475 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40476 this.el.setStyle('position', 'absolute');
40481 * Returns the toolbar for this Panel if one was configured.
40482 * @return {Roo.Toolbar}
40484 getToolbar : function(){
40485 return this.toolbar;
40488 setActiveState : function(active)
40490 this.active = active;
40491 this.setActiveClass(active);
40493 if(this.fireEvent("deactivate", this) === false){
40498 this.fireEvent("activate", this);
40502 * Updates this panel's element (not for iframe)
40503 * @param {String} content The new content
40504 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40506 setContent : function(content, loadScripts){
40511 this.el.update(content, loadScripts);
40514 ignoreResize : function(w, h)
40516 return false; // always resize?
40517 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40520 this.lastSize = {width: w, height: h};
40525 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40526 * @return {Roo.UpdateManager} The UpdateManager
40528 getUpdateManager : function(){
40532 return this.el.getUpdateManager();
40535 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40536 * Does not work with IFRAME contents
40537 * @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:
40540 url: "your-url.php",
40541 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40542 callback: yourFunction,
40543 scope: yourObject, //(optional scope)
40546 text: "Loading...",
40552 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40553 * 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.
40554 * @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}
40555 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40556 * @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.
40557 * @return {Roo.ContentPanel} this
40565 var um = this.el.getUpdateManager();
40566 um.update.apply(um, arguments);
40572 * 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.
40573 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40574 * @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)
40575 * @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)
40576 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40578 setUrl : function(url, params, loadOnce){
40580 this.iframeEl.dom.src = url;
40584 if(this.refreshDelegate){
40585 this.removeListener("activate", this.refreshDelegate);
40587 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40588 this.on("activate", this.refreshDelegate);
40589 return this.el.getUpdateManager();
40592 _handleRefresh : function(url, params, loadOnce){
40593 if(!loadOnce || !this.loaded){
40594 var updater = this.el.getUpdateManager();
40595 updater.update(url, params, this._setLoaded.createDelegate(this));
40599 _setLoaded : function(){
40600 this.loaded = true;
40604 * Returns this panel's id
40607 getId : function(){
40612 * Returns this panel's element - used by regiosn to add.
40613 * @return {Roo.Element}
40615 getEl : function(){
40616 return this.wrapEl || this.el;
40621 adjustForComponents : function(width, height)
40623 //Roo.log('adjustForComponents ');
40624 if(this.resizeEl != this.el){
40625 width -= this.el.getFrameWidth('lr');
40626 height -= this.el.getFrameWidth('tb');
40629 var te = this.toolbar.getEl();
40630 te.setWidth(width);
40631 height -= te.getHeight();
40634 var te = this.footer.getEl();
40635 te.setWidth(width);
40636 height -= te.getHeight();
40640 if(this.adjustments){
40641 width += this.adjustments[0];
40642 height += this.adjustments[1];
40644 return {"width": width, "height": height};
40647 setSize : function(width, height){
40648 if(this.fitToFrame && !this.ignoreResize(width, height)){
40649 if(this.fitContainer && this.resizeEl != this.el){
40650 this.el.setSize(width, height);
40652 var size = this.adjustForComponents(width, height);
40654 this.iframeEl.setSize(width,height);
40657 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40658 this.fireEvent('resize', this, size.width, size.height);
40665 * Returns this panel's title
40668 getTitle : function(){
40670 if (typeof(this.title) != 'object') {
40675 for (var k in this.title) {
40676 if (!this.title.hasOwnProperty(k)) {
40680 if (k.indexOf('-') >= 0) {
40681 var s = k.split('-');
40682 for (var i = 0; i<s.length; i++) {
40683 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40686 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40693 * Set this panel's title
40694 * @param {String} title
40696 setTitle : function(title){
40697 this.title = title;
40699 this.region.updatePanelTitle(this, title);
40704 * Returns true is this panel was configured to be closable
40705 * @return {Boolean}
40707 isClosable : function(){
40708 return this.closable;
40711 beforeSlide : function(){
40713 this.resizeEl.clip();
40716 afterSlide : function(){
40718 this.resizeEl.unclip();
40722 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40723 * Will fail silently if the {@link #setUrl} method has not been called.
40724 * This does not activate the panel, just updates its content.
40726 refresh : function(){
40727 if(this.refreshDelegate){
40728 this.loaded = false;
40729 this.refreshDelegate();
40734 * Destroys this panel
40736 destroy : function(){
40737 this.el.removeAllListeners();
40738 var tempEl = document.createElement("span");
40739 tempEl.appendChild(this.el.dom);
40740 tempEl.innerHTML = "";
40746 * form - if the content panel contains a form - this is a reference to it.
40747 * @type {Roo.form.Form}
40751 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40752 * This contains a reference to it.
40758 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40768 * @param {Object} cfg Xtype definition of item to add.
40772 getChildContainer: function () {
40773 return this.getEl();
40777 onScroll : function(e)
40779 this.fireEvent('scroll', this, e);
40784 var ret = new Roo.factory(cfg);
40789 if (cfg.xtype.match(/^Form$/)) {
40792 //if (this.footer) {
40793 // el = this.footer.container.insertSibling(false, 'before');
40795 el = this.el.createChild();
40798 this.form = new Roo.form.Form(cfg);
40801 if ( this.form.allItems.length) {
40802 this.form.render(el.dom);
40806 // should only have one of theses..
40807 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40808 // views.. should not be just added - used named prop 'view''
40810 cfg.el = this.el.appendChild(document.createElement("div"));
40813 var ret = new Roo.factory(cfg);
40815 ret.render && ret.render(false, ''); // render blank..
40825 * @class Roo.bootstrap.panel.Grid
40826 * @extends Roo.bootstrap.panel.Content
40828 * Create a new GridPanel.
40829 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40830 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40831 * @param {Object} config A the config object
40837 Roo.bootstrap.panel.Grid = function(config)
40841 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40842 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40844 config.el = this.wrapper;
40845 //this.el = this.wrapper;
40847 if (config.container) {
40848 // ctor'ed from a Border/panel.grid
40851 this.wrapper.setStyle("overflow", "hidden");
40852 this.wrapper.addClass('roo-grid-container');
40857 if(config.toolbar){
40858 var tool_el = this.wrapper.createChild();
40859 this.toolbar = Roo.factory(config.toolbar);
40861 if (config.toolbar.items) {
40862 ti = config.toolbar.items ;
40863 delete config.toolbar.items ;
40867 this.toolbar.render(tool_el);
40868 for(var i =0;i < ti.length;i++) {
40869 // Roo.log(['add child', items[i]]);
40870 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40872 this.toolbar.items = nitems;
40874 delete config.toolbar;
40877 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40878 config.grid.scrollBody = true;;
40879 config.grid.monitorWindowResize = false; // turn off autosizing
40880 config.grid.autoHeight = false;
40881 config.grid.autoWidth = false;
40883 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40885 if (config.background) {
40886 // render grid on panel activation (if panel background)
40887 this.on('activate', function(gp) {
40888 if (!gp.grid.rendered) {
40889 gp.grid.render(this.wrapper);
40890 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40895 this.grid.render(this.wrapper);
40896 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40899 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40900 // ??? needed ??? config.el = this.wrapper;
40905 // xtype created footer. - not sure if will work as we normally have to render first..
40906 if (this.footer && !this.footer.el && this.footer.xtype) {
40908 var ctr = this.grid.getView().getFooterPanel(true);
40909 this.footer.dataSource = this.grid.dataSource;
40910 this.footer = Roo.factory(this.footer, Roo);
40911 this.footer.render(ctr);
40921 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40924 is_resizing : false,
40926 getId : function(){
40927 return this.grid.id;
40931 * Returns the grid for this panel
40932 * @return {Roo.bootstrap.Table}
40934 getGrid : function(){
40938 setSize : function(width, height)
40940 if (this.is_resizing) {
40944 this.is_resizing = true;
40945 if(!this.ignoreResize(width, height)){
40946 var grid = this.grid;
40947 var size = this.adjustForComponents(width, height);
40948 // tfoot is not a footer?
40951 var gridel = grid.getGridEl();
40952 gridel.setSize(size.width, size.height);
40954 var tbd = grid.getGridEl().select('tbody', true).first();
40955 var thd = grid.getGridEl().select('thead',true).first();
40956 var tbf= grid.getGridEl().select('tfoot', true).first();
40959 size.height -= tbf.getHeight();
40962 size.height -= thd.getHeight();
40965 tbd.setSize(size.width, size.height );
40966 // this is for the account management tab -seems to work there.
40967 var thd = grid.getGridEl().select('thead',true).first();
40969 // tbd.setSize(size.width, size.height - thd.getHeight());
40974 this.is_resizing = false;
40979 beforeSlide : function(){
40980 this.grid.getView().scroller.clip();
40983 afterSlide : function(){
40984 this.grid.getView().scroller.unclip();
40987 destroy : function(){
40988 this.grid.destroy();
40990 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40995 * @class Roo.bootstrap.panel.Nest
40996 * @extends Roo.bootstrap.panel.Content
40998 * Create a new Panel, that can contain a layout.Border.
41001 * @param {String/Object} config A string to set only the title or a config object
41003 Roo.bootstrap.panel.Nest = function(config)
41005 // construct with only one argument..
41006 /* FIXME - implement nicer consturctors
41007 if (layout.layout) {
41009 layout = config.layout;
41010 delete config.layout;
41012 if (layout.xtype && !layout.getEl) {
41013 // then layout needs constructing..
41014 layout = Roo.factory(layout, Roo);
41018 config.el = config.layout.getEl();
41020 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41022 config.layout.monitorWindowResize = false; // turn off autosizing
41023 this.layout = config.layout;
41024 this.layout.getEl().addClass("roo-layout-nested-layout");
41025 this.layout.parent = this;
41032 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41034 * @cfg {Roo.BorderLayout} layout The layout for this panel
41038 setSize : function(width, height){
41039 if(!this.ignoreResize(width, height)){
41040 var size = this.adjustForComponents(width, height);
41041 var el = this.layout.getEl();
41042 if (size.height < 1) {
41043 el.setWidth(size.width);
41045 el.setSize(size.width, size.height);
41047 var touch = el.dom.offsetWidth;
41048 this.layout.layout();
41049 // ie requires a double layout on the first pass
41050 if(Roo.isIE && !this.initialized){
41051 this.initialized = true;
41052 this.layout.layout();
41057 // activate all subpanels if not currently active..
41059 setActiveState : function(active){
41060 this.active = active;
41061 this.setActiveClass(active);
41064 this.fireEvent("deactivate", this);
41068 this.fireEvent("activate", this);
41069 // not sure if this should happen before or after..
41070 if (!this.layout) {
41071 return; // should not happen..
41074 for (var r in this.layout.regions) {
41075 reg = this.layout.getRegion(r);
41076 if (reg.getActivePanel()) {
41077 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41078 reg.setActivePanel(reg.getActivePanel());
41081 if (!reg.panels.length) {
41084 reg.showPanel(reg.getPanel(0));
41093 * Returns the nested BorderLayout for this panel
41094 * @return {Roo.BorderLayout}
41096 getLayout : function(){
41097 return this.layout;
41101 * Adds a xtype elements to the layout of the nested panel
41105 xtype : 'ContentPanel',
41112 xtype : 'NestedLayoutPanel',
41118 items : [ ... list of content panels or nested layout panels.. ]
41122 * @param {Object} cfg Xtype definition of item to add.
41124 addxtype : function(cfg) {
41125 return this.layout.addxtype(cfg);
41130 * Ext JS Library 1.1.1
41131 * Copyright(c) 2006-2007, Ext JS, LLC.
41133 * Originally Released Under LGPL - original licence link has changed is not relivant.
41136 * <script type="text/javascript">
41139 * @class Roo.TabPanel
41140 * @extends Roo.util.Observable
41141 * A lightweight tab container.
41145 // basic tabs 1, built from existing content
41146 var tabs = new Roo.TabPanel("tabs1");
41147 tabs.addTab("script", "View Script");
41148 tabs.addTab("markup", "View Markup");
41149 tabs.activate("script");
41151 // more advanced tabs, built from javascript
41152 var jtabs = new Roo.TabPanel("jtabs");
41153 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41155 // set up the UpdateManager
41156 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41157 var updater = tab2.getUpdateManager();
41158 updater.setDefaultUrl("ajax1.htm");
41159 tab2.on('activate', updater.refresh, updater, true);
41161 // Use setUrl for Ajax loading
41162 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41163 tab3.setUrl("ajax2.htm", null, true);
41166 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41169 jtabs.activate("jtabs-1");
41172 * Create a new TabPanel.
41173 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41174 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41176 Roo.bootstrap.panel.Tabs = function(config){
41178 * The container element for this TabPanel.
41179 * @type Roo.Element
41181 this.el = Roo.get(config.el);
41184 if(typeof config == "boolean"){
41185 this.tabPosition = config ? "bottom" : "top";
41187 Roo.apply(this, config);
41191 if(this.tabPosition == "bottom"){
41192 // if tabs are at the bottom = create the body first.
41193 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41194 this.el.addClass("roo-tabs-bottom");
41196 // next create the tabs holders
41198 if (this.tabPosition == "west"){
41200 var reg = this.region; // fake it..
41202 if (!reg.mgr.parent) {
41205 reg = reg.mgr.parent.region;
41207 Roo.log("got nest?");
41209 if (reg.mgr.getRegion('west')) {
41210 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41211 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41212 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41213 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41214 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41222 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41223 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41224 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41225 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41230 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41233 // finally - if tabs are at the top, then create the body last..
41234 if(this.tabPosition != "bottom"){
41235 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41236 * @type Roo.Element
41238 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41239 this.el.addClass("roo-tabs-top");
41243 this.bodyEl.setStyle("position", "relative");
41245 this.active = null;
41246 this.activateDelegate = this.activate.createDelegate(this);
41251 * Fires when the active tab changes
41252 * @param {Roo.TabPanel} this
41253 * @param {Roo.TabPanelItem} activePanel The new active tab
41257 * @event beforetabchange
41258 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41259 * @param {Roo.TabPanel} this
41260 * @param {Object} e Set cancel to true on this object to cancel the tab change
41261 * @param {Roo.TabPanelItem} tab The tab being changed to
41263 "beforetabchange" : true
41266 Roo.EventManager.onWindowResize(this.onResize, this);
41267 this.cpad = this.el.getPadding("lr");
41268 this.hiddenCount = 0;
41271 // toolbar on the tabbar support...
41272 if (this.toolbar) {
41273 alert("no toolbar support yet");
41274 this.toolbar = false;
41276 var tcfg = this.toolbar;
41277 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41278 this.toolbar = new Roo.Toolbar(tcfg);
41279 if (Roo.isSafari) {
41280 var tbl = tcfg.container.child('table', true);
41281 tbl.setAttribute('width', '100%');
41289 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41292 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41294 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41296 tabPosition : "top",
41298 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41300 currentTabWidth : 0,
41302 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41306 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41310 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41312 preferredTabWidth : 175,
41314 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41316 resizeTabs : false,
41318 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41320 monitorResize : true,
41322 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41324 toolbar : false, // set by caller..
41326 region : false, /// set by caller
41328 disableTooltips : true, // not used yet...
41331 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41332 * @param {String} id The id of the div to use <b>or create</b>
41333 * @param {String} text The text for the tab
41334 * @param {String} content (optional) Content to put in the TabPanelItem body
41335 * @param {Boolean} closable (optional) True to create a close icon on the tab
41336 * @return {Roo.TabPanelItem} The created TabPanelItem
41338 addTab : function(id, text, content, closable, tpl)
41340 var item = new Roo.bootstrap.panel.TabItem({
41344 closable : closable,
41347 this.addTabItem(item);
41349 item.setContent(content);
41355 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41356 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41357 * @return {Roo.TabPanelItem}
41359 getTab : function(id){
41360 return this.items[id];
41364 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41365 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41367 hideTab : function(id){
41368 var t = this.items[id];
41371 this.hiddenCount++;
41372 this.autoSizeTabs();
41377 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41378 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41380 unhideTab : function(id){
41381 var t = this.items[id];
41383 t.setHidden(false);
41384 this.hiddenCount--;
41385 this.autoSizeTabs();
41390 * Adds an existing {@link Roo.TabPanelItem}.
41391 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41393 addTabItem : function(item)
41395 this.items[item.id] = item;
41396 this.items.push(item);
41397 this.autoSizeTabs();
41398 // if(this.resizeTabs){
41399 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41400 // this.autoSizeTabs();
41402 // item.autoSize();
41407 * Removes a {@link Roo.TabPanelItem}.
41408 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41410 removeTab : function(id){
41411 var items = this.items;
41412 var tab = items[id];
41413 if(!tab) { return; }
41414 var index = items.indexOf(tab);
41415 if(this.active == tab && items.length > 1){
41416 var newTab = this.getNextAvailable(index);
41421 this.stripEl.dom.removeChild(tab.pnode.dom);
41422 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41423 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41425 items.splice(index, 1);
41426 delete this.items[tab.id];
41427 tab.fireEvent("close", tab);
41428 tab.purgeListeners();
41429 this.autoSizeTabs();
41432 getNextAvailable : function(start){
41433 var items = this.items;
41435 // look for a next tab that will slide over to
41436 // replace the one being removed
41437 while(index < items.length){
41438 var item = items[++index];
41439 if(item && !item.isHidden()){
41443 // if one isn't found select the previous tab (on the left)
41446 var item = items[--index];
41447 if(item && !item.isHidden()){
41455 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41456 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41458 disableTab : function(id){
41459 var tab = this.items[id];
41460 if(tab && this.active != tab){
41466 * Enables a {@link Roo.TabPanelItem} that is disabled.
41467 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41469 enableTab : function(id){
41470 var tab = this.items[id];
41475 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41476 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41477 * @return {Roo.TabPanelItem} The TabPanelItem.
41479 activate : function(id)
41481 //Roo.log('activite:' + id);
41483 var tab = this.items[id];
41487 if(tab == this.active || tab.disabled){
41491 this.fireEvent("beforetabchange", this, e, tab);
41492 if(e.cancel !== true && !tab.disabled){
41494 this.active.hide();
41496 this.active = this.items[id];
41497 this.active.show();
41498 this.fireEvent("tabchange", this, this.active);
41504 * Gets the active {@link Roo.TabPanelItem}.
41505 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41507 getActiveTab : function(){
41508 return this.active;
41512 * Updates the tab body element to fit the height of the container element
41513 * for overflow scrolling
41514 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41516 syncHeight : function(targetHeight){
41517 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41518 var bm = this.bodyEl.getMargins();
41519 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41520 this.bodyEl.setHeight(newHeight);
41524 onResize : function(){
41525 if(this.monitorResize){
41526 this.autoSizeTabs();
41531 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41533 beginUpdate : function(){
41534 this.updating = true;
41538 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41540 endUpdate : function(){
41541 this.updating = false;
41542 this.autoSizeTabs();
41546 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41548 autoSizeTabs : function()
41550 var count = this.items.length;
41551 var vcount = count - this.hiddenCount;
41554 this.stripEl.hide();
41556 this.stripEl.show();
41559 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41564 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41565 var availWidth = Math.floor(w / vcount);
41566 var b = this.stripBody;
41567 if(b.getWidth() > w){
41568 var tabs = this.items;
41569 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41570 if(availWidth < this.minTabWidth){
41571 /*if(!this.sleft){ // incomplete scrolling code
41572 this.createScrollButtons();
41575 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41578 if(this.currentTabWidth < this.preferredTabWidth){
41579 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41585 * Returns the number of tabs in this TabPanel.
41588 getCount : function(){
41589 return this.items.length;
41593 * Resizes all the tabs to the passed width
41594 * @param {Number} The new width
41596 setTabWidth : function(width){
41597 this.currentTabWidth = width;
41598 for(var i = 0, len = this.items.length; i < len; i++) {
41599 if(!this.items[i].isHidden()) {
41600 this.items[i].setWidth(width);
41606 * Destroys this TabPanel
41607 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41609 destroy : function(removeEl){
41610 Roo.EventManager.removeResizeListener(this.onResize, this);
41611 for(var i = 0, len = this.items.length; i < len; i++){
41612 this.items[i].purgeListeners();
41614 if(removeEl === true){
41615 this.el.update("");
41620 createStrip : function(container)
41622 var strip = document.createElement("nav");
41623 strip.className = Roo.bootstrap.version == 4 ?
41624 "navbar-light bg-light" :
41625 "navbar navbar-default"; //"x-tabs-wrap";
41626 container.appendChild(strip);
41630 createStripList : function(strip)
41632 // div wrapper for retard IE
41633 // returns the "tr" element.
41634 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41635 //'<div class="x-tabs-strip-wrap">'+
41636 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41637 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41638 return strip.firstChild; //.firstChild.firstChild.firstChild;
41640 createBody : function(container)
41642 var body = document.createElement("div");
41643 Roo.id(body, "tab-body");
41644 //Roo.fly(body).addClass("x-tabs-body");
41645 Roo.fly(body).addClass("tab-content");
41646 container.appendChild(body);
41649 createItemBody :function(bodyEl, id){
41650 var body = Roo.getDom(id);
41652 body = document.createElement("div");
41655 //Roo.fly(body).addClass("x-tabs-item-body");
41656 Roo.fly(body).addClass("tab-pane");
41657 bodyEl.insertBefore(body, bodyEl.firstChild);
41661 createStripElements : function(stripEl, text, closable, tpl)
41663 var td = document.createElement("li"); // was td..
41664 td.className = 'nav-item';
41666 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41669 stripEl.appendChild(td);
41671 td.className = "x-tabs-closable";
41672 if(!this.closeTpl){
41673 this.closeTpl = new Roo.Template(
41674 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41675 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41676 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41679 var el = this.closeTpl.overwrite(td, {"text": text});
41680 var close = el.getElementsByTagName("div")[0];
41681 var inner = el.getElementsByTagName("em")[0];
41682 return {"el": el, "close": close, "inner": inner};
41685 // not sure what this is..
41686 // if(!this.tabTpl){
41687 //this.tabTpl = new Roo.Template(
41688 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41689 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41691 // this.tabTpl = new Roo.Template(
41692 // '<a href="#">' +
41693 // '<span unselectable="on"' +
41694 // (this.disableTooltips ? '' : ' title="{text}"') +
41695 // ' >{text}</span></a>'
41701 var template = tpl || this.tabTpl || false;
41704 template = new Roo.Template(
41705 Roo.bootstrap.version == 4 ?
41707 '<a class="nav-link" href="#" unselectable="on"' +
41708 (this.disableTooltips ? '' : ' title="{text}"') +
41711 '<a class="nav-link" href="#">' +
41712 '<span unselectable="on"' +
41713 (this.disableTooltips ? '' : ' title="{text}"') +
41714 ' >{text}</span></a>'
41719 switch (typeof(template)) {
41723 template = new Roo.Template(template);
41729 var el = template.overwrite(td, {"text": text});
41731 var inner = el.getElementsByTagName("span")[0];
41733 return {"el": el, "inner": inner};
41741 * @class Roo.TabPanelItem
41742 * @extends Roo.util.Observable
41743 * Represents an individual item (tab plus body) in a TabPanel.
41744 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41745 * @param {String} id The id of this TabPanelItem
41746 * @param {String} text The text for the tab of this TabPanelItem
41747 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41749 Roo.bootstrap.panel.TabItem = function(config){
41751 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41752 * @type Roo.TabPanel
41754 this.tabPanel = config.panel;
41756 * The id for this TabPanelItem
41759 this.id = config.id;
41761 this.disabled = false;
41763 this.text = config.text;
41765 this.loaded = false;
41766 this.closable = config.closable;
41769 * The body element for this TabPanelItem.
41770 * @type Roo.Element
41772 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41773 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41774 this.bodyEl.setStyle("display", "block");
41775 this.bodyEl.setStyle("zoom", "1");
41776 //this.hideAction();
41778 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41780 this.el = Roo.get(els.el);
41781 this.inner = Roo.get(els.inner, true);
41782 this.textEl = Roo.bootstrap.version == 4 ?
41783 this.el : Roo.get(this.el.dom.firstChild, true);
41785 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41786 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41789 // this.el.on("mousedown", this.onTabMouseDown, this);
41790 this.el.on("click", this.onTabClick, this);
41792 if(config.closable){
41793 var c = Roo.get(els.close, true);
41794 c.dom.title = this.closeText;
41795 c.addClassOnOver("close-over");
41796 c.on("click", this.closeClick, this);
41802 * Fires when this tab becomes the active tab.
41803 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41804 * @param {Roo.TabPanelItem} this
41808 * @event beforeclose
41809 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41810 * @param {Roo.TabPanelItem} this
41811 * @param {Object} e Set cancel to true on this object to cancel the close.
41813 "beforeclose": true,
41816 * Fires when this tab is closed.
41817 * @param {Roo.TabPanelItem} this
41821 * @event deactivate
41822 * Fires when this tab is no longer the active tab.
41823 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41824 * @param {Roo.TabPanelItem} this
41826 "deactivate" : true
41828 this.hidden = false;
41830 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41833 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41835 purgeListeners : function(){
41836 Roo.util.Observable.prototype.purgeListeners.call(this);
41837 this.el.removeAllListeners();
41840 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41843 this.status_node.addClass("active");
41846 this.tabPanel.stripWrap.repaint();
41848 this.fireEvent("activate", this.tabPanel, this);
41852 * Returns true if this tab is the active tab.
41853 * @return {Boolean}
41855 isActive : function(){
41856 return this.tabPanel.getActiveTab() == this;
41860 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41863 this.status_node.removeClass("active");
41865 this.fireEvent("deactivate", this.tabPanel, this);
41868 hideAction : function(){
41869 this.bodyEl.hide();
41870 this.bodyEl.setStyle("position", "absolute");
41871 this.bodyEl.setLeft("-20000px");
41872 this.bodyEl.setTop("-20000px");
41875 showAction : function(){
41876 this.bodyEl.setStyle("position", "relative");
41877 this.bodyEl.setTop("");
41878 this.bodyEl.setLeft("");
41879 this.bodyEl.show();
41883 * Set the tooltip for the tab.
41884 * @param {String} tooltip The tab's tooltip
41886 setTooltip : function(text){
41887 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41888 this.textEl.dom.qtip = text;
41889 this.textEl.dom.removeAttribute('title');
41891 this.textEl.dom.title = text;
41895 onTabClick : function(e){
41896 e.preventDefault();
41897 this.tabPanel.activate(this.id);
41900 onTabMouseDown : function(e){
41901 e.preventDefault();
41902 this.tabPanel.activate(this.id);
41905 getWidth : function(){
41906 return this.inner.getWidth();
41909 setWidth : function(width){
41910 var iwidth = width - this.linode.getPadding("lr");
41911 this.inner.setWidth(iwidth);
41912 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41913 this.linode.setWidth(width);
41917 * Show or hide the tab
41918 * @param {Boolean} hidden True to hide or false to show.
41920 setHidden : function(hidden){
41921 this.hidden = hidden;
41922 this.linode.setStyle("display", hidden ? "none" : "");
41926 * Returns true if this tab is "hidden"
41927 * @return {Boolean}
41929 isHidden : function(){
41930 return this.hidden;
41934 * Returns the text for this tab
41937 getText : function(){
41941 autoSize : function(){
41942 //this.el.beginMeasure();
41943 this.textEl.setWidth(1);
41945 * #2804 [new] Tabs in Roojs
41946 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41948 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41949 //this.el.endMeasure();
41953 * Sets the text for the tab (Note: this also sets the tooltip text)
41954 * @param {String} text The tab's text and tooltip
41956 setText : function(text){
41958 this.textEl.update(text);
41959 this.setTooltip(text);
41960 //if(!this.tabPanel.resizeTabs){
41961 // this.autoSize();
41965 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41967 activate : function(){
41968 this.tabPanel.activate(this.id);
41972 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41974 disable : function(){
41975 if(this.tabPanel.active != this){
41976 this.disabled = true;
41977 this.status_node.addClass("disabled");
41982 * Enables this TabPanelItem if it was previously disabled.
41984 enable : function(){
41985 this.disabled = false;
41986 this.status_node.removeClass("disabled");
41990 * Sets the content for this TabPanelItem.
41991 * @param {String} content The content
41992 * @param {Boolean} loadScripts true to look for and load scripts
41994 setContent : function(content, loadScripts){
41995 this.bodyEl.update(content, loadScripts);
41999 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42000 * @return {Roo.UpdateManager} The UpdateManager
42002 getUpdateManager : function(){
42003 return this.bodyEl.getUpdateManager();
42007 * Set a URL to be used to load the content for this TabPanelItem.
42008 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42009 * @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)
42010 * @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)
42011 * @return {Roo.UpdateManager} The UpdateManager
42013 setUrl : function(url, params, loadOnce){
42014 if(this.refreshDelegate){
42015 this.un('activate', this.refreshDelegate);
42017 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42018 this.on("activate", this.refreshDelegate);
42019 return this.bodyEl.getUpdateManager();
42023 _handleRefresh : function(url, params, loadOnce){
42024 if(!loadOnce || !this.loaded){
42025 var updater = this.bodyEl.getUpdateManager();
42026 updater.update(url, params, this._setLoaded.createDelegate(this));
42031 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42032 * Will fail silently if the setUrl method has not been called.
42033 * This does not activate the panel, just updates its content.
42035 refresh : function(){
42036 if(this.refreshDelegate){
42037 this.loaded = false;
42038 this.refreshDelegate();
42043 _setLoaded : function(){
42044 this.loaded = true;
42048 closeClick : function(e){
42051 this.fireEvent("beforeclose", this, o);
42052 if(o.cancel !== true){
42053 this.tabPanel.removeTab(this.id);
42057 * The text displayed in the tooltip for the close icon.
42060 closeText : "Close this tab"
42063 * This script refer to:
42064 * Title: International Telephone Input
42065 * Author: Jack O'Connor
42066 * Code version: v12.1.12
42067 * Availability: https://github.com/jackocnr/intl-tel-input.git
42070 Roo.bootstrap.form.PhoneInputData = function() {
42073 "Afghanistan (افغانستان)",
42078 "Albania (Shqipëri)",
42083 "Algeria (الجزائر)",
42108 "Antigua and Barbuda",
42118 "Armenia (Հայաստան)",
42134 "Austria (Österreich)",
42139 "Azerbaijan (Azərbaycan)",
42149 "Bahrain (البحرين)",
42154 "Bangladesh (বাংলাদেশ)",
42164 "Belarus (Беларусь)",
42169 "Belgium (België)",
42199 "Bosnia and Herzegovina (Босна и Херцеговина)",
42214 "British Indian Ocean Territory",
42219 "British Virgin Islands",
42229 "Bulgaria (България)",
42239 "Burundi (Uburundi)",
42244 "Cambodia (កម្ពុជា)",
42249 "Cameroon (Cameroun)",
42258 ["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"]
42261 "Cape Verde (Kabu Verdi)",
42266 "Caribbean Netherlands",
42277 "Central African Republic (République centrafricaine)",
42297 "Christmas Island",
42303 "Cocos (Keeling) Islands",
42314 "Comoros (جزر القمر)",
42319 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42324 "Congo (Republic) (Congo-Brazzaville)",
42344 "Croatia (Hrvatska)",
42365 "Czech Republic (Česká republika)",
42370 "Denmark (Danmark)",
42385 "Dominican Republic (República Dominicana)",
42389 ["809", "829", "849"]
42407 "Equatorial Guinea (Guinea Ecuatorial)",
42427 "Falkland Islands (Islas Malvinas)",
42432 "Faroe Islands (Føroyar)",
42453 "French Guiana (Guyane française)",
42458 "French Polynesia (Polynésie française)",
42473 "Georgia (საქართველო)",
42478 "Germany (Deutschland)",
42498 "Greenland (Kalaallit Nunaat)",
42535 "Guinea-Bissau (Guiné Bissau)",
42560 "Hungary (Magyarország)",
42565 "Iceland (Ísland)",
42585 "Iraq (العراق)",
42601 "Israel (ישראל)",
42628 "Jordan (الأردن)",
42633 "Kazakhstan (Казахстан)",
42654 "Kuwait (الكويت)",
42659 "Kyrgyzstan (Кыргызстан)",
42669 "Latvia (Latvija)",
42674 "Lebanon (لبنان)",
42689 "Libya (ليبيا)",
42699 "Lithuania (Lietuva)",
42714 "Macedonia (FYROM) (Македонија)",
42719 "Madagascar (Madagasikara)",
42749 "Marshall Islands",
42759 "Mauritania (موريتانيا)",
42764 "Mauritius (Moris)",
42785 "Moldova (Republica Moldova)",
42795 "Mongolia (Монгол)",
42800 "Montenegro (Crna Gora)",
42810 "Morocco (المغرب)",
42816 "Mozambique (Moçambique)",
42821 "Myanmar (Burma) (မြန်မာ)",
42826 "Namibia (Namibië)",
42841 "Netherlands (Nederland)",
42846 "New Caledonia (Nouvelle-Calédonie)",
42881 "North Korea (조선 민주주의 인민 공화국)",
42886 "Northern Mariana Islands",
42902 "Pakistan (پاکستان)",
42912 "Palestine (فلسطين)",
42922 "Papua New Guinea",
42964 "Réunion (La Réunion)",
42970 "Romania (România)",
42986 "Saint Barthélemy",
42997 "Saint Kitts and Nevis",
43007 "Saint Martin (Saint-Martin (partie française))",
43013 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43018 "Saint Vincent and the Grenadines",
43033 "São Tomé and Príncipe (São Tomé e Príncipe)",
43038 "Saudi Arabia (المملكة العربية السعودية)",
43043 "Senegal (Sénégal)",
43073 "Slovakia (Slovensko)",
43078 "Slovenia (Slovenija)",
43088 "Somalia (Soomaaliya)",
43098 "South Korea (대한민국)",
43103 "South Sudan (جنوب السودان)",
43113 "Sri Lanka (ශ්රී ලංකාව)",
43118 "Sudan (السودان)",
43128 "Svalbard and Jan Mayen",
43139 "Sweden (Sverige)",
43144 "Switzerland (Schweiz)",
43149 "Syria (سوريا)",
43194 "Trinidad and Tobago",
43199 "Tunisia (تونس)",
43204 "Turkey (Türkiye)",
43214 "Turks and Caicos Islands",
43224 "U.S. Virgin Islands",
43234 "Ukraine (Україна)",
43239 "United Arab Emirates (الإمارات العربية المتحدة)",
43261 "Uzbekistan (Oʻzbekiston)",
43271 "Vatican City (Città del Vaticano)",
43282 "Vietnam (Việt Nam)",
43287 "Wallis and Futuna (Wallis-et-Futuna)",
43292 "Western Sahara (الصحراء الغربية)",
43298 "Yemen (اليمن)",
43322 * This script refer to:
43323 * Title: International Telephone Input
43324 * Author: Jack O'Connor
43325 * Code version: v12.1.12
43326 * Availability: https://github.com/jackocnr/intl-tel-input.git
43330 * @class Roo.bootstrap.form.PhoneInput
43331 * @extends Roo.bootstrap.form.TriggerField
43332 * An input with International dial-code selection
43334 * @cfg {String} defaultDialCode default '+852'
43335 * @cfg {Array} preferedCountries default []
43338 * Create a new PhoneInput.
43339 * @param {Object} config Configuration options
43342 Roo.bootstrap.form.PhoneInput = function(config) {
43343 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43346 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43348 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43350 listWidth: undefined,
43352 selectedClass: 'active',
43354 invalidClass : "has-warning",
43356 validClass: 'has-success',
43358 allowed: '0123456789',
43363 * @cfg {String} defaultDialCode The default dial code when initializing the input
43365 defaultDialCode: '+852',
43368 * @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
43370 preferedCountries: false,
43372 getAutoCreate : function()
43374 var data = Roo.bootstrap.form.PhoneInputData();
43375 var align = this.labelAlign || this.parentLabelAlign();
43378 this.allCountries = [];
43379 this.dialCodeMapping = [];
43381 for (var i = 0; i < data.length; i++) {
43383 this.allCountries[i] = {
43387 priority: c[3] || 0,
43388 areaCodes: c[4] || null
43390 this.dialCodeMapping[c[2]] = {
43393 priority: c[3] || 0,
43394 areaCodes: c[4] || null
43406 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43407 maxlength: this.max_length,
43408 cls : 'form-control tel-input',
43409 autocomplete: 'new-password'
43412 var hiddenInput = {
43415 cls: 'hidden-tel-input'
43419 hiddenInput.name = this.name;
43422 if (this.disabled) {
43423 input.disabled = true;
43426 var flag_container = {
43443 cls: this.hasFeedback ? 'has-feedback' : '',
43449 cls: 'dial-code-holder',
43456 cls: 'roo-select2-container input-group',
43463 if (this.fieldLabel.length) {
43466 tooltip: 'This field is required'
43472 cls: 'control-label',
43478 html: this.fieldLabel
43481 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43487 if(this.indicatorpos == 'right') {
43488 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43495 if(align == 'left') {
43503 if(this.labelWidth > 12){
43504 label.style = "width: " + this.labelWidth + 'px';
43506 if(this.labelWidth < 13 && this.labelmd == 0){
43507 this.labelmd = this.labelWidth;
43509 if(this.labellg > 0){
43510 label.cls += ' col-lg-' + this.labellg;
43511 input.cls += ' col-lg-' + (12 - this.labellg);
43513 if(this.labelmd > 0){
43514 label.cls += ' col-md-' + this.labelmd;
43515 container.cls += ' col-md-' + (12 - this.labelmd);
43517 if(this.labelsm > 0){
43518 label.cls += ' col-sm-' + this.labelsm;
43519 container.cls += ' col-sm-' + (12 - this.labelsm);
43521 if(this.labelxs > 0){
43522 label.cls += ' col-xs-' + this.labelxs;
43523 container.cls += ' col-xs-' + (12 - this.labelxs);
43533 var settings = this;
43535 ['xs','sm','md','lg'].map(function(size){
43536 if (settings[size]) {
43537 cfg.cls += ' col-' + size + '-' + settings[size];
43541 this.store = new Roo.data.Store({
43542 proxy : new Roo.data.MemoryProxy({}),
43543 reader : new Roo.data.JsonReader({
43554 'name' : 'dialCode',
43558 'name' : 'priority',
43562 'name' : 'areaCodes',
43569 if(!this.preferedCountries) {
43570 this.preferedCountries = [
43577 var p = this.preferedCountries.reverse();
43580 for (var i = 0; i < p.length; i++) {
43581 for (var j = 0; j < this.allCountries.length; j++) {
43582 if(this.allCountries[j].iso2 == p[i]) {
43583 var t = this.allCountries[j];
43584 this.allCountries.splice(j,1);
43585 this.allCountries.unshift(t);
43591 this.store.proxy.data = {
43593 data: this.allCountries
43599 initEvents : function()
43602 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43604 this.indicator = this.indicatorEl();
43605 this.flag = this.flagEl();
43606 this.dialCodeHolder = this.dialCodeHolderEl();
43608 this.trigger = this.el.select('div.flag-box',true).first();
43609 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43614 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43615 _this.list.setWidth(lw);
43618 this.list.on('mouseover', this.onViewOver, this);
43619 this.list.on('mousemove', this.onViewMove, this);
43620 this.inputEl().on("keyup", this.onKeyUp, this);
43621 this.inputEl().on("keypress", this.onKeyPress, this);
43623 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43625 this.view = new Roo.View(this.list, this.tpl, {
43626 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43629 this.view.on('click', this.onViewClick, this);
43630 this.setValue(this.defaultDialCode);
43633 onTriggerClick : function(e)
43635 Roo.log('trigger click');
43640 if(this.isExpanded()){
43642 this.hasFocus = false;
43644 this.store.load({});
43645 this.hasFocus = true;
43650 isExpanded : function()
43652 return this.list.isVisible();
43655 collapse : function()
43657 if(!this.isExpanded()){
43661 Roo.get(document).un('mousedown', this.collapseIf, this);
43662 Roo.get(document).un('mousewheel', this.collapseIf, this);
43663 this.fireEvent('collapse', this);
43667 expand : function()
43671 if(this.isExpanded() || !this.hasFocus){
43675 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43676 this.list.setWidth(lw);
43679 this.restrictHeight();
43681 Roo.get(document).on('mousedown', this.collapseIf, this);
43682 Roo.get(document).on('mousewheel', this.collapseIf, this);
43684 this.fireEvent('expand', this);
43687 restrictHeight : function()
43689 this.list.alignTo(this.inputEl(), this.listAlign);
43690 this.list.alignTo(this.inputEl(), this.listAlign);
43693 onViewOver : function(e, t)
43695 if(this.inKeyMode){
43698 var item = this.view.findItemFromChild(t);
43701 var index = this.view.indexOf(item);
43702 this.select(index, false);
43707 onViewClick : function(view, doFocus, el, e)
43709 var index = this.view.getSelectedIndexes()[0];
43711 var r = this.store.getAt(index);
43714 this.onSelect(r, index);
43716 if(doFocus !== false && !this.blockFocus){
43717 this.inputEl().focus();
43721 onViewMove : function(e, t)
43723 this.inKeyMode = false;
43726 select : function(index, scrollIntoView)
43728 this.selectedIndex = index;
43729 this.view.select(index);
43730 if(scrollIntoView !== false){
43731 var el = this.view.getNode(index);
43733 this.list.scrollChildIntoView(el, false);
43738 createList : function()
43740 this.list = Roo.get(document.body).createChild({
43742 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43743 style: 'display:none'
43746 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43749 collapseIf : function(e)
43751 var in_combo = e.within(this.el);
43752 var in_list = e.within(this.list);
43753 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43755 if (in_combo || in_list || is_list) {
43761 onSelect : function(record, index)
43763 if(this.fireEvent('beforeselect', this, record, index) !== false){
43765 this.setFlagClass(record.data.iso2);
43766 this.setDialCode(record.data.dialCode);
43767 this.hasFocus = false;
43769 this.fireEvent('select', this, record, index);
43773 flagEl : function()
43775 var flag = this.el.select('div.flag',true).first();
43782 dialCodeHolderEl : function()
43784 var d = this.el.select('input.dial-code-holder',true).first();
43791 setDialCode : function(v)
43793 this.dialCodeHolder.dom.value = '+'+v;
43796 setFlagClass : function(n)
43798 this.flag.dom.className = 'flag '+n;
43801 getValue : function()
43803 var v = this.inputEl().getValue();
43804 if(this.dialCodeHolder) {
43805 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43810 setValue : function(v)
43812 var d = this.getDialCode(v);
43814 //invalid dial code
43815 if(v.length == 0 || !d || d.length == 0) {
43817 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43818 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43824 this.setFlagClass(this.dialCodeMapping[d].iso2);
43825 this.setDialCode(d);
43826 this.inputEl().dom.value = v.replace('+'+d,'');
43827 this.hiddenEl().dom.value = this.getValue();
43832 getDialCode : function(v)
43836 if (v.length == 0) {
43837 return this.dialCodeHolder.dom.value;
43841 if (v.charAt(0) != "+") {
43844 var numericChars = "";
43845 for (var i = 1; i < v.length; i++) {
43846 var c = v.charAt(i);
43849 if (this.dialCodeMapping[numericChars]) {
43850 dialCode = v.substr(1, i);
43852 if (numericChars.length == 4) {
43862 this.setValue(this.defaultDialCode);
43866 hiddenEl : function()
43868 return this.el.select('input.hidden-tel-input',true).first();
43871 // after setting val
43872 onKeyUp : function(e){
43873 this.setValue(this.getValue());
43876 onKeyPress : function(e){
43877 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43884 * @class Roo.bootstrap.form.MoneyField
43885 * @extends Roo.bootstrap.form.ComboBox
43886 * Bootstrap MoneyField class
43889 * Create a new MoneyField.
43890 * @param {Object} config Configuration options
43893 Roo.bootstrap.form.MoneyField = function(config) {
43895 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43899 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43902 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43904 allowDecimals : true,
43906 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43908 decimalSeparator : ".",
43910 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43912 decimalPrecision : 0,
43914 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43916 allowNegative : true,
43918 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43922 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43924 minValue : Number.NEGATIVE_INFINITY,
43926 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43928 maxValue : Number.MAX_VALUE,
43930 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43932 minText : "The minimum value for this field is {0}",
43934 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43936 maxText : "The maximum value for this field is {0}",
43938 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43939 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43941 nanText : "{0} is not a valid number",
43943 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43947 * @cfg {String} defaults currency of the MoneyField
43948 * value should be in lkey
43950 defaultCurrency : false,
43952 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43954 thousandsDelimiter : false,
43956 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43965 * @cfg {Roo.data.Store} store Store to lookup currency??
43969 getAutoCreate : function()
43971 var align = this.labelAlign || this.parentLabelAlign();
43983 cls : 'form-control roo-money-amount-input',
43984 autocomplete: 'new-password'
43987 var hiddenInput = {
43991 cls: 'hidden-number-input'
43994 if(this.max_length) {
43995 input.maxlength = this.max_length;
43999 hiddenInput.name = this.name;
44002 if (this.disabled) {
44003 input.disabled = true;
44006 var clg = 12 - this.inputlg;
44007 var cmd = 12 - this.inputmd;
44008 var csm = 12 - this.inputsm;
44009 var cxs = 12 - this.inputxs;
44013 cls : 'row roo-money-field',
44017 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44021 cls: 'roo-select2-container input-group',
44025 cls : 'form-control roo-money-currency-input',
44026 autocomplete: 'new-password',
44028 name : this.currencyName
44032 cls : 'input-group-addon',
44046 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44050 cls: this.hasFeedback ? 'has-feedback' : '',
44061 if (this.fieldLabel.length) {
44064 tooltip: 'This field is required'
44070 cls: 'control-label',
44076 html: this.fieldLabel
44079 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44085 if(this.indicatorpos == 'right') {
44086 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44093 if(align == 'left') {
44101 if(this.labelWidth > 12){
44102 label.style = "width: " + this.labelWidth + 'px';
44104 if(this.labelWidth < 13 && this.labelmd == 0){
44105 this.labelmd = this.labelWidth;
44107 if(this.labellg > 0){
44108 label.cls += ' col-lg-' + this.labellg;
44109 input.cls += ' col-lg-' + (12 - this.labellg);
44111 if(this.labelmd > 0){
44112 label.cls += ' col-md-' + this.labelmd;
44113 container.cls += ' col-md-' + (12 - this.labelmd);
44115 if(this.labelsm > 0){
44116 label.cls += ' col-sm-' + this.labelsm;
44117 container.cls += ' col-sm-' + (12 - this.labelsm);
44119 if(this.labelxs > 0){
44120 label.cls += ' col-xs-' + this.labelxs;
44121 container.cls += ' col-xs-' + (12 - this.labelxs);
44132 var settings = this;
44134 ['xs','sm','md','lg'].map(function(size){
44135 if (settings[size]) {
44136 cfg.cls += ' col-' + size + '-' + settings[size];
44143 initEvents : function()
44145 this.indicator = this.indicatorEl();
44147 this.initCurrencyEvent();
44149 this.initNumberEvent();
44152 initCurrencyEvent : function()
44155 throw "can not find store for combo";
44158 this.store = Roo.factory(this.store, Roo.data);
44159 this.store.parent = this;
44163 this.triggerEl = this.el.select('.input-group-addon', true).first();
44165 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44170 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44171 _this.list.setWidth(lw);
44174 this.list.on('mouseover', this.onViewOver, this);
44175 this.list.on('mousemove', this.onViewMove, this);
44176 this.list.on('scroll', this.onViewScroll, this);
44179 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44182 this.view = new Roo.View(this.list, this.tpl, {
44183 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44186 this.view.on('click', this.onViewClick, this);
44188 this.store.on('beforeload', this.onBeforeLoad, this);
44189 this.store.on('load', this.onLoad, this);
44190 this.store.on('loadexception', this.onLoadException, this);
44192 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44193 "up" : function(e){
44194 this.inKeyMode = true;
44198 "down" : function(e){
44199 if(!this.isExpanded()){
44200 this.onTriggerClick();
44202 this.inKeyMode = true;
44207 "enter" : function(e){
44210 if(this.fireEvent("specialkey", this, e)){
44211 this.onViewClick(false);
44217 "esc" : function(e){
44221 "tab" : function(e){
44224 if(this.fireEvent("specialkey", this, e)){
44225 this.onViewClick(false);
44233 doRelay : function(foo, bar, hname){
44234 if(hname == 'down' || this.scope.isExpanded()){
44235 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44243 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44247 initNumberEvent : function(e)
44249 this.inputEl().on("keydown" , this.fireKey, this);
44250 this.inputEl().on("focus", this.onFocus, this);
44251 this.inputEl().on("blur", this.onBlur, this);
44253 this.inputEl().relayEvent('keyup', this);
44255 if(this.indicator){
44256 this.indicator.addClass('invisible');
44259 this.originalValue = this.getValue();
44261 if(this.validationEvent == 'keyup'){
44262 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44263 this.inputEl().on('keyup', this.filterValidation, this);
44265 else if(this.validationEvent !== false){
44266 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44269 if(this.selectOnFocus){
44270 this.on("focus", this.preFocus, this);
44273 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44274 this.inputEl().on("keypress", this.filterKeys, this);
44276 this.inputEl().relayEvent('keypress', this);
44279 var allowed = "0123456789";
44281 if(this.allowDecimals){
44282 allowed += this.decimalSeparator;
44285 if(this.allowNegative){
44289 if(this.thousandsDelimiter) {
44293 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44295 var keyPress = function(e){
44297 var k = e.getKey();
44299 var c = e.getCharCode();
44302 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44303 allowed.indexOf(String.fromCharCode(c)) === -1
44309 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44313 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44318 this.inputEl().on("keypress", keyPress, this);
44322 onTriggerClick : function(e)
44329 this.loadNext = false;
44331 if(this.isExpanded()){
44336 this.hasFocus = true;
44338 if(this.triggerAction == 'all') {
44339 this.doQuery(this.allQuery, true);
44343 this.doQuery(this.getRawValue());
44346 getCurrency : function()
44348 var v = this.currencyEl().getValue();
44353 restrictHeight : function()
44355 this.list.alignTo(this.currencyEl(), this.listAlign);
44356 this.list.alignTo(this.currencyEl(), this.listAlign);
44359 onViewClick : function(view, doFocus, el, e)
44361 var index = this.view.getSelectedIndexes()[0];
44363 var r = this.store.getAt(index);
44366 this.onSelect(r, index);
44370 onSelect : function(record, index){
44372 if(this.fireEvent('beforeselect', this, record, index) !== false){
44374 this.setFromCurrencyData(index > -1 ? record.data : false);
44378 this.fireEvent('select', this, record, index);
44382 setFromCurrencyData : function(o)
44386 this.lastCurrency = o;
44388 if (this.currencyField) {
44389 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44391 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44394 this.lastSelectionText = currency;
44396 //setting default currency
44397 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44398 this.setCurrency(this.defaultCurrency);
44402 this.setCurrency(currency);
44405 setFromData : function(o)
44409 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44411 this.setFromCurrencyData(c);
44416 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44418 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44421 this.setValue(value);
44425 setCurrency : function(v)
44427 this.currencyValue = v;
44430 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44435 setValue : function(v)
44437 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44443 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44445 this.inputEl().dom.value = (v == '') ? '' :
44446 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44448 if(!this.allowZero && v === '0') {
44449 this.hiddenEl().dom.value = '';
44450 this.inputEl().dom.value = '';
44457 getRawValue : function()
44459 var v = this.inputEl().getValue();
44464 getValue : function()
44466 return this.fixPrecision(this.parseValue(this.getRawValue()));
44469 parseValue : function(value)
44471 if(this.thousandsDelimiter) {
44473 r = new RegExp(",", "g");
44474 value = value.replace(r, "");
44477 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44478 return isNaN(value) ? '' : value;
44482 fixPrecision : function(value)
44484 if(this.thousandsDelimiter) {
44486 r = new RegExp(",", "g");
44487 value = value.replace(r, "");
44490 var nan = isNaN(value);
44492 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44493 return nan ? '' : value;
44495 return parseFloat(value).toFixed(this.decimalPrecision);
44498 decimalPrecisionFcn : function(v)
44500 return Math.floor(v);
44503 validateValue : function(value)
44505 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44509 var num = this.parseValue(value);
44512 this.markInvalid(String.format(this.nanText, value));
44516 if(num < this.minValue){
44517 this.markInvalid(String.format(this.minText, this.minValue));
44521 if(num > this.maxValue){
44522 this.markInvalid(String.format(this.maxText, this.maxValue));
44529 validate : function()
44531 if(this.disabled || this.allowBlank){
44536 var currency = this.getCurrency();
44538 if(this.validateValue(this.getRawValue()) && currency.length){
44543 this.markInvalid();
44547 getName: function()
44552 beforeBlur : function()
44558 var v = this.parseValue(this.getRawValue());
44565 onBlur : function()
44569 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44570 //this.el.removeClass(this.focusClass);
44573 this.hasFocus = false;
44575 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44579 var v = this.getValue();
44581 if(String(v) !== String(this.startValue)){
44582 this.fireEvent('change', this, v, this.startValue);
44585 this.fireEvent("blur", this);
44588 inputEl : function()
44590 return this.el.select('.roo-money-amount-input', true).first();
44593 currencyEl : function()
44595 return this.el.select('.roo-money-currency-input', true).first();
44598 hiddenEl : function()
44600 return this.el.select('input.hidden-number-input',true).first();
44604 * @class Roo.bootstrap.BezierSignature
44605 * @extends Roo.bootstrap.Component
44606 * Bootstrap BezierSignature class
44607 * This script refer to:
44608 * Title: Signature Pad
44610 * Availability: https://github.com/szimek/signature_pad
44613 * Create a new BezierSignature
44614 * @param {Object} config The config object
44617 Roo.bootstrap.BezierSignature = function(config){
44618 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44624 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44631 mouse_btn_down: true,
44634 * @cfg {int} canvas height
44636 canvas_height: '200px',
44639 * @cfg {float|function} Radius of a single dot.
44644 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44649 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44654 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44659 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44664 * @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.
44666 bg_color: 'rgba(0, 0, 0, 0)',
44669 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44671 dot_color: 'black',
44674 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44676 velocity_filter_weight: 0.7,
44679 * @cfg {function} Callback when stroke begin.
44684 * @cfg {function} Callback when stroke end.
44688 getAutoCreate : function()
44690 var cls = 'roo-signature column';
44693 cls += ' ' + this.cls;
44703 for(var i = 0; i < col_sizes.length; i++) {
44704 if(this[col_sizes[i]]) {
44705 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44715 cls: 'roo-signature-body',
44719 cls: 'roo-signature-body-canvas',
44720 height: this.canvas_height,
44721 width: this.canvas_width
44728 style: 'display: none'
44736 initEvents: function()
44738 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44740 var canvas = this.canvasEl();
44742 // mouse && touch event swapping...
44743 canvas.dom.style.touchAction = 'none';
44744 canvas.dom.style.msTouchAction = 'none';
44746 this.mouse_btn_down = false;
44747 canvas.on('mousedown', this._handleMouseDown, this);
44748 canvas.on('mousemove', this._handleMouseMove, this);
44749 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44751 if (window.PointerEvent) {
44752 canvas.on('pointerdown', this._handleMouseDown, this);
44753 canvas.on('pointermove', this._handleMouseMove, this);
44754 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44757 if ('ontouchstart' in window) {
44758 canvas.on('touchstart', this._handleTouchStart, this);
44759 canvas.on('touchmove', this._handleTouchMove, this);
44760 canvas.on('touchend', this._handleTouchEnd, this);
44763 Roo.EventManager.onWindowResize(this.resize, this, true);
44765 // file input event
44766 this.fileEl().on('change', this.uploadImage, this);
44773 resize: function(){
44775 var canvas = this.canvasEl().dom;
44776 var ctx = this.canvasElCtx();
44777 var img_data = false;
44779 if(canvas.width > 0) {
44780 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44782 // setting canvas width will clean img data
44785 var style = window.getComputedStyle ?
44786 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44788 var padding_left = parseInt(style.paddingLeft) || 0;
44789 var padding_right = parseInt(style.paddingRight) || 0;
44791 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44794 ctx.putImageData(img_data, 0, 0);
44798 _handleMouseDown: function(e)
44800 if (e.browserEvent.which === 1) {
44801 this.mouse_btn_down = true;
44802 this.strokeBegin(e);
44806 _handleMouseMove: function (e)
44808 if (this.mouse_btn_down) {
44809 this.strokeMoveUpdate(e);
44813 _handleMouseUp: function (e)
44815 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44816 this.mouse_btn_down = false;
44821 _handleTouchStart: function (e) {
44823 e.preventDefault();
44824 if (e.browserEvent.targetTouches.length === 1) {
44825 // var touch = e.browserEvent.changedTouches[0];
44826 // this.strokeBegin(touch);
44828 this.strokeBegin(e); // assume e catching the correct xy...
44832 _handleTouchMove: function (e) {
44833 e.preventDefault();
44834 // var touch = event.targetTouches[0];
44835 // _this._strokeMoveUpdate(touch);
44836 this.strokeMoveUpdate(e);
44839 _handleTouchEnd: function (e) {
44840 var wasCanvasTouched = e.target === this.canvasEl().dom;
44841 if (wasCanvasTouched) {
44842 e.preventDefault();
44843 // var touch = event.changedTouches[0];
44844 // _this._strokeEnd(touch);
44849 reset: function () {
44850 this._lastPoints = [];
44851 this._lastVelocity = 0;
44852 this._lastWidth = (this.min_width + this.max_width) / 2;
44853 this.canvasElCtx().fillStyle = this.dot_color;
44856 strokeMoveUpdate: function(e)
44858 this.strokeUpdate(e);
44860 if (this.throttle) {
44861 this.throttleStroke(this.strokeUpdate, this.throttle);
44864 this.strokeUpdate(e);
44868 strokeBegin: function(e)
44870 var newPointGroup = {
44871 color: this.dot_color,
44875 if (typeof this.onBegin === 'function') {
44879 this.curve_data.push(newPointGroup);
44881 this.strokeUpdate(e);
44884 strokeUpdate: function(e)
44886 var rect = this.canvasEl().dom.getBoundingClientRect();
44887 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44888 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44889 var lastPoints = lastPointGroup.points;
44890 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44891 var isLastPointTooClose = lastPoint
44892 ? point.distanceTo(lastPoint) <= this.min_distance
44894 var color = lastPointGroup.color;
44895 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44896 var curve = this.addPoint(point);
44898 this.drawDot({color: color, point: point});
44901 this.drawCurve({color: color, curve: curve});
44911 strokeEnd: function(e)
44913 this.strokeUpdate(e);
44914 if (typeof this.onEnd === 'function') {
44919 addPoint: function (point) {
44920 var _lastPoints = this._lastPoints;
44921 _lastPoints.push(point);
44922 if (_lastPoints.length > 2) {
44923 if (_lastPoints.length === 3) {
44924 _lastPoints.unshift(_lastPoints[0]);
44926 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44927 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44928 _lastPoints.shift();
44934 calculateCurveWidths: function (startPoint, endPoint) {
44935 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44936 (1 - this.velocity_filter_weight) * this._lastVelocity;
44938 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44941 start: this._lastWidth
44944 this._lastVelocity = velocity;
44945 this._lastWidth = newWidth;
44949 drawDot: function (_a) {
44950 var color = _a.color, point = _a.point;
44951 var ctx = this.canvasElCtx();
44952 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44954 this.drawCurveSegment(point.x, point.y, width);
44956 ctx.fillStyle = color;
44960 drawCurve: function (_a) {
44961 var color = _a.color, curve = _a.curve;
44962 var ctx = this.canvasElCtx();
44963 var widthDelta = curve.endWidth - curve.startWidth;
44964 var drawSteps = Math.floor(curve.length()) * 2;
44966 ctx.fillStyle = color;
44967 for (var i = 0; i < drawSteps; i += 1) {
44968 var t = i / drawSteps;
44974 var x = uuu * curve.startPoint.x;
44975 x += 3 * uu * t * curve.control1.x;
44976 x += 3 * u * tt * curve.control2.x;
44977 x += ttt * curve.endPoint.x;
44978 var y = uuu * curve.startPoint.y;
44979 y += 3 * uu * t * curve.control1.y;
44980 y += 3 * u * tt * curve.control2.y;
44981 y += ttt * curve.endPoint.y;
44982 var width = curve.startWidth + ttt * widthDelta;
44983 this.drawCurveSegment(x, y, width);
44989 drawCurveSegment: function (x, y, width) {
44990 var ctx = this.canvasElCtx();
44992 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44993 this.is_empty = false;
44998 var ctx = this.canvasElCtx();
44999 var canvas = this.canvasEl().dom;
45000 ctx.fillStyle = this.bg_color;
45001 ctx.clearRect(0, 0, canvas.width, canvas.height);
45002 ctx.fillRect(0, 0, canvas.width, canvas.height);
45003 this.curve_data = [];
45005 this.is_empty = true;
45010 return this.el.select('input',true).first();
45013 canvasEl: function()
45015 return this.el.select('canvas',true).first();
45018 canvasElCtx: function()
45020 return this.el.select('canvas',true).first().dom.getContext('2d');
45023 getImage: function(type)
45025 if(this.is_empty) {
45030 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45033 drawFromImage: function(img_src)
45035 var img = new Image();
45037 img.onload = function(){
45038 this.canvasElCtx().drawImage(img, 0, 0);
45043 this.is_empty = false;
45046 selectImage: function()
45048 this.fileEl().dom.click();
45051 uploadImage: function(e)
45053 var reader = new FileReader();
45055 reader.onload = function(e){
45056 var img = new Image();
45057 img.onload = function(){
45059 this.canvasElCtx().drawImage(img, 0, 0);
45061 img.src = e.target.result;
45064 reader.readAsDataURL(e.target.files[0]);
45067 // Bezier Point Constructor
45068 Point: (function () {
45069 function Point(x, y, time) {
45072 this.time = time || Date.now();
45074 Point.prototype.distanceTo = function (start) {
45075 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45077 Point.prototype.equals = function (other) {
45078 return this.x === other.x && this.y === other.y && this.time === other.time;
45080 Point.prototype.velocityFrom = function (start) {
45081 return this.time !== start.time
45082 ? this.distanceTo(start) / (this.time - start.time)
45089 // Bezier Constructor
45090 Bezier: (function () {
45091 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45092 this.startPoint = startPoint;
45093 this.control2 = control2;
45094 this.control1 = control1;
45095 this.endPoint = endPoint;
45096 this.startWidth = startWidth;
45097 this.endWidth = endWidth;
45099 Bezier.fromPoints = function (points, widths, scope) {
45100 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45101 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45102 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45104 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45105 var dx1 = s1.x - s2.x;
45106 var dy1 = s1.y - s2.y;
45107 var dx2 = s2.x - s3.x;
45108 var dy2 = s2.y - s3.y;
45109 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45110 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45111 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45112 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45113 var dxm = m1.x - m2.x;
45114 var dym = m1.y - m2.y;
45115 var k = l2 / (l1 + l2);
45116 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45117 var tx = s2.x - cm.x;
45118 var ty = s2.y - cm.y;
45120 c1: new scope.Point(m1.x + tx, m1.y + ty),
45121 c2: new scope.Point(m2.x + tx, m2.y + ty)
45124 Bezier.prototype.length = function () {
45129 for (var i = 0; i <= steps; i += 1) {
45131 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45132 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45134 var xdiff = cx - px;
45135 var ydiff = cy - py;
45136 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45143 Bezier.prototype.point = function (t, start, c1, c2, end) {
45144 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45145 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45146 + (3.0 * c2 * (1.0 - t) * t * t)
45147 + (end * t * t * t);
45152 throttleStroke: function(fn, wait) {
45153 if (wait === void 0) { wait = 250; }
45155 var timeout = null;
45159 var later = function () {
45160 previous = Date.now();
45162 result = fn.apply(storedContext, storedArgs);
45164 storedContext = null;
45168 return function wrapper() {
45170 for (var _i = 0; _i < arguments.length; _i++) {
45171 args[_i] = arguments[_i];
45173 var now = Date.now();
45174 var remaining = wait - (now - previous);
45175 storedContext = this;
45177 if (remaining <= 0 || remaining > wait) {
45179 clearTimeout(timeout);
45183 result = fn.apply(storedContext, storedArgs);
45185 storedContext = null;
45189 else if (!timeout) {
45190 timeout = window.setTimeout(later, remaining);
45200 // old names for form elements
45201 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45202 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45203 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45204 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45205 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45206 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45207 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45208 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45209 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45210 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45211 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45212 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45213 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45214 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45215 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45216 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45217 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45218 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45219 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45220 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45221 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45222 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45223 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45224 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45225 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45226 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45228 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45229 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45231 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45232 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45234 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45235 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45236 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45237 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator