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
859 * @children Roo.bootstrap.Component
861 * Bootstrap Body class
865 * @param {Object} config The config object
868 Roo.bootstrap.Body = function(config){
870 config = config || {};
872 Roo.bootstrap.Body.superclass.constructor.call(this, config);
873 this.el = Roo.get(config.el ? config.el : document.body );
874 if (this.cls && this.cls.length) {
875 Roo.get(document.body).addClass(this.cls);
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
881 is_body : true,// just to make sure it's constructed?
886 onRender : function(ct, position)
888 /* Roo.log("Roo.bootstrap.Body - onRender");
889 if (this.cls && this.cls.length) {
890 Roo.get(document.body).addClass(this.cls);
909 * @class Roo.bootstrap.ButtonGroup
910 * @extends Roo.bootstrap.Component
911 * Bootstrap ButtonGroup class
912 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
914 * @cfg {String} size lg | sm | xs (default empty normal)
915 * @cfg {String} align vertical | justified (default none)
916 * @cfg {String} direction up | down (default down)
917 * @cfg {Boolean} toolbar false | true
918 * @cfg {Boolean} btn true | false
923 * @param {Object} config The config object
926 Roo.bootstrap.ButtonGroup = function(config){
927 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
938 getAutoCreate : function(){
944 cfg.html = this.html || cfg.html;
955 if (['vertical','justified'].indexOf(this.align)!==-1) {
956 cfg.cls = 'btn-group-' + this.align;
958 if (this.align == 'justified') {
959 console.log(this.items);
963 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964 cfg.cls += ' btn-group-' + this.size;
967 if (this.direction == 'up') {
968 cfg.cls += ' dropup' ;
974 * Add a button to the group (similar to NavItem API.)
976 addItem : function(cfg)
978 var cn = new Roo.bootstrap.Button(cfg);
980 cn.parentId = this.id;
981 cn.onRender(this.el, null);
995 * @class Roo.bootstrap.Button
996 * @extends Roo.bootstrap.Component
997 * Bootstrap Button class
998 * @cfg {String} html The button content
999 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002 * @cfg {String} size (lg|sm|xs)
1003 * @cfg {String} tag (a|input|submit)
1004 * @cfg {String} href empty or href
1005 * @cfg {Boolean} disabled default false;
1006 * @cfg {Boolean} isClose default false;
1007 * @cfg {String} glyphicon depricated - use fa
1008 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009 * @cfg {String} badge text for badge
1010 * @cfg {String} theme (default|glow)
1011 * @cfg {Boolean} inverse dark themed version
1012 * @cfg {Boolean} toggle is it a slidy toggle button
1013 * @cfg {Boolean} pressed default null - if the button ahs active state
1014 * @cfg {String} ontext text for on slidy toggle state
1015 * @cfg {String} offtext text for off slidy toggle state
1016 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1017 * @cfg {Boolean} removeClass remove the standard class..
1018 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1019 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1023 * Create a new button
1024 * @param {Object} config The config object
1028 Roo.bootstrap.Button = function(config){
1029 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1035 * When a button is pressed
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1042 * When a button is double clicked
1043 * @param {Roo.bootstrap.Button} btn
1044 * @param {Roo.EventObject} e
1049 * After the button has been toggles
1050 * @param {Roo.bootstrap.Button} btn
1051 * @param {Roo.EventObject} e
1052 * @param {boolean} pressed (also available as button.pressed)
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1079 preventDefault: true,
1088 getAutoCreate : function(){
1096 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098 this.tag = 'button';
1102 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1104 if (this.toggle == true) {
1107 cls: 'slider-frame roo-button',
1111 'data-on-text':'ON',
1112 'data-off-text':'OFF',
1113 cls: 'slider-button',
1118 // why are we validating the weights?
1119 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120 cfg.cls += ' ' + this.weight;
1127 cfg.cls += ' close';
1129 cfg["aria-hidden"] = true;
1131 cfg.html = "×";
1137 if (this.theme==='default') {
1138 cfg.cls = 'btn roo-button';
1140 //if (this.parentType != 'Navbar') {
1141 this.weight = this.weight.length ? this.weight : 'default';
1143 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147 cfg.cls += ' btn-' + outline + weight;
1148 if (this.weight == 'default') {
1150 cfg.cls += ' btn-' + this.weight;
1153 } else if (this.theme==='glow') {
1156 cfg.cls = 'btn-glow roo-button';
1158 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1160 cfg.cls += ' ' + this.weight;
1166 this.cls += ' inverse';
1170 if (this.active || this.pressed === true) {
1171 cfg.cls += ' active';
1174 if (this.disabled) {
1175 cfg.disabled = 'disabled';
1179 Roo.log('changing to ul' );
1181 this.glyphicon = 'caret';
1182 if (Roo.bootstrap.version == 4) {
1183 this.fa = 'caret-down';
1188 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1190 //gsRoo.log(this.parentType);
1191 if (this.parentType === 'Navbar' && !this.parent().bar) {
1192 Roo.log('changing to li?');
1201 href : this.href || '#'
1204 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1205 cfg.cls += ' dropdown';
1212 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1214 if (this.glyphicon) {
1215 cfg.html = ' ' + cfg.html;
1220 cls: 'glyphicon glyphicon-' + this.glyphicon
1225 cfg.html = ' ' + cfg.html;
1230 cls: 'fa fas fa-' + this.fa
1240 // cfg.cls='btn roo-button';
1244 var value = cfg.html;
1249 cls: 'glyphicon glyphicon-' + this.glyphicon,
1256 cls: 'fa fas fa-' + this.fa,
1261 var bw = this.badge_weight.length ? this.badge_weight :
1262 (this.weight.length ? this.weight : 'secondary');
1263 bw = bw == 'default' ? 'secondary' : bw;
1269 cls: 'badge badge-' + bw,
1278 cfg.cls += ' dropdown';
1279 cfg.html = typeof(cfg.html) != 'undefined' ?
1280 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1283 if (cfg.tag !== 'a' && this.href !== '') {
1284 throw "Tag must be a to set href.";
1285 } else if (this.href.length > 0) {
1286 cfg.href = this.href;
1289 if(this.removeClass){
1294 cfg.target = this.target;
1299 initEvents: function() {
1300 // Roo.log('init events?');
1301 // Roo.log(this.el.dom);
1304 if (typeof (this.menu) != 'undefined') {
1305 this.menu.parentType = this.xtype;
1306 this.menu.triggerEl = this.el;
1307 this.addxtype(Roo.apply({}, this.menu));
1311 if (this.el.hasClass('roo-button')) {
1312 this.el.on('click', this.onClick, this);
1313 this.el.on('dblclick', this.onDblClick, this);
1315 this.el.select('.roo-button').on('click', this.onClick, this);
1316 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1320 if(this.removeClass){
1321 this.el.on('click', this.onClick, this);
1324 if (this.group === true) {
1325 if (this.pressed === false || this.pressed === true) {
1328 this.pressed = false;
1329 this.setActive(this.pressed);
1334 this.el.enableDisplayMode();
1337 onClick : function(e)
1339 if (this.disabled) {
1343 Roo.log('button on click ');
1344 if(this.preventDefault){
1353 this.setActive(true);
1354 var pi = this.parent().items;
1355 for (var i = 0;i < pi.length;i++) {
1356 if (this == pi[i]) {
1359 if (pi[i].el.hasClass('roo-button')) {
1360 pi[i].setActive(false);
1363 this.fireEvent('click', this, e);
1367 if (this.pressed === true || this.pressed === false) {
1368 this.toggleActive(e);
1372 this.fireEvent('click', this, e);
1374 onDblClick: function(e)
1376 if (this.disabled) {
1379 if(this.preventDefault){
1382 this.fireEvent('dblclick', this, e);
1385 * Enables this button
1389 this.disabled = false;
1390 this.el.removeClass('disabled');
1391 this.el.dom.removeAttribute("disabled");
1395 * Disable this button
1397 disable : function()
1399 this.disabled = true;
1400 this.el.addClass('disabled');
1401 this.el.attr("disabled", "disabled")
1404 * sets the active state on/off,
1405 * @param {Boolean} state (optional) Force a particular state
1407 setActive : function(v) {
1409 this.el[v ? 'addClass' : 'removeClass']('active');
1413 * toggles the current active state
1415 toggleActive : function(e)
1417 this.setActive(!this.pressed); // this modifies pressed...
1418 this.fireEvent('toggle', this, e, this.pressed);
1421 * get the current active state
1422 * @return {boolean} true if it's active
1424 isActive : function()
1426 return this.el.hasClass('active');
1429 * set the text of the first selected button
1431 setText : function(str)
1433 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1436 * get the text of the first selected button
1438 getText : function()
1440 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1443 setWeight : function(str)
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1448 var outline = this.outline ? 'outline-' : '';
1449 if (str == 'default') {
1450 this.el.addClass('btn-default btn-outline-secondary');
1453 this.el.addClass('btn-' + outline + str);
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1460 Roo.bootstrap.Button.weights = [
1480 * @class Roo.bootstrap.Column
1481 * @extends Roo.bootstrap.Component
1482 * @children Roo.bootstrap.Component
1483 * Bootstrap Column class
1484 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1494 * @cfg {Boolean} hidden (true|false) hide the element
1495 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496 * @cfg {String} fa (ban|check|...) font awesome icon
1497 * @cfg {Number} fasize (1|2|....) font awsome size
1499 * @cfg {String} icon (info-sign|check|...) glyphicon name
1501 * @cfg {String} html content of column.
1504 * Create a new Column
1505 * @param {Object} config The config object
1508 Roo.bootstrap.Column = function(config){
1509 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1530 getAutoCreate : function(){
1531 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1539 var sizes = ['xs','sm','md','lg'];
1540 sizes.map(function(size ,ix){
1541 //Roo.log( size + ':' + settings[size]);
1543 if (settings[size+'off'] !== false) {
1544 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1547 if (settings[size] === false) {
1551 if (!settings[size]) { // 0 = hidden
1552 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1554 for (var i = ix; i > -1; i--) {
1555 cfg.cls += ' d-' + sizes[i] + '-none';
1561 cfg.cls += ' col-' + size + '-' + settings[size] + (
1562 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1568 cfg.cls += ' hidden';
1571 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572 cfg.cls +=' alert alert-' + this.alert;
1576 if (this.html.length) {
1577 cfg.html = this.html;
1581 if (this.fasize > 1) {
1582 fasize = ' fa-' + this.fasize + 'x';
1584 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1589 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1608 * @class Roo.bootstrap.Container
1609 * @extends Roo.bootstrap.Component
1611 * @children Roo.bootstrap.Component
1612 * Bootstrap Container class
1613 * @cfg {Boolean} jumbotron is it a jumbotron element
1614 * @cfg {String} html content of element
1615 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1617 * @cfg {String} header content of header (for panel)
1618 * @cfg {String} footer content of footer (for panel)
1619 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620 * @cfg {String} tag (header|aside|section) type of HTML tag.
1621 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622 * @cfg {String} fa font awesome icon
1623 * @cfg {String} icon (info-sign|check|...) glyphicon name
1624 * @cfg {Boolean} hidden (true|false) hide the element
1625 * @cfg {Boolean} expandable (true|false) default false
1626 * @cfg {Boolean} expanded (true|false) default true
1627 * @cfg {String} rheader contet on the right of header
1628 * @cfg {Boolean} clickable (true|false) default false
1632 * Create a new Container
1633 * @param {Object} config The config object
1636 Roo.bootstrap.Container = function(config){
1637 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1643 * After the panel has been expand
1645 * @param {Roo.bootstrap.Container} this
1650 * After the panel has been collapsed
1652 * @param {Roo.bootstrap.Container} this
1657 * When a element is chick
1658 * @param {Roo.bootstrap.Container} this
1659 * @param {Roo.EventObject} e
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1683 getChildContainer : function() {
1689 if (this.panel.length) {
1690 return this.el.select('.panel-body',true).first();
1697 getAutoCreate : function(){
1700 tag : this.tag || 'div',
1704 if (this.jumbotron) {
1705 cfg.cls = 'jumbotron';
1710 // - this is applied by the parent..
1712 // cfg.cls = this.cls + '';
1715 if (this.sticky.length) {
1717 var bd = Roo.get(document.body);
1718 if (!bd.hasClass('bootstrap-sticky')) {
1719 bd.addClass('bootstrap-sticky');
1720 Roo.select('html',true).setStyle('height', '100%');
1723 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1727 if (this.well.length) {
1728 switch (this.well) {
1731 cfg.cls +=' well well-' +this.well;
1740 cfg.cls += ' hidden';
1744 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745 cfg.cls +=' alert alert-' + this.alert;
1750 if (this.panel.length) {
1751 cfg.cls += ' panel panel-' + this.panel;
1753 if (this.header.length) {
1757 if(this.expandable){
1759 cfg.cls = cfg.cls + ' expandable';
1763 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1771 cls : 'panel-title',
1772 html : (this.expandable ? ' ' : '') + this.header
1776 cls: 'panel-header-right',
1782 cls : 'panel-heading',
1783 style : this.expandable ? 'cursor: pointer' : '',
1791 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1796 if (this.footer.length) {
1798 cls : 'panel-footer',
1807 body.html = this.html || cfg.html;
1808 // prefix with the icons..
1810 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1813 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1818 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819 cfg.cls = 'container';
1825 initEvents: function()
1827 if(this.expandable){
1828 var headerEl = this.headerEl();
1831 headerEl.on('click', this.onToggleClick, this);
1836 this.el.on('click', this.onClick, this);
1841 onToggleClick : function()
1843 var headerEl = this.headerEl();
1859 if(this.fireEvent('expand', this)) {
1861 this.expanded = true;
1863 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1865 this.el.select('.panel-body',true).first().removeClass('hide');
1867 var toggleEl = this.toggleEl();
1873 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1878 collapse : function()
1880 if(this.fireEvent('collapse', this)) {
1882 this.expanded = false;
1884 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885 this.el.select('.panel-body',true).first().addClass('hide');
1887 var toggleEl = this.toggleEl();
1893 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1897 toggleEl : function()
1899 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1903 return this.el.select('.panel-heading .fa',true).first();
1906 headerEl : function()
1908 if(!this.el || !this.panel.length || !this.header.length){
1912 return this.el.select('.panel-heading',true).first()
1917 if(!this.el || !this.panel.length){
1921 return this.el.select('.panel-body',true).first()
1924 titleEl : function()
1926 if(!this.el || !this.panel.length || !this.header.length){
1930 return this.el.select('.panel-title',true).first();
1933 setTitle : function(v)
1935 var titleEl = this.titleEl();
1941 titleEl.dom.innerHTML = v;
1944 getTitle : function()
1947 var titleEl = this.titleEl();
1953 return titleEl.dom.innerHTML;
1956 setRightTitle : function(v)
1958 var t = this.el.select('.panel-header-right',true).first();
1964 t.dom.innerHTML = v;
1967 onClick : function(e)
1971 this.fireEvent('click', this, e);
1976 * @class Roo.bootstrap.Card
1977 * @extends Roo.bootstrap.Component
1978 * @children Roo.bootstrap.Component
1980 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1983 * possible... may not be implemented..
1984 * @cfg {String} header_image src url of image.
1985 * @cfg {String|Object} header
1986 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1989 * @cfg {String} title
1990 * @cfg {String} subtitle
1991 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992 * @cfg {String} footer
1994 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1996 * @cfg {String} margin (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2004 * @cfg {String} padding (0|1|2|3|4|5)
2005 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007 * @cfg {String} padding_left (0|1|2|3|4|5)
2008 * @cfg {String} padding_right (0|1|2|3|4|5)
2009 * @cfg {String} padding_x (0|1|2|3|4|5)
2010 * @cfg {String} padding_y (0|1|2|3|4|5)
2012 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018 * @config {Boolean} dragable if this card can be dragged.
2019 * @config {String} drag_group group for drag
2020 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2021 * @config {String} drop_group group for drag
2023 * @config {Boolean} collapsable can the body be collapsed.
2024 * @config {Boolean} collapsed is the body collapsed when rendered...
2025 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026 * @config {Boolean} rotated is the body rotated when rendered...
2029 * Create a new Container
2030 * @param {Object} config The config object
2033 Roo.bootstrap.Card = function(config){
2034 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2040 * When a element a card is dropped
2041 * @param {Roo.bootstrap.Card} this
2044 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045 * @param {String} position 'above' or 'below'
2046 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2052 * When a element a card is rotate
2053 * @param {Roo.bootstrap.Card} this
2054 * @param {Roo.Element} n the node being dropped?
2055 * @param {Boolean} rotate status
2060 * When a card element is dragged over ready to drop (return false to block dropable)
2061 * @param {Roo.bootstrap.Card} this
2062 * @param {Object} data from dragdrop
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2075 margin: '', /// may be better in component?
2105 collapsable : false,
2114 childContainer : false,
2115 dropEl : false, /// the dom placeholde element that indicates drop location.
2116 containerEl: false, // body container
2117 bodyEl: false, // card-body
2118 headerContainerEl : false, //
2120 header_imageEl : false,
2123 layoutCls : function()
2127 Roo.log(this.margin_bottom.length);
2128 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2131 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2134 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2139 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2145 // more generic support?
2153 // Roo.log("Call onRender: " + this.xtype);
2154 /* We are looking at something like this.
2156 <img src="..." class="card-img-top" alt="...">
2157 <div class="card-body">
2158 <h5 class="card-title">Card title</h5>
2159 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2161 >> this bit is really the body...
2162 <div> << we will ad dthis in hopefully it will not break shit.
2164 ** card text does not actually have any styling...
2166 <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>
2169 <a href="#" class="card-link">Card link</a>
2172 <div class="card-footer">
2173 <small class="text-muted">Last updated 3 mins ago</small>
2177 getAutoCreate : function(){
2185 if (this.weight.length && this.weight != 'light') {
2186 cfg.cls += ' text-white';
2188 cfg.cls += ' text-dark'; // need as it's nested..
2190 if (this.weight.length) {
2191 cfg.cls += ' bg-' + this.weight;
2194 cfg.cls += ' ' + this.layoutCls();
2197 var hdr_ctr = false;
2198 if (this.header.length) {
2200 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2215 if (this.collapsable) {
2218 cls : 'd-block user-select-none',
2222 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2227 hdr.cn.push(hdr_ctr);
2232 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2237 if (this.header_image.length) {
2240 cls : 'card-img-top',
2241 src: this.header_image // escape?
2246 cls : 'card-img-top d-none'
2252 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2256 if (this.collapsable || this.rotateable) {
2259 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2266 if (this.title.length) {
2270 src: this.title // escape?
2274 if (this.subtitle.length) {
2278 src: this.subtitle // escape?
2284 cls : 'roo-card-body-ctr'
2287 if (this.html.length) {
2293 // fixme ? handle objects?
2295 if (this.footer.length) {
2298 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2303 cfg.cn.push({cls : 'card-footer d-none'});
2312 getCardHeader : function()
2314 var ret = this.el.select('.card-header',true).first();
2315 if (ret.hasClass('d-none')) {
2316 ret.removeClass('d-none');
2321 getCardFooter : function()
2323 var ret = this.el.select('.card-footer',true).first();
2324 if (ret.hasClass('d-none')) {
2325 ret.removeClass('d-none');
2330 getCardImageTop : function()
2332 var ret = this.header_imageEl;
2333 if (ret.hasClass('d-none')) {
2334 ret.removeClass('d-none');
2340 getChildContainer : function()
2346 return this.el.select('.roo-card-body-ctr',true).first();
2349 initEvents: function()
2351 this.bodyEl = this.el.select('.card-body',true).first();
2352 this.containerEl = this.getChildContainer();
2354 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355 containerScroll: true,
2356 ddGroup: this.drag_group || 'default_card_drag_group'
2358 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2360 if (this.dropable) {
2361 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362 containerScroll: true,
2363 ddGroup: this.drop_group || 'default_card_drag_group'
2365 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2372 if (this.collapsable) {
2373 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2375 if (this.rotateable) {
2376 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2378 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2380 this.footerEl = this.el.select('.card-footer',true).first();
2381 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383 this.headerEl = this.el.select('.card-header',true).first();
2386 this.el.addClass('roo-card-rotated');
2387 this.fireEvent('rotate', this, true);
2389 this.header_imageEl = this.el.select('.card-img-top',true).first();
2390 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2393 getDragData : function(e)
2395 var target = this.getEl();
2397 //this.handleSelection(e);
2402 nodes: this.getEl(),
2407 dragData.ddel = target.dom ; // the div element
2408 Roo.log(target.getWidth( ));
2409 dragData.ddel.style.width = target.getWidth() + 'px';
2416 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2417 * whole Element becomes the target, and this causes the drop gesture to append.
2419 * Returns an object:
2422 position : 'below' or 'above'
2423 card : relateive to card OBJECT (or true for no cards listed)
2424 items_n : relative to nth item in list
2425 card_n : relative to nth card in list
2430 getTargetFromEvent : function(e, dragged_card_el)
2432 var target = e.getTarget();
2433 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434 target = target.parentNode;
2445 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446 // see if target is one of the 'cards'...
2449 //Roo.log(this.items.length);
2452 var last_card_n = 0;
2454 for (var i = 0;i< this.items.length;i++) {
2456 if (!this.items[i].el.hasClass('card')) {
2459 pos = this.getDropPoint(e, this.items[i].el.dom);
2461 cards_len = ret.cards.length;
2462 //Roo.log(this.items[i].el.dom.id);
2463 ret.cards.push(this.items[i]);
2465 if (ret.card_n < 0 && pos == 'above') {
2466 ret.position = cards_len > 0 ? 'below' : pos;
2467 ret.items_n = i > 0 ? i - 1 : 0;
2468 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2469 ret.card = ret.cards[ret.card_n];
2472 if (!ret.cards.length) {
2474 ret.position = 'below';
2478 // could not find a card.. stick it at the end..
2479 if (ret.card_n < 0) {
2480 ret.card_n = last_card_n;
2481 ret.card = ret.cards[last_card_n];
2482 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483 ret.position = 'below';
2486 if (this.items[ret.items_n].el == dragged_card_el) {
2490 if (ret.position == 'below') {
2491 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2493 if (card_after && card_after.el == dragged_card_el) {
2500 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2502 if (card_before && card_before.el == dragged_card_el) {
2509 onNodeEnter : function(n, dd, e, data){
2512 onNodeOver : function(n, dd, e, data)
2515 var target_info = this.getTargetFromEvent(e,data.source.el);
2516 if (target_info === false) {
2517 this.dropPlaceHolder('hide');
2520 Roo.log(['getTargetFromEvent', target_info ]);
2523 if (this.fireEvent('cardover', this, [ data ]) === false) {
2527 this.dropPlaceHolder('show', target_info,data);
2531 onNodeOut : function(n, dd, e, data){
2532 this.dropPlaceHolder('hide');
2535 onNodeDrop : function(n, dd, e, data)
2538 // call drop - return false if
2540 // this could actually fail - if the Network drops..
2541 // we will ignore this at present..- client should probably reload
2542 // the whole set of cards if stuff like that fails.
2545 var info = this.getTargetFromEvent(e,data.source.el);
2546 if (info === false) {
2549 this.dropPlaceHolder('hide');
2553 this.acceptCard(data.source, info.position, info.card, info.items_n);
2557 firstChildCard : function()
2559 for (var i = 0;i< this.items.length;i++) {
2561 if (!this.items[i].el.hasClass('card')) {
2564 return this.items[i];
2566 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2571 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2573 acceptCard : function(move_card, position, next_to_card )
2575 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2579 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2581 move_card.parent().removeCard(move_card);
2584 var dom = move_card.el.dom;
2585 dom.style.width = ''; // clear with - which is set by drag.
2587 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588 var cardel = next_to_card.el.dom;
2590 if (position == 'above' ) {
2591 cardel.parentNode.insertBefore(dom, cardel);
2592 } else if (cardel.nextSibling) {
2593 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2595 cardel.parentNode.append(dom);
2598 // card container???
2599 this.containerEl.dom.append(dom);
2602 //FIXME HANDLE card = true
2604 // add this to the correct place in items.
2606 // remove Card from items.
2609 if (this.items.length) {
2611 //Roo.log([info.items_n, info.position, this.items.length]);
2612 for (var i =0; i < this.items.length; i++) {
2613 if (i == to_items_n && position == 'above') {
2614 nitems.push(move_card);
2616 nitems.push(this.items[i]);
2617 if (i == to_items_n && position == 'below') {
2618 nitems.push(move_card);
2621 this.items = nitems;
2622 Roo.log(this.items);
2624 this.items.push(move_card);
2627 move_card.parentId = this.id;
2633 removeCard : function(c)
2635 this.items = this.items.filter(function(e) { return e != c });
2638 dom.parentNode.removeChild(dom);
2639 dom.style.width = ''; // clear with - which is set by drag.
2644 /** Decide whether to drop above or below a View node. */
2645 getDropPoint : function(e, n, dd)
2650 if (n == this.containerEl.dom) {
2653 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654 var c = t + (b - t) / 2;
2655 var y = Roo.lib.Event.getPageY(e);
2662 onToggleCollapse : function(e)
2664 if (this.collapsed) {
2665 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666 this.collapsableEl.addClass('show');
2667 this.collapsed = false;
2670 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671 this.collapsableEl.removeClass('show');
2672 this.collapsed = true;
2677 onToggleRotate : function(e)
2679 this.collapsableEl.removeClass('show');
2680 this.footerEl.removeClass('d-none');
2681 this.el.removeClass('roo-card-rotated');
2682 this.el.removeClass('d-none');
2685 this.collapsableEl.addClass('show');
2686 this.rotated = false;
2687 this.fireEvent('rotate', this, this.rotated);
2690 this.el.addClass('roo-card-rotated');
2691 this.footerEl.addClass('d-none');
2692 this.el.select('.roo-collapsable').removeClass('show');
2694 this.rotated = true;
2695 this.fireEvent('rotate', this, this.rotated);
2699 dropPlaceHolder: function (action, info, data)
2701 if (this.dropEl === false) {
2702 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2706 this.dropEl.removeClass(['d-none', 'd-block']);
2707 if (action == 'hide') {
2709 this.dropEl.addClass('d-none');
2712 // FIXME - info.card == true!!!
2713 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2715 if (info.card !== true) {
2716 var cardel = info.card.el.dom;
2718 if (info.position == 'above') {
2719 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720 } else if (cardel.nextSibling) {
2721 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2723 cardel.parentNode.append(this.dropEl.dom);
2726 // card container???
2727 this.containerEl.dom.append(this.dropEl.dom);
2730 this.dropEl.addClass('d-block roo-card-dropzone');
2732 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2739 setHeaderText: function(html)
2742 if (this.headerContainerEl) {
2743 this.headerContainerEl.dom.innerHTML = html;
2746 onHeaderImageLoad : function(ev, he)
2748 if (!this.header_image_fit_square) {
2752 var hw = he.naturalHeight / he.naturalWidth;
2755 //var w = he.dom.naturalWidth;
2758 he.style.position = 'relative';
2760 var nw = (ww * (1/hw));
2761 Roo.get(he).setSize( ww * (1/hw), ww);
2762 he.style.left = ((ww - nw)/ 2) + 'px';
2763 he.style.position = 'relative';
2774 * Card header - holder for the card header elements.
2779 * @class Roo.bootstrap.CardHeader
2780 * @extends Roo.bootstrap.Element
2781 * @parent Roo.bootstrap.Card
2782 * @children Roo.bootstrap.Component
2783 * Bootstrap CardHeader class
2785 * Create a new Card Header - that you can embed children into
2786 * @param {Object} config The config object
2789 Roo.bootstrap.CardHeader = function(config){
2790 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2796 container_method : 'getCardHeader'
2809 * Card footer - holder for the card footer elements.
2814 * @class Roo.bootstrap.CardFooter
2815 * @extends Roo.bootstrap.Element
2816 * @parent Roo.bootstrap.Card
2817 * @children Roo.bootstrap.Component
2818 * Bootstrap CardFooter class
2821 * Create a new Card Footer - that you can embed children into
2822 * @param {Object} config The config object
2825 Roo.bootstrap.CardFooter = function(config){
2826 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2832 container_method : 'getCardFooter'
2845 * Card header - holder for the card header elements.
2850 * @class Roo.bootstrap.CardImageTop
2851 * @extends Roo.bootstrap.Element
2852 * @parent Roo.bootstrap.Card
2853 * @children Roo.bootstrap.Component
2854 * Bootstrap CardImageTop class
2857 * Create a new Card Image Top container
2858 * @param {Object} config The config object
2861 Roo.bootstrap.CardImageTop = function(config){
2862 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2868 container_method : 'getCardImageTop'
2883 * @class Roo.bootstrap.ButtonUploader
2884 * @extends Roo.bootstrap.Button
2885 * Bootstrap Button Uploader class - it's a button which when you add files to it
2888 * @cfg {Number} errorTimeout default 3000
2889 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2890 * @cfg {Array} html The button text.
2891 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2894 * Create a new CardUploader
2895 * @param {Object} config The config object
2898 Roo.bootstrap.ButtonUploader = function(config){
2902 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2908 * @event beforeselect
2909 * When button is pressed, before show upload files dialog is shown
2910 * @param {Roo.bootstrap.UploaderButton} this
2913 'beforeselect' : true,
2915 * @event fired when files have been selected,
2916 * When a the download link is clicked
2917 * @param {Roo.bootstrap.UploaderButton} this
2918 * @param {Array} Array of files that have been uploaded
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2928 errorTimeout : 3000,
2932 fileCollection : false,
2937 getAutoCreate : function()
2942 cls : 'd-none roo-card-upload-selector'
2945 if (this.multiple) {
2946 im.multiple = 'multiple';
2952 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2962 initEvents : function()
2965 Roo.bootstrap.Button.prototype.initEvents.call(this);
2971 this.urlAPI = (window.createObjectURL && window) ||
2972 (window.URL && URL.revokeObjectURL && URL) ||
2973 (window.webkitURL && webkitURL);
2978 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2980 this.selectorEl.on('change', this.onFileSelected, this);
2987 onClick : function(e)
2991 if ( this.fireEvent('beforeselect', this) === false) {
2995 this.selectorEl.dom.click();
2999 onFileSelected : function(e)
3003 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3006 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007 this.selectorEl.dom.value = '';// hopefully reset..
3009 this.fireEvent('uploaded', this, files );
3017 * addCard - add an Attachment to the uploader
3018 * @param data - the data about the image to upload
3022 title : "Title of file",
3023 is_uploaded : false,
3024 src : "http://.....",
3025 srcfile : { the File upload object },
3026 mimetype : file.type,
3029 .. any other data...
3054 * @class Roo.bootstrap.Img
3055 * @extends Roo.bootstrap.Component
3056 * Bootstrap Img class
3057 * @cfg {Boolean} imgResponsive false | true
3058 * @cfg {String} border rounded | circle | thumbnail
3059 * @cfg {String} src image source
3060 * @cfg {String} alt image alternative text
3061 * @cfg {String} href a tag href
3062 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063 * @cfg {String} xsUrl xs image source
3064 * @cfg {String} smUrl sm image source
3065 * @cfg {String} mdUrl md image source
3066 * @cfg {String} lgUrl lg image source
3067 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3070 * Create a new Input
3071 * @param {Object} config The config object
3074 Roo.bootstrap.Img = function(config){
3075 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3081 * The img click event for the img.
3082 * @param {Roo.EventObject} e
3087 * The when any image loads
3088 * @param {Roo.EventObject} e
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3096 imgResponsive: true,
3105 backgroundContain : false,
3107 getAutoCreate : function()
3109 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110 return this.createSingleImg();
3115 cls: 'roo-image-responsive-group',
3120 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3122 if(!_this[size + 'Url']){
3128 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129 html: _this.html || cfg.html,
3130 src: _this[size + 'Url']
3133 img.cls += ' roo-image-responsive-' + size;
3135 var s = ['xs', 'sm', 'md', 'lg'];
3137 s.splice(s.indexOf(size), 1);
3139 Roo.each(s, function(ss){
3140 img.cls += ' hidden-' + ss;
3143 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144 cfg.cls += ' img-' + _this.border;
3148 cfg.alt = _this.alt;
3161 a.target = _this.target;
3165 cfg.cn.push((_this.href) ? a : img);
3172 createSingleImg : function()
3176 cls: (this.imgResponsive) ? 'img-responsive' : '',
3178 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3181 if (this.backgroundContain) {
3182 cfg.cls += ' background-contain';
3185 cfg.html = this.html || cfg.html;
3187 if (this.backgroundContain) {
3188 cfg.style="background-image: url(" + this.src + ')';
3190 cfg.src = this.src || cfg.src;
3193 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194 cfg.cls += ' img-' + this.border;
3211 a.target = this.target;
3216 return (this.href) ? a : cfg;
3219 initEvents: function()
3222 this.el.on('click', this.onClick, this);
3224 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225 this.el.on('load', this.onImageLoad, this);
3227 // not sure if this works.. not tested
3228 this.el.select('img', true).on('load', this.onImageLoad, this);
3233 onClick : function(e)
3235 Roo.log('img onclick');
3236 this.fireEvent('click', this, e);
3238 onImageLoad: function(e)
3240 Roo.log('img load');
3241 this.fireEvent('load', this, e);
3245 * Sets the url of the image - used to update it
3246 * @param {String} url the url of the image
3249 setSrc : function(url)
3253 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254 if (this.backgroundContain) {
3255 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3257 this.el.dom.src = url;
3262 this.el.select('img', true).first().dom.src = url;
3278 * @class Roo.bootstrap.Link
3279 * @extends Roo.bootstrap.Component
3280 * @children Roo.bootstrap.Component
3281 * Bootstrap Link Class (eg. '<a href>')
3283 * @cfg {String} alt image alternative text
3284 * @cfg {String} href a tag href
3285 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286 * @cfg {String} html the content of the link.
3287 * @cfg {String} anchor name for the anchor link
3288 * @cfg {String} fa - favicon
3290 * @cfg {Boolean} preventDefault (true | false) default false
3294 * Create a new Input
3295 * @param {Object} config The config object
3298 Roo.bootstrap.Link = function(config){
3299 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3305 * The img click event for the img.
3306 * @param {Roo.EventObject} e
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3316 preventDefault: false,
3322 getAutoCreate : function()
3324 var html = this.html || '';
3326 if (this.fa !== false) {
3327 html = '<i class="fa fa-' + this.fa + '"></i>';
3332 // anchor's do not require html/href...
3333 if (this.anchor === false) {
3335 cfg.href = this.href || '#';
3337 cfg.name = this.anchor;
3338 if (this.html !== false || this.fa !== false) {
3341 if (this.href !== false) {
3342 cfg.href = this.href;
3346 if(this.alt !== false){
3351 if(this.target !== false) {
3352 cfg.target = this.target;
3358 initEvents: function() {
3360 if(!this.href || this.preventDefault){
3361 this.el.on('click', this.onClick, this);
3365 onClick : function(e)
3367 if(this.preventDefault){
3370 //Roo.log('img onclick');
3371 this.fireEvent('click', this, e);
3384 * @class Roo.bootstrap.Header
3385 * @extends Roo.bootstrap.Component
3386 * @children Roo.bootstrap.Component
3387 * Bootstrap Header class
3390 * @cfg {String} html content of header
3391 * @cfg {Number} level (1|2|3|4|5|6) default 1
3394 * Create a new Header
3395 * @param {Object} config The config object
3399 Roo.bootstrap.Header = function(config){
3400 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3411 getAutoCreate : function(){
3416 tag: 'h' + (1 *this.level),
3417 html: this.html || ''
3428 * @class Roo.bootstrap.MenuMgr
3430 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3433 Roo.bootstrap.menu.Manager = function(){
3434 var menus, active, groups = {}, attached = false, lastShow = new Date();
3436 // private - called when first menu is created
3439 active = new Roo.util.MixedCollection();
3440 Roo.get(document).addKeyListener(27, function(){
3441 if(active.length > 0){
3449 if(active && active.length > 0){
3450 var c = active.clone();
3460 if(active.length < 1){
3461 Roo.get(document).un("mouseup", onMouseDown);
3469 var last = active.last();
3470 lastShow = new Date();
3473 Roo.get(document).on("mouseup", onMouseDown);
3478 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479 m.parentMenu.activeChild = m;
3480 }else if(last && last.isVisible()){
3481 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3486 function onBeforeHide(m){
3488 m.activeChild.hide();
3490 if(m.autoHideTimer){
3491 clearTimeout(m.autoHideTimer);
3492 delete m.autoHideTimer;
3497 function onBeforeShow(m){
3498 var pm = m.parentMenu;
3499 if(!pm && !m.allowOtherMenus){
3501 }else if(pm && pm.activeChild && active != m){
3502 pm.activeChild.hide();
3506 // private this should really trigger on mouseup..
3507 function onMouseDown(e){
3508 Roo.log("on Mouse Up");
3510 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511 Roo.log("MenuManager hideAll");
3520 function onBeforeCheck(mi, state){
3522 var g = groups[mi.group];
3523 for(var i = 0, l = g.length; i < l; i++){
3525 g[i].setChecked(false);
3534 * Hides all menus that are currently visible
3536 hideAll : function(){
3541 register : function(menu){
3545 menus[menu.id] = menu;
3546 menu.on("beforehide", onBeforeHide);
3547 menu.on("hide", onHide);
3548 menu.on("beforeshow", onBeforeShow);
3549 menu.on("show", onShow);
3551 if(g && menu.events["checkchange"]){
3555 groups[g].push(menu);
3556 menu.on("checkchange", onCheck);
3561 * Returns a {@link Roo.menu.Menu} object
3562 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563 * be used to generate and return a new Menu instance.
3565 get : function(menu){
3566 if(typeof menu == "string"){ // menu id
3568 }else if(menu.events){ // menu instance
3571 /*else if(typeof menu.length == 'number'){ // array of menu items?
3572 return new Roo.bootstrap.Menu({items:menu});
3573 }else{ // otherwise, must be a config
3574 return new Roo.bootstrap.Menu(menu);
3581 unregister : function(menu){
3582 delete menus[menu.id];
3583 menu.un("beforehide", onBeforeHide);
3584 menu.un("hide", onHide);
3585 menu.un("beforeshow", onBeforeShow);
3586 menu.un("show", onShow);
3588 if(g && menu.events["checkchange"]){
3589 groups[g].remove(menu);
3590 menu.un("checkchange", onCheck);
3595 registerCheckable : function(menuItem){
3596 var g = menuItem.group;
3601 groups[g].push(menuItem);
3602 menuItem.on("beforecheckchange", onBeforeCheck);
3607 unregisterCheckable : function(menuItem){
3608 var g = menuItem.group;
3610 groups[g].remove(menuItem);
3611 menuItem.un("beforecheckchange", onBeforeCheck);
3617 * @class Roo.bootstrap.menu.Menu
3618 * @extends Roo.bootstrap.Component
3620 * @children Roo.bootstrap.menu.Item
3622 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3624 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625 * @cfg {bool} hidden if the menu should be hidden when rendered.
3626 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3627 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3628 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3629 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3633 * @param {Object} config The config objectQ
3637 Roo.bootstrap.menu.Menu = function(config){
3639 if (config.type == 'treeview') {
3640 // normally menu's are drawn attached to the document to handle layering etc..
3641 // however treeview (used by the docs menu is drawn into the parent element)
3642 this.container_method = 'getChildContainer';
3645 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646 if (this.registerMenu && this.type != 'treeview') {
3647 Roo.bootstrap.menu.Manager.register(this);
3654 * Fires before this menu is displayed (return false to block)
3655 * @param {Roo.menu.Menu} this
3660 * Fires before this menu is hidden (return false to block)
3661 * @param {Roo.menu.Menu} this
3666 * Fires after this menu is displayed
3667 * @param {Roo.menu.Menu} this
3672 * Fires after this menu is hidden
3673 * @param {Roo.menu.Menu} this
3678 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679 * @param {Roo.menu.Menu} this
3680 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681 * @param {Roo.EventObject} e
3686 * Fires when the mouse is hovering over this menu
3687 * @param {Roo.menu.Menu} this
3688 * @param {Roo.EventObject} e
3689 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694 * Fires when the mouse exits this menu
3695 * @param {Roo.menu.Menu} this
3696 * @param {Roo.EventObject} e
3697 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702 * Fires when a menu item contained in this menu is clicked
3703 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704 * @param {Roo.EventObject} e
3708 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3715 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3718 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3720 registerMenu : true,
3722 menuItems :false, // stores the menu items..
3732 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3734 hideTrigger : false,
3739 getChildContainer : function() {
3743 getAutoCreate : function(){
3745 //if (['right'].indexOf(this.align)!==-1) {
3746 // cfg.cn[1].cls += ' pull-right'
3751 cls : 'dropdown-menu shadow' ,
3752 style : 'z-index:1000'
3756 if (this.type === 'submenu') {
3757 cfg.cls = 'submenu active';
3759 if (this.type === 'treeview') {
3760 cfg.cls = 'treeview-menu';
3765 initEvents : function() {
3767 // Roo.log("ADD event");
3768 // Roo.log(this.triggerEl.dom);
3769 if (this.triggerEl) {
3771 this.triggerEl.on('click', this.onTriggerClick, this);
3773 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3775 if (!this.hideTrigger) {
3776 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777 // dropdown toggle on the 'a' in BS4?
3778 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3780 this.triggerEl.addClass('dropdown-toggle');
3786 this.el.on('touchstart' , this.onTouch, this);
3788 this.el.on('click' , this.onClick, this);
3790 this.el.on("mouseover", this.onMouseOver, this);
3791 this.el.on("mouseout", this.onMouseOut, this);
3795 findTargetItem : function(e)
3797 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3801 //Roo.log(t); Roo.log(t.id);
3803 //Roo.log(this.menuitems);
3804 return this.menuitems.get(t.id);
3806 //return this.items.get(t.menuItemId);
3812 onTouch : function(e)
3814 Roo.log("menu.onTouch");
3815 //e.stopEvent(); this make the user popdown broken
3819 onClick : function(e)
3821 Roo.log("menu.onClick");
3823 var t = this.findTargetItem(e);
3824 if(!t || t.isContainer){
3829 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3830 if(t == this.activeItem && t.shouldDeactivate(e)){
3831 this.activeItem.deactivate();
3832 delete this.activeItem;
3836 this.setActiveItem(t, true);
3844 Roo.log('pass click event');
3848 this.fireEvent("click", this, t, e);
3852 if(!t.href.length || t.href == '#'){
3853 (function() { _this.hide(); }).defer(100);
3858 onMouseOver : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t.canActivate && !t.disabled){
3863 // this.setActiveItem(t, true);
3867 this.fireEvent("mouseover", this, e, t);
3869 isVisible : function(){
3870 return !this.hidden;
3872 onMouseOut : function(e){
3873 var t = this.findTargetItem(e);
3876 // if(t == this.activeItem && t.shouldDeactivate(e)){
3877 // this.activeItem.deactivate();
3878 // delete this.activeItem;
3881 this.fireEvent("mouseout", this, e, t);
3886 * Displays this menu relative to another element
3887 * @param {String/HTMLElement/Roo.Element} element The element to align to
3888 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889 * the element (defaults to this.defaultAlign)
3890 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3892 show : function(el, pos, parentMenu)
3894 if (false === this.fireEvent("beforeshow", this)) {
3895 Roo.log("show canceled");
3898 this.parentMenu = parentMenu;
3902 this.el.addClass('show'); // show otherwise we do not know how big we are..
3904 var xy = this.el.getAlignToXY(el, pos);
3906 // bl-tl << left align below
3907 // tl-bl << left align
3909 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910 // if it goes to far to the right.. -> align left.
3911 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3914 // was left align - go right?
3915 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3918 // goes down the bottom
3919 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3921 var a = this.align.replace('?', '').split('-');
3922 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3926 this.showAt( xy , parentMenu, false);
3929 * Displays this menu at a specific xy position
3930 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3933 showAt : function(xy, parentMenu, /* private: */_e){
3934 this.parentMenu = parentMenu;
3939 this.fireEvent("beforeshow", this);
3940 //xy = this.el.adjustForConstraints(xy);
3944 this.hideMenuItems();
3945 this.hidden = false;
3946 if (this.triggerEl) {
3947 this.triggerEl.addClass('open');
3950 this.el.addClass('show');
3954 // reassign x when hitting right
3956 // reassign y when hitting bottom
3958 // but the list may align on trigger left or trigger top... should it be a properity?
3960 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3965 this.fireEvent("show", this);
3971 this.doFocus.defer(50, this);
3975 doFocus : function(){
3977 this.focusEl.focus();
3982 * Hides this menu and optionally all parent menus
3983 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3985 hide : function(deep)
3987 if (false === this.fireEvent("beforehide", this)) {
3988 Roo.log("hide canceled");
3991 this.hideMenuItems();
3992 if(this.el && this.isVisible()){
3994 if(this.activeItem){
3995 this.activeItem.deactivate();
3996 this.activeItem = null;
3998 if (this.triggerEl) {
3999 this.triggerEl.removeClass('open');
4002 this.el.removeClass('show');
4004 this.fireEvent("hide", this);
4006 if(deep === true && this.parentMenu){
4007 this.parentMenu.hide(true);
4011 onTriggerClick : function(e)
4013 Roo.log('trigger click');
4015 var target = e.getTarget();
4017 Roo.log(target.nodeName.toLowerCase());
4019 if(target.nodeName.toLowerCase() === 'i'){
4025 onTriggerPress : function(e)
4027 Roo.log('trigger press');
4028 //Roo.log(e.getTarget());
4029 // Roo.log(this.triggerEl.dom);
4031 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032 var pel = Roo.get(e.getTarget());
4033 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034 Roo.log('is treeview or dropdown?');
4038 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4042 if (this.isVisible()) {
4048 this.show(this.triggerEl, this.align, false);
4051 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4058 hideMenuItems : function()
4060 Roo.log("hide Menu Items");
4065 this.el.select('.open',true).each(function(aa) {
4067 aa.removeClass('open');
4071 addxtypeChild : function (tree, cntr) {
4072 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4074 this.menuitems.add(comp);
4086 this.getEl().dom.innerHTML = '';
4087 this.menuitems.clear();
4093 * @class Roo.bootstrap.menu.Item
4094 * @extends Roo.bootstrap.Component
4095 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096 * @parent Roo.bootstrap.menu.Menu
4098 * Bootstrap MenuItem class
4100 * @cfg {String} html the menu label
4101 * @cfg {String} href the link
4102 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104 * @cfg {Boolean} active used on sidebars to highlight active itesm
4105 * @cfg {String} fa favicon to show on left of menu item.
4106 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4110 * Create a new MenuItem
4111 * @param {Object} config The config object
4115 Roo.bootstrap.menu.Item = function(config){
4116 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4121 * The raw click event for the entire grid.
4122 * @param {Roo.bootstrap.menu.Item} this
4123 * @param {Roo.EventObject} e
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4133 preventDefault: false,
4134 isContainer : false,
4138 getAutoCreate : function(){
4140 if(this.isContainer){
4143 cls: 'dropdown-menu-item '
4153 cls : 'dropdown-item',
4158 if (this.fa !== false) {
4161 cls : 'fa fa-' + this.fa
4170 cls: 'dropdown-menu-item',
4173 if (this.parent().type == 'treeview') {
4174 cfg.cls = 'treeview-menu';
4177 cfg.cls += ' active';
4182 anc.href = this.href || cfg.cn[0].href ;
4183 ctag.html = this.html || cfg.cn[0].html ;
4187 initEvents: function()
4189 if (this.parent().type == 'treeview') {
4190 this.el.select('a').on('click', this.onClick, this);
4194 this.menu.parentType = this.xtype;
4195 this.menu.triggerEl = this.el;
4196 this.menu = this.addxtype(Roo.apply({}, this.menu));
4200 onClick : function(e)
4202 Roo.log('item on click ');
4204 if(this.preventDefault){
4207 //this.parent().hideMenuItems();
4209 this.fireEvent('click', this, e);
4223 * @class Roo.bootstrap.menu.Separator
4224 * @extends Roo.bootstrap.Component
4226 * @parent Roo.bootstrap.menu.Menu
4227 * Bootstrap Separator class
4230 * Create a new Separator
4231 * @param {Object} config The config object
4235 Roo.bootstrap.menu.Separator = function(config){
4236 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4241 getAutoCreate : function(){
4244 cls: 'dropdown-divider divider'
4260 * @class Roo.bootstrap.Modal
4261 * @extends Roo.bootstrap.Component
4264 * @children Roo.bootstrap.Component
4265 * Bootstrap Modal class
4266 * @cfg {String} title Title of dialog
4267 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4269 * @cfg {Boolean} specificTitle default false
4270 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272 * @cfg {Boolean} animate default true
4273 * @cfg {Boolean} allow_close default true
4274 * @cfg {Boolean} fitwindow default false
4275 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278 * @cfg {String} size (sm|lg|xl) default empty
4279 * @cfg {Number} max_width set the max width of modal
4280 * @cfg {Boolean} editableTitle can the title be edited
4285 * Create a new Modal Dialog
4286 * @param {Object} config The config object
4289 Roo.bootstrap.Modal = function(config){
4290 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4295 * The raw btnclick event for the button
4296 * @param {Roo.EventObject} e
4301 * Fire when dialog resize
4302 * @param {Roo.bootstrap.Modal} this
4303 * @param {Roo.EventObject} e
4307 * @event titlechanged
4308 * Fire when the editable title has been changed
4309 * @param {Roo.bootstrap.Modal} this
4310 * @param {Roo.EventObject} value
4312 "titlechanged" : true
4315 this.buttons = this.buttons || [];
4318 this.tmpl = Roo.factory(this.tmpl);
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4325 title : 'test dialog',
4335 specificTitle: false,
4337 buttonPosition: 'right',
4359 editableTitle : false,
4361 onRender : function(ct, position)
4363 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4366 var cfg = Roo.apply({}, this.getAutoCreate());
4369 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4371 //if (!cfg.name.length) {
4375 cfg.cls += ' ' + this.cls;
4378 cfg.style = this.style;
4380 this.el = Roo.get(document.body).createChild(cfg, position);
4382 //var type = this.el.dom.type;
4385 if(this.tabIndex !== undefined){
4386 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4389 this.dialogEl = this.el.select('.modal-dialog',true).first();
4390 this.bodyEl = this.el.select('.modal-body',true).first();
4391 this.closeEl = this.el.select('.modal-header .close', true).first();
4392 this.headerEl = this.el.select('.modal-header',true).first();
4393 this.titleEl = this.el.select('.modal-title',true).first();
4394 this.footerEl = this.el.select('.modal-footer',true).first();
4396 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4398 //this.el.addClass("x-dlg-modal");
4400 if (this.buttons.length) {
4401 Roo.each(this.buttons, function(bb) {
4402 var b = Roo.apply({}, bb);
4403 b.xns = b.xns || Roo.bootstrap;
4404 b.xtype = b.xtype || 'Button';
4405 if (typeof(b.listeners) == 'undefined') {
4406 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4409 var btn = Roo.factory(b);
4411 btn.render(this.getButtonContainer());
4415 // render the children.
4418 if(typeof(this.items) != 'undefined'){
4419 var items = this.items;
4422 for(var i =0;i < items.length;i++) {
4423 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427 this.items = nitems;
4429 // where are these used - they used to be body/close/footer
4433 //this.el.addClass([this.fieldClass, this.cls]);
4437 getAutoCreate : function()
4439 // we will default to modal-body-overflow - might need to remove or make optional later.
4441 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4442 html : this.html || ''
4447 cls : 'modal-title',
4451 if(this.specificTitle){ // WTF is this?
4456 if (this.allow_close && Roo.bootstrap.version == 3) {
4466 if (this.editableTitle) {
4468 cls: 'form-control roo-editable-title d-none',
4474 if (this.allow_close && Roo.bootstrap.version == 4) {
4484 if(this.size.length){
4485 size = 'modal-' + this.size;
4488 var footer = Roo.bootstrap.version == 3 ?
4490 cls : 'modal-footer',
4494 cls: 'btn-' + this.buttonPosition
4499 { // BS4 uses mr-auto on left buttons....
4500 cls : 'modal-footer'
4511 cls: "modal-dialog " + size,
4514 cls : "modal-content",
4517 cls : 'modal-header',
4532 modal.cls += ' fade';
4538 getChildContainer : function() {
4543 getButtonContainer : function() {
4545 return Roo.bootstrap.version == 4 ?
4546 this.el.select('.modal-footer',true).first()
4547 : this.el.select('.modal-footer div',true).first();
4550 initEvents : function()
4552 if (this.allow_close) {
4553 this.closeEl.on('click', this.hide, this);
4555 Roo.EventManager.onWindowResize(this.resize, this, true);
4556 if (this.editableTitle) {
4557 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4558 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559 this.headerEditEl.on('keyup', function(e) {
4560 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561 this.toggleHeaderInput(false)
4564 this.headerEditEl.on('blur', function(e) {
4565 this.toggleHeaderInput(false)
4574 this.maskEl.setSize(
4575 Roo.lib.Dom.getViewWidth(true),
4576 Roo.lib.Dom.getViewHeight(true)
4579 if (this.fitwindow) {
4581 this.dialogEl.setStyle( { 'max-width' : '100%' });
4583 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589 if(this.max_width !== 0) {
4591 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4594 this.setSize(w, this.height);
4598 if(this.max_height) {
4599 this.setSize(w,Math.min(
4601 Roo.lib.Dom.getViewportHeight(true) - 60
4607 if(!this.fit_content) {
4608 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612 this.setSize(w, Math.min(
4614 this.headerEl.getHeight() +
4615 this.footerEl.getHeight() +
4616 this.getChildHeight(this.bodyEl.dom.childNodes),
4617 Roo.lib.Dom.getViewportHeight(true) - 60)
4623 setSize : function(w,h)
4634 if (!this.rendered) {
4637 this.toggleHeaderInput(false);
4638 //this.el.setStyle('display', 'block');
4639 this.el.removeClass('hideing');
4640 this.el.dom.style.display='block';
4642 Roo.get(document.body).addClass('modal-open');
4644 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4647 this.el.addClass('show');
4648 this.el.addClass('in');
4651 this.el.addClass('show');
4652 this.el.addClass('in');
4655 // not sure how we can show data in here..
4657 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4660 Roo.get(document.body).addClass("x-body-masked");
4662 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4663 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664 this.maskEl.dom.style.display = 'block';
4665 this.maskEl.addClass('show');
4670 this.fireEvent('show', this);
4672 // set zindex here - otherwise it appears to be ignored...
4673 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676 this.items.forEach( function(e) {
4677 e.layout ? e.layout() : false;
4685 if(this.fireEvent("beforehide", this) !== false){
4687 this.maskEl.removeClass('show');
4689 this.maskEl.dom.style.display = '';
4690 Roo.get(document.body).removeClass("x-body-masked");
4691 this.el.removeClass('in');
4692 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4694 if(this.animate){ // why
4695 this.el.addClass('hideing');
4696 this.el.removeClass('show');
4698 if (!this.el.hasClass('hideing')) {
4699 return; // it's been shown again...
4702 this.el.dom.style.display='';
4704 Roo.get(document.body).removeClass('modal-open');
4705 this.el.removeClass('hideing');
4709 this.el.removeClass('show');
4710 this.el.dom.style.display='';
4711 Roo.get(document.body).removeClass('modal-open');
4714 this.fireEvent('hide', this);
4717 isVisible : function()
4720 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4724 addButton : function(str, cb)
4728 var b = Roo.apply({}, { html : str } );
4729 b.xns = b.xns || Roo.bootstrap;
4730 b.xtype = b.xtype || 'Button';
4731 if (typeof(b.listeners) == 'undefined') {
4732 b.listeners = { click : cb.createDelegate(this) };
4735 var btn = Roo.factory(b);
4737 btn.render(this.getButtonContainer());
4743 setDefaultButton : function(btn)
4745 //this.el.select('.modal-footer').()
4748 resizeTo: function(w,h)
4750 this.dialogEl.setWidth(w);
4752 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4754 this.bodyEl.setHeight(h - diff);
4756 this.fireEvent('resize', this);
4759 setContentSize : function(w, h)
4763 onButtonClick: function(btn,e)
4766 this.fireEvent('btnclick', btn.name, e);
4769 * Set the title of the Dialog
4770 * @param {String} str new Title
4772 setTitle: function(str) {
4773 this.titleEl.dom.innerHTML = str;
4777 * Set the body of the Dialog
4778 * @param {String} str new Title
4780 setBody: function(str) {
4781 this.bodyEl.dom.innerHTML = str;
4784 * Set the body of the Dialog using the template
4785 * @param {Obj} data - apply this data to the template and replace the body contents.
4787 applyBody: function(obj)
4790 Roo.log("Error - using apply Body without a template");
4793 this.tmpl.overwrite(this.bodyEl, obj);
4796 getChildHeight : function(child_nodes)
4800 child_nodes.length == 0
4805 var child_height = 0;
4807 for(var i = 0; i < child_nodes.length; i++) {
4810 * for modal with tabs...
4811 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4813 var layout_childs = child_nodes[i].childNodes;
4815 for(var j = 0; j < layout_childs.length; j++) {
4817 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4819 var layout_body_childs = layout_childs[j].childNodes;
4821 for(var k = 0; k < layout_body_childs.length; k++) {
4823 if(layout_body_childs[k].classList.contains('navbar')) {
4824 child_height += layout_body_childs[k].offsetHeight;
4828 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4830 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4832 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4834 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4850 child_height += child_nodes[i].offsetHeight;
4851 // Roo.log(child_nodes[i].offsetHeight);
4854 return child_height;
4856 toggleHeaderInput : function(is_edit)
4858 if (!this.editableTitle) {
4859 return; // not editable.
4861 if (is_edit && this.is_header_editing) {
4862 return; // already editing..
4866 this.headerEditEl.dom.value = this.title;
4867 this.headerEditEl.removeClass('d-none');
4868 this.headerEditEl.dom.focus();
4869 this.titleEl.addClass('d-none');
4871 this.is_header_editing = true;
4874 // flip back to not editing.
4875 this.title = this.headerEditEl.dom.value;
4876 this.headerEditEl.addClass('d-none');
4877 this.titleEl.removeClass('d-none');
4878 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879 this.is_header_editing = false;
4880 this.fireEvent('titlechanged', this, this.title);
4889 Roo.apply(Roo.bootstrap.Modal, {
4891 * Button config that displays a single OK button
4900 * Button config that displays Yes and No buttons
4916 * Button config that displays OK and Cancel buttons
4931 * Button config that displays Yes, No and Cancel buttons
4956 * messagebox - can be used as a replace
4960 * @class Roo.MessageBox
4961 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4970 // process text value...
4974 // Show a dialog using config options:
4976 title:'Save Changes?',
4977 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978 buttons: Roo.Msg.YESNOCANCEL,
4985 Roo.bootstrap.MessageBox = function(){
4986 var dlg, opt, mask, waitTimer;
4987 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988 var buttons, activeTextEl, bwidth;
4992 var handleButton = function(button){
4994 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4998 var handleHide = function(){
5000 dlg.el.removeClass(opt.cls);
5003 // Roo.TaskMgr.stop(waitTimer);
5004 // waitTimer = null;
5009 var updateButtons = function(b){
5012 buttons["ok"].hide();
5013 buttons["cancel"].hide();
5014 buttons["yes"].hide();
5015 buttons["no"].hide();
5016 dlg.footerEl.hide();
5020 dlg.footerEl.show();
5021 for(var k in buttons){
5022 if(typeof buttons[k] != "function"){
5025 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026 width += buttons[k].el.getWidth()+15;
5036 var handleEsc = function(d, k, e){
5037 if(opt && opt.closable !== false){
5047 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048 * @return {Roo.BasicDialog} The BasicDialog element
5050 getDialog : function(){
5052 dlg = new Roo.bootstrap.Modal( {
5055 //constraintoviewport:false,
5057 //collapsible : false,
5062 //buttonAlign:"center",
5063 closeClick : function(){
5064 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5067 handleButton("cancel");
5072 dlg.on("hide", handleHide);
5074 //dlg.addKeyListener(27, handleEsc);
5076 this.buttons = buttons;
5077 var bt = this.buttonText;
5078 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5083 bodyEl = dlg.bodyEl.createChild({
5085 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086 '<textarea class="roo-mb-textarea"></textarea>' +
5087 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5089 msgEl = bodyEl.dom.firstChild;
5090 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091 textboxEl.enableDisplayMode();
5092 textboxEl.addKeyListener([10,13], function(){
5093 if(dlg.isVisible() && opt && opt.buttons){
5096 }else if(opt.buttons.yes){
5097 handleButton("yes");
5101 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102 textareaEl.enableDisplayMode();
5103 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104 progressEl.enableDisplayMode();
5106 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107 var pf = progressEl.dom.firstChild;
5109 pp = Roo.get(pf.firstChild);
5110 pp.setHeight(pf.offsetHeight);
5118 * Updates the message box body text
5119 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120 * the XHTML-compliant non-breaking space character '&#160;')
5121 * @return {Roo.MessageBox} This message box
5123 updateText : function(text)
5125 if(!dlg.isVisible() && !opt.width){
5126 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5129 msgEl.innerHTML = text || ' ';
5131 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5134 Math.min(opt.width || cw , this.maxWidth),
5135 Math.max(opt.minWidth || this.minWidth, bwidth)
5138 activeTextEl.setWidth(w);
5140 if(dlg.isVisible()){
5141 dlg.fixedcenter = false;
5143 // to big, make it scroll. = But as usual stupid IE does not support
5146 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5150 bodyEl.dom.style.height = '';
5151 bodyEl.dom.style.overflowY = '';
5154 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5156 bodyEl.dom.style.overflowX = '';
5159 dlg.setContentSize(w, bodyEl.getHeight());
5160 if(dlg.isVisible()){
5161 dlg.fixedcenter = true;
5167 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5168 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171 * @return {Roo.MessageBox} This message box
5173 updateProgress : function(value, text){
5175 this.updateText(text);
5178 if (pp) { // weird bug on my firefox - for some reason this is not defined
5179 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5186 * Returns true if the message box is currently displayed
5187 * @return {Boolean} True if the message box is visible, else false
5189 isVisible : function(){
5190 return dlg && dlg.isVisible();
5194 * Hides the message box if it is displayed
5197 if(this.isVisible()){
5203 * Displays a new message box, or reinitializes an existing message box, based on the config options
5204 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205 * The following config object properties are supported:
5207 Property Type Description
5208 ---------- --------------- ------------------------------------------------------------------------------------
5209 animEl String/Element An id or Element from which the message box should animate as it opens and
5210 closes (defaults to undefined)
5211 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable Boolean False to hide the top-right close button (defaults to true). Note that
5214 progress and wait dialogs will ignore this property and always hide the
5215 close button as they can only be closed programmatically.
5216 cls String A custom CSS class to apply to the message box element
5217 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5218 displayed (defaults to 75)
5219 fn Function A callback function to execute after closing the dialog. The arguments to the
5220 function will be btn (the name of the button that was clicked, if applicable,
5221 e.g. "ok"), and text (the value of the active text field, if applicable).
5222 Progress and wait dialogs will ignore this option since they do not respond to
5223 user actions and can only be closed programmatically, so any required function
5224 should be called by the same code after it closes the dialog.
5225 icon String A CSS class that provides a background image to be used as an icon for
5226 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5228 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5229 modal Boolean False to allow user interaction with the page while the message box is
5230 displayed (defaults to true)
5231 msg String A string that will replace the existing message box body text (defaults
5232 to the XHTML-compliant non-breaking space character ' ')
5233 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5234 progress Boolean True to display a progress bar (defaults to false)
5235 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5238 title String The title text
5239 value String The string value to set into the active textbox element if displayed
5240 wait Boolean True to display a progress bar (defaults to false)
5241 width Number The width of the dialog in pixels
5248 msg: 'Please enter your address:',
5250 buttons: Roo.MessageBox.OKCANCEL,
5253 animEl: 'addAddressBtn'
5256 * @param {Object} config Configuration options
5257 * @return {Roo.MessageBox} This message box
5259 show : function(options)
5262 // this causes nightmares if you show one dialog after another
5263 // especially on callbacks..
5265 if(this.isVisible()){
5268 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5270 Roo.log("New Dialog Message:" + options.msg )
5271 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5275 var d = this.getDialog();
5277 d.setTitle(opt.title || " ");
5278 d.closeEl.setDisplayed(opt.closable !== false);
5279 activeTextEl = textboxEl;
5280 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5285 textareaEl.setHeight(typeof opt.multiline == "number" ?
5286 opt.multiline : this.defaultTextHeight);
5287 activeTextEl = textareaEl;
5296 progressEl.setDisplayed(opt.progress === true);
5298 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5300 this.updateProgress(0);
5301 activeTextEl.dom.value = opt.value || "";
5303 dlg.setDefaultButton(activeTextEl);
5305 var bs = opt.buttons;
5309 }else if(bs && bs.yes){
5310 db = buttons["yes"];
5312 dlg.setDefaultButton(db);
5314 bwidth = updateButtons(opt.buttons);
5315 this.updateText(opt.msg);
5317 d.el.addClass(opt.cls);
5319 d.proxyDrag = opt.proxyDrag === true;
5320 d.modal = opt.modal !== false;
5321 d.mask = opt.modal !== false ? mask : false;
5323 // force it to the end of the z-index stack so it gets a cursor in FF
5324 document.body.appendChild(dlg.el.dom);
5325 d.animateTarget = null;
5326 d.show(options.animEl);
5332 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5333 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334 * and closing the message box when the process is complete.
5335 * @param {String} title The title bar text
5336 * @param {String} msg The message box body text
5337 * @return {Roo.MessageBox} This message box
5339 progress : function(title, msg){
5346 minWidth: this.minProgressWidth,
5353 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354 * If a callback function is passed it will be called after the user clicks the button, and the
5355 * id of the button that was clicked will be passed as the only parameter to the callback
5356 * (could also be the top-right close button).
5357 * @param {String} title The title bar text
5358 * @param {String} msg The message box body text
5359 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360 * @param {Object} scope (optional) The scope of the callback function
5361 * @return {Roo.MessageBox} This message box
5363 alert : function(title, msg, fn, scope)
5378 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5379 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380 * You are responsible for closing the message box when the process is complete.
5381 * @param {String} msg The message box body text
5382 * @param {String} title (optional) The title bar text
5383 * @return {Roo.MessageBox} This message box
5385 wait : function(msg, title){
5396 waitTimer = Roo.TaskMgr.start({
5398 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5406 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409 * @param {String} title The title bar text
5410 * @param {String} msg The message box body text
5411 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412 * @param {Object} scope (optional) The scope of the callback function
5413 * @return {Roo.MessageBox} This message box
5415 confirm : function(title, msg, fn, scope){
5419 buttons: this.YESNO,
5428 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5430 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431 * (could also be the top-right close button) and the text that was entered will be passed as the two
5432 * parameters to the callback.
5433 * @param {String} title The title bar text
5434 * @param {String} msg The message box body text
5435 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436 * @param {Object} scope (optional) The scope of the callback function
5437 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439 * @return {Roo.MessageBox} This message box
5441 prompt : function(title, msg, fn, scope, multiline){
5445 buttons: this.OKCANCEL,
5450 multiline: multiline,
5457 * Button config that displays a single OK button
5462 * Button config that displays Yes and No buttons
5465 YESNO : {yes:true, no:true},
5467 * Button config that displays OK and Cancel buttons
5470 OKCANCEL : {ok:true, cancel:true},
5472 * Button config that displays Yes, No and Cancel buttons
5475 YESNOCANCEL : {yes:true, no:true, cancel:true},
5478 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5481 defaultTextHeight : 75,
5483 * The maximum width in pixels of the message box (defaults to 600)
5488 * The minimum width in pixels of the message box (defaults to 100)
5493 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5494 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5497 minProgressWidth : 250,
5499 * An object containing the default button text strings that can be overriden for localized language support.
5500 * Supported properties are: ok, cancel, yes and no.
5501 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5514 * Shorthand for {@link Roo.MessageBox}
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 * @class Roo.bootstrap.nav.Bar
5527 * @extends Roo.bootstrap.Component
5529 * Bootstrap Navbar class
5532 * Create a new Navbar
5533 * @param {Object} config The config object
5537 Roo.bootstrap.nav.Bar = function(config){
5538 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5542 * @event beforetoggle
5543 * Fire before toggle the menu
5544 * @param {Roo.EventObject} e
5546 "beforetoggle" : true
5550 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5559 getAutoCreate : function(){
5562 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5566 initEvents :function ()
5568 //Roo.log(this.el.select('.navbar-toggle',true));
5569 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5576 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5578 var size = this.el.getSize();
5579 this.maskEl.setSize(size.width, size.height);
5580 this.maskEl.enableDisplayMode("block");
5589 getChildContainer : function()
5591 if (this.el && this.el.select('.collapse').getCount()) {
5592 return this.el.select('.collapse',true).first();
5607 onToggle : function()
5610 if(this.fireEvent('beforetoggle', this) === false){
5613 var ce = this.el.select('.navbar-collapse',true).first();
5615 if (!ce.hasClass('show')) {
5625 * Expand the navbar pulldown
5627 expand : function ()
5630 var ce = this.el.select('.navbar-collapse',true).first();
5631 if (ce.hasClass('collapsing')) {
5634 ce.dom.style.height = '';
5636 ce.addClass('in'); // old...
5637 ce.removeClass('collapse');
5638 ce.addClass('show');
5639 var h = ce.getHeight();
5641 ce.removeClass('show');
5642 // at this point we should be able to see it..
5643 ce.addClass('collapsing');
5645 ce.setHeight(0); // resize it ...
5646 ce.on('transitionend', function() {
5647 //Roo.log('done transition');
5648 ce.removeClass('collapsing');
5649 ce.addClass('show');
5650 ce.removeClass('collapse');
5652 ce.dom.style.height = '';
5653 }, this, { single: true} );
5655 ce.dom.scrollTop = 0;
5658 * Collapse the navbar pulldown
5660 collapse : function()
5662 var ce = this.el.select('.navbar-collapse',true).first();
5664 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5665 // it's collapsed or collapsing..
5668 ce.removeClass('in'); // old...
5669 ce.setHeight(ce.getHeight());
5670 ce.removeClass('show');
5671 ce.addClass('collapsing');
5673 ce.on('transitionend', function() {
5674 ce.dom.style.height = '';
5675 ce.removeClass('collapsing');
5676 ce.addClass('collapse');
5677 }, this, { single: true} );
5697 * @class Roo.bootstrap.nav.Simplebar
5698 * @extends Roo.bootstrap.nav.Bar
5699 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5700 * Bootstrap Sidebar class
5702 * @cfg {Boolean} inverse is inverted color
5704 * @cfg {String} type (nav | pills | tabs)
5705 * @cfg {Boolean} arrangement stacked | justified
5706 * @cfg {String} align (left | right) alignment
5708 * @cfg {Boolean} main (true|false) main nav bar? default false
5709 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5711 * @cfg {String} tag (header|footer|nav|div) default is nav
5713 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5717 * Create a new Sidebar
5718 * @param {Object} config The config object
5722 Roo.bootstrap.nav.Simplebar = function(config){
5723 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5726 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5742 getAutoCreate : function(){
5746 tag : this.tag || 'div',
5747 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5749 if (['light','white'].indexOf(this.weight) > -1) {
5750 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5752 cfg.cls += ' bg-' + this.weight;
5755 cfg.cls += ' navbar-inverse';
5759 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5761 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770 cls: 'nav nav-' + this.xtype,
5776 this.type = this.type || 'nav';
5777 if (['tabs','pills'].indexOf(this.type) != -1) {
5778 cfg.cn[0].cls += ' nav-' + this.type
5782 if (this.type!=='nav') {
5783 Roo.log('nav type must be nav/tabs/pills')
5785 cfg.cn[0].cls += ' navbar-nav'
5791 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5792 cfg.cn[0].cls += ' nav-' + this.arrangement;
5796 if (this.align === 'right') {
5797 cfg.cn[0].cls += ' navbar-right';
5822 * navbar-expand-md fixed-top
5826 * @class Roo.bootstrap.nav.Headerbar
5827 * @extends Roo.bootstrap.nav.Simplebar
5828 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5829 * Bootstrap Sidebar class
5831 * @cfg {String} brand what is brand
5832 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833 * @cfg {String} brand_href href of the brand
5834 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5835 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840 * Create a new Sidebar
5841 * @param {Object} config The config object
5845 Roo.bootstrap.nav.Headerbar = function(config){
5846 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5850 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5857 desktopCenter : false,
5860 getAutoCreate : function(){
5863 tag: this.nav || 'nav',
5864 cls: 'navbar navbar-expand-md',
5870 if (this.desktopCenter) {
5871 cn.push({cls : 'container', cn : []});
5879 cls: 'navbar-toggle navbar-toggler',
5880 'data-toggle': 'collapse',
5885 html: 'Toggle navigation'
5889 cls: 'icon-bar navbar-toggler-icon'
5902 cn.push( Roo.bootstrap.version == 4 ? btn : {
5904 cls: 'navbar-header',
5913 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5917 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5919 if (['light','white'].indexOf(this.weight) > -1) {
5920 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5922 cfg.cls += ' bg-' + this.weight;
5925 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5928 // tag can override this..
5930 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5933 if (this.brand !== '') {
5934 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5937 href: this.brand_href ? this.brand_href : '#',
5938 cls: 'navbar-brand',
5946 cfg.cls += ' main-nav';
5954 getHeaderChildContainer : function()
5956 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957 return this.el.select('.navbar-header',true).first();
5960 return this.getChildContainer();
5963 getChildContainer : function()
5966 return this.el.select('.roo-navbar-collapse',true).first();
5971 initEvents : function()
5973 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5975 if (this.autohide) {
5980 Roo.get(document).on('scroll',function(e) {
5981 var ns = Roo.get(document).getScroll().top;
5982 var os = prevScroll;
5986 ft.removeClass('slideDown');
5987 ft.addClass('slideUp');
5990 ft.removeClass('slideUp');
5991 ft.addClass('slideDown');
6012 * @class Roo.bootstrap.nav.Sidebar
6013 * @extends Roo.bootstrap.nav.Bar
6014 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6015 * Bootstrap Sidebar class
6018 * Create a new Sidebar
6019 * @param {Object} config The config object
6023 Roo.bootstrap.nav.Sidebar = function(config){
6024 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6027 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6029 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6031 getAutoCreate : function(){
6036 cls: 'sidebar sidebar-nav'
6058 * @class Roo.bootstrap.nav.Group
6059 * @extends Roo.bootstrap.Component
6060 * @children Roo.bootstrap.nav.Item
6061 * Bootstrap NavGroup class
6062 * @cfg {String} align (left|right)
6063 * @cfg {Boolean} inverse
6064 * @cfg {String} type (nav|pills|tab) default nav
6065 * @cfg {String} navId - reference Id for navbar.
6066 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6069 * Create a new nav group
6070 * @param {Object} config The config object
6073 Roo.bootstrap.nav.Group = function(config){
6074 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6077 Roo.bootstrap.nav.Group.register(this);
6081 * Fires when the active item changes
6082 * @param {Roo.bootstrap.nav.Group} this
6083 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6091 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6103 getAutoCreate : function()
6105 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6111 if (Roo.bootstrap.version == 4) {
6112 if (['tabs','pills'].indexOf(this.type) != -1) {
6113 cfg.cls += ' nav-' + this.type;
6115 // trying to remove so header bar can right align top?
6116 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117 // do not use on header bar...
6118 cfg.cls += ' navbar-nav';
6123 if (['tabs','pills'].indexOf(this.type) != -1) {
6124 cfg.cls += ' nav-' + this.type
6126 if (this.type !== 'nav') {
6127 Roo.log('nav type must be nav/tabs/pills')
6129 cfg.cls += ' navbar-nav'
6133 if (this.parent() && this.parent().sidebar) {
6136 cls: 'dashboard-menu sidebar-menu'
6142 if (this.form === true) {
6145 cls: 'navbar-form form-inline'
6147 //nav navbar-right ml-md-auto
6148 if (this.align === 'right') {
6149 cfg.cls += ' navbar-right ml-md-auto';
6151 cfg.cls += ' navbar-left';
6155 if (this.align === 'right') {
6156 cfg.cls += ' navbar-right ml-md-auto';
6158 cfg.cls += ' mr-auto';
6162 cfg.cls += ' navbar-inverse';
6170 * sets the active Navigation item
6171 * @param {Roo.bootstrap.nav.Item} the new current navitem
6173 setActiveItem : function(item)
6176 Roo.each(this.navItems, function(v){
6181 v.setActive(false, true);
6188 item.setActive(true, true);
6189 this.fireEvent('changed', this, item, prev);
6194 * gets the active Navigation item
6195 * @return {Roo.bootstrap.nav.Item} the current navitem
6197 getActive : function()
6201 Roo.each(this.navItems, function(v){
6212 indexOfNav : function()
6216 Roo.each(this.navItems, function(v,i){
6227 * adds a Navigation item
6228 * @param {Roo.bootstrap.nav.Item} the navitem to add
6230 addItem : function(cfg)
6232 if (this.form && Roo.bootstrap.version == 4) {
6235 var cn = new Roo.bootstrap.nav.Item(cfg);
6237 cn.parentId = this.id;
6238 cn.onRender(this.el, null);
6242 * register a Navigation item
6243 * @param {Roo.bootstrap.nav.Item} the navitem to add
6245 register : function(item)
6247 this.navItems.push( item);
6248 item.navId = this.navId;
6253 * clear all the Navigation item
6256 clearAll : function()
6259 this.el.dom.innerHTML = '';
6262 getNavItem: function(tabId)
6265 Roo.each(this.navItems, function(e) {
6266 if (e.tabId == tabId) {
6276 setActiveNext : function()
6278 var i = this.indexOfNav(this.getActive());
6279 if (i > this.navItems.length) {
6282 this.setActiveItem(this.navItems[i+1]);
6284 setActivePrev : function()
6286 var i = this.indexOfNav(this.getActive());
6290 this.setActiveItem(this.navItems[i-1]);
6292 clearWasActive : function(except) {
6293 Roo.each(this.navItems, function(e) {
6294 if (e.tabId != except.tabId && e.was_active) {
6295 e.was_active = false;
6302 getWasActive : function ()
6305 Roo.each(this.navItems, function(e) {
6320 Roo.apply(Roo.bootstrap.nav.Group, {
6324 * register a Navigation Group
6325 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6327 register : function(navgrp)
6329 this.groups[navgrp.navId] = navgrp;
6333 * fetch a Navigation Group based on the navigation ID
6334 * @param {string} the navgroup to add
6335 * @returns {Roo.bootstrap.nav.Group} the navgroup
6337 get: function(navId) {
6338 if (typeof(this.groups[navId]) == 'undefined') {
6340 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6342 return this.groups[navId] ;
6350 * @class Roo.bootstrap.nav.Item
6351 * @extends Roo.bootstrap.Component
6352 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6353 * @parent Roo.bootstrap.nav.Group
6355 * Bootstrap Navbar.NavItem class
6357 * @cfg {String} href link to
6358 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6359 * @cfg {Boolean} button_outline show and outlined button
6360 * @cfg {String} html content of button
6361 * @cfg {String} badge text inside badge
6362 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6363 * @cfg {String} glyphicon DEPRICATED - use fa
6364 * @cfg {String} icon DEPRICATED - use fa
6365 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6366 * @cfg {Boolean} active Is item active
6367 * @cfg {Boolean} disabled Is item disabled
6368 * @cfg {String} linkcls Link Class
6369 * @cfg {Boolean} preventDefault (true | false) default false
6370 * @cfg {String} tabId the tab that this item activates.
6371 * @cfg {String} tagtype (a|span) render as a href or span?
6372 * @cfg {Boolean} animateRef (true|false) link to element default false
6373 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6376 * Create a new Navbar Item
6377 * @param {Object} config The config object
6379 Roo.bootstrap.nav.Item = function(config){
6380 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6385 * The raw click event for the entire grid.
6386 * @param {Roo.EventObject} e
6391 * Fires when the active item active state changes
6392 * @param {Roo.bootstrap.nav.Item} this
6393 * @param {boolean} state the new state
6399 * Fires when scroll to element
6400 * @param {Roo.bootstrap.nav.Item} this
6401 * @param {Object} options
6402 * @param {Roo.EventObject} e
6410 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6419 preventDefault : false,
6427 button_outline : false,
6431 getAutoCreate : function(){
6438 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6441 cfg.cls += ' active' ;
6443 if (this.disabled) {
6444 cfg.cls += ' disabled';
6448 if (this.button_weight.length) {
6449 cfg.tag = this.href ? 'a' : 'button';
6450 cfg.html = this.html || '';
6451 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6453 cfg.href = this.href;
6456 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6458 cfg.cls += " nav-html";
6461 // menu .. should add dropdown-menu class - so no need for carat..
6463 if (this.badge !== '') {
6465 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6470 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6474 href : this.href || "#",
6475 html: this.html || '',
6479 if (this.tagtype == 'a') {
6480 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6484 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485 } else if (this.fa) {
6486 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487 } else if(this.glyphicon) {
6488 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6490 cfg.cn[0].cls += " nav-html";
6494 cfg.cn[0].html += " <span class='caret'></span>";
6498 if (this.badge !== '') {
6499 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6507 onRender : function(ct, position)
6509 // Roo.log("Call onRender: " + this.xtype);
6510 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6514 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6515 this.navLink = this.el.select('.nav-link',true).first();
6516 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6521 initEvents: function()
6523 if (typeof (this.menu) != 'undefined') {
6524 this.menu.parentType = this.xtype;
6525 this.menu.triggerEl = this.el;
6526 this.menu = this.addxtype(Roo.apply({}, this.menu));
6529 this.el.on('click', this.onClick, this);
6531 //if(this.tagtype == 'span'){
6532 // this.el.select('span',true).on('click', this.onClick, this);
6535 // at this point parent should be available..
6536 this.parent().register(this);
6539 onClick : function(e)
6541 if (e.getTarget('.dropdown-menu-item')) {
6542 // did you click on a menu itemm.... - then don't trigger onclick..
6547 this.preventDefault ||
6550 Roo.log("NavItem - prevent Default?");
6554 if (this.disabled) {
6558 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559 if (tg && tg.transition) {
6560 Roo.log("waiting for the transitionend");
6566 //Roo.log("fire event clicked");
6567 if(this.fireEvent('click', this, e) === false){
6571 if(this.tagtype == 'span'){
6575 //Roo.log(this.href);
6576 var ael = this.el.select('a',true).first();
6579 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582 return; // ignore... - it's a 'hash' to another page.
6584 Roo.log("NavItem - prevent Default?");
6586 this.scrollToElement(e);
6590 var p = this.parent();
6592 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593 if (typeof(p.setActiveItem) !== 'undefined') {
6594 p.setActiveItem(this);
6598 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600 // remove the collapsed menu expand...
6601 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6605 isActive: function () {
6608 setActive : function(state, fire, is_was_active)
6610 if (this.active && !state && this.navId) {
6611 this.was_active = true;
6612 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6614 nv.clearWasActive(this);
6618 this.active = state;
6621 this.el.removeClass('active');
6622 this.navLink ? this.navLink.removeClass('active') : false;
6623 } else if (!this.el.hasClass('active')) {
6625 this.el.addClass('active');
6626 if (Roo.bootstrap.version == 4 && this.navLink ) {
6627 this.navLink.addClass('active');
6632 this.fireEvent('changed', this, state);
6635 // show a panel if it's registered and related..
6637 if (!this.navId || !this.tabId || !state || is_was_active) {
6641 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6645 var pan = tg.getPanelByName(this.tabId);
6649 // if we can not flip to new panel - go back to old nav highlight..
6650 if (false == tg.showPanel(pan)) {
6651 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6653 var onav = nv.getWasActive();
6655 onav.setActive(true, false, true);
6664 // this should not be here...
6665 setDisabled : function(state)
6667 this.disabled = state;
6669 this.el.removeClass('disabled');
6670 } else if (!this.el.hasClass('disabled')) {
6671 this.el.addClass('disabled');
6677 * Fetch the element to display the tooltip on.
6678 * @return {Roo.Element} defaults to this.el
6680 tooltipEl : function()
6682 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6685 scrollToElement : function(e)
6687 var c = document.body;
6690 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6692 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693 c = document.documentElement;
6696 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6702 var o = target.calcOffsetsTo(c);
6709 this.fireEvent('scrollto', this, options, e);
6711 Roo.get(c).scrollTo('top', options.value, true);
6716 * Set the HTML (text content) of the item
6717 * @param {string} html content for the nav item
6719 setHtml : function(html)
6722 this.htmlEl.dom.innerHTML = html;
6734 * <span> icon </span>
6735 * <span> text </span>
6736 * <span>badge </span>
6740 * @class Roo.bootstrap.nav.SidebarItem
6741 * @extends Roo.bootstrap.nav.Item
6742 * Bootstrap Navbar.NavSidebarItem class
6744 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6745 * {Boolean} open is the menu open
6746 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6747 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6748 * {String} buttonSize (sm|md|lg)the extra classes for the button
6749 * {Boolean} showArrow show arrow next to the text (default true)
6751 * Create a new Navbar Button
6752 * @param {Object} config The config object
6754 Roo.bootstrap.nav.SidebarItem = function(config){
6755 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6760 * The raw click event for the entire grid.
6761 * @param {Roo.EventObject} e
6766 * Fires when the active item active state changes
6767 * @param {Roo.bootstrap.nav.SidebarItem} this
6768 * @param {boolean} state the new state
6776 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6778 badgeWeight : 'default',
6784 buttonWeight : 'default',
6790 getAutoCreate : function(){
6795 href : this.href || '#',
6801 if(this.buttonView){
6804 href : this.href || '#',
6805 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6818 cfg.cls += ' active';
6821 if (this.disabled) {
6822 cfg.cls += ' disabled';
6825 cfg.cls += ' open x-open';
6828 if (this.glyphicon || this.icon) {
6829 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6830 a.cn.push({ tag : 'i', cls : c }) ;
6833 if(!this.buttonView){
6836 html : this.html || ''
6843 if (this.badge !== '') {
6844 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6850 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6853 a.cls += ' dropdown-toggle treeview' ;
6859 initEvents : function()
6861 if (typeof (this.menu) != 'undefined') {
6862 this.menu.parentType = this.xtype;
6863 this.menu.triggerEl = this.el;
6864 this.menu = this.addxtype(Roo.apply({}, this.menu));
6867 this.el.on('click', this.onClick, this);
6869 if(this.badge !== ''){
6870 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6875 onClick : function(e)
6882 if(this.preventDefault){
6886 this.fireEvent('click', this, e);
6889 disable : function()
6891 this.setDisabled(true);
6896 this.setDisabled(false);
6899 setDisabled : function(state)
6901 if(this.disabled == state){
6905 this.disabled = state;
6908 this.el.addClass('disabled');
6912 this.el.removeClass('disabled');
6917 setActive : function(state)
6919 if(this.active == state){
6923 this.active = state;
6926 this.el.addClass('active');
6930 this.el.removeClass('active');
6935 isActive: function ()
6940 setBadge : function(str)
6946 this.badgeEl.dom.innerHTML = str;
6963 * @class Roo.bootstrap.nav.ProgressBar
6964 * @extends Roo.bootstrap.Component
6965 * @children Roo.bootstrap.nav.ProgressBarItem
6966 * Bootstrap NavProgressBar class
6969 * Create a new nav progress bar - a bar indicating step along a process
6970 * @param {Object} config The config object
6973 Roo.bootstrap.nav.ProgressBar = function(config){
6974 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6976 this.bullets = this.bullets || [];
6978 // Roo.bootstrap.nav.ProgressBar.register(this);
6982 * Fires when the active item changes
6983 * @param {Roo.bootstrap.nav.ProgressBar} this
6984 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6985 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6992 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6994 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6995 * Bullets for the Nav Progress bar for the toolbar
7000 getAutoCreate : function()
7002 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7006 cls : 'roo-navigation-bar-group',
7010 cls : 'roo-navigation-top-bar'
7014 cls : 'roo-navigation-bullets-bar',
7018 cls : 'roo-navigation-bar'
7025 cls : 'roo-navigation-bottom-bar'
7035 initEvents: function()
7040 onRender : function(ct, position)
7042 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7044 if(this.bullets.length){
7045 Roo.each(this.bullets, function(b){
7054 addItem : function(cfg)
7056 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7058 item.parentId = this.id;
7059 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7062 var top = new Roo.bootstrap.Element({
7064 cls : 'roo-navigation-bar-text'
7067 var bottom = new Roo.bootstrap.Element({
7069 cls : 'roo-navigation-bar-text'
7072 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7073 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7075 var topText = new Roo.bootstrap.Element({
7077 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7080 var bottomText = new Roo.bootstrap.Element({
7082 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7085 topText.onRender(top.el, null);
7086 bottomText.onRender(bottom.el, null);
7089 item.bottomEl = bottom;
7092 this.barItems.push(item);
7097 getActive : function()
7101 Roo.each(this.barItems, function(v){
7103 if (!v.isActive()) {
7115 setActiveItem : function(item)
7119 Roo.each(this.barItems, function(v){
7120 if (v.rid == item.rid) {
7130 item.setActive(true);
7132 this.fireEvent('changed', this, item, prev);
7135 getBarItem: function(rid)
7139 Roo.each(this.barItems, function(e) {
7151 indexOfItem : function(item)
7155 Roo.each(this.barItems, function(v, i){
7157 if (v.rid != item.rid) {
7168 setActiveNext : function()
7170 var i = this.indexOfItem(this.getActive());
7172 if (i > this.barItems.length) {
7176 this.setActiveItem(this.barItems[i+1]);
7179 setActivePrev : function()
7181 var i = this.indexOfItem(this.getActive());
7187 this.setActiveItem(this.barItems[i-1]);
7192 if(!this.barItems.length){
7196 var width = 100 / this.barItems.length;
7198 Roo.each(this.barItems, function(i){
7199 i.el.setStyle('width', width + '%');
7200 i.topEl.el.setStyle('width', width + '%');
7201 i.bottomEl.el.setStyle('width', width + '%');
7215 * @class Roo.bootstrap.nav.ProgressBarItem
7216 * @extends Roo.bootstrap.Component
7217 * Bootstrap NavProgressBarItem class
7218 * @cfg {String} rid the reference id
7219 * @cfg {Boolean} active (true|false) Is item active default false
7220 * @cfg {Boolean} disabled (true|false) Is item active default false
7221 * @cfg {String} html
7222 * @cfg {String} position (top|bottom) text position default bottom
7223 * @cfg {String} icon show icon instead of number
7226 * Create a new NavProgressBarItem
7227 * @param {Object} config The config object
7229 Roo.bootstrap.nav.ProgressBarItem = function(config){
7230 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7235 * The raw click event for the entire grid.
7236 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7237 * @param {Roo.EventObject} e
7244 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7250 position : 'bottom',
7253 getAutoCreate : function()
7255 var iconCls = 'roo-navigation-bar-item-icon';
7257 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7261 cls: 'roo-navigation-bar-item',
7271 cfg.cls += ' active';
7274 cfg.cls += ' disabled';
7280 disable : function()
7282 this.setDisabled(true);
7287 this.setDisabled(false);
7290 initEvents: function()
7292 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7294 this.iconEl.on('click', this.onClick, this);
7297 onClick : function(e)
7305 if(this.fireEvent('click', this, e) === false){
7309 this.parent().setActiveItem(this);
7312 isActive: function ()
7317 setActive : function(state)
7319 if(this.active == state){
7323 this.active = state;
7326 this.el.addClass('active');
7330 this.el.removeClass('active');
7335 setDisabled : function(state)
7337 if(this.disabled == state){
7341 this.disabled = state;
7344 this.el.addClass('disabled');
7348 this.el.removeClass('disabled');
7351 tooltipEl : function()
7353 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7364 Roo.namespace('Roo.bootstrap.breadcrumb');
7368 * @class Roo.bootstrap.breadcrumb.Nav
7369 * @extends Roo.bootstrap.Component
7370 * Bootstrap Breadcrumb Nav Class
7372 * @children Roo.bootstrap.breadcrumb.Item
7375 * Create a new breadcrumb.Nav
7376 * @param {Object} config The config object
7380 Roo.bootstrap.breadcrumb.Nav = function(config){
7381 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7386 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7388 getAutoCreate : function()
7405 initEvents: function()
7407 this.olEl = this.el.select('ol',true).first();
7409 getChildContainer : function()
7425 * @class Roo.bootstrap.breadcrumb.Nav
7426 * @extends Roo.bootstrap.Component
7427 * @children Roo.bootstrap.Component
7428 * @parent Roo.bootstrap.breadcrumb.Nav
7429 * Bootstrap Breadcrumb Nav Class
7432 * @cfg {String} html the content of the link.
7433 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7434 * @cfg {Boolean} active is it active
7438 * Create a new breadcrumb.Nav
7439 * @param {Object} config The config object
7442 Roo.bootstrap.breadcrumb.Item = function(config){
7443 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7448 * The img click event for the img.
7449 * @param {Roo.EventObject} e
7456 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7461 getAutoCreate : function()
7466 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7468 if (this.href !== false) {
7475 cfg.html = this.html;
7481 initEvents: function()
7484 this.el.select('a', true).first().on('click',this.onClick, this)
7488 onClick : function(e)
7491 this.fireEvent('click',this, e);
7504 * @class Roo.bootstrap.Row
7505 * @extends Roo.bootstrap.Component
7506 * @children Roo.bootstrap.Component
7507 * Bootstrap Row class (contains columns...)
7511 * @param {Object} config The config object
7514 Roo.bootstrap.Row = function(config){
7515 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7518 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7520 getAutoCreate : function(){
7539 * @class Roo.bootstrap.Pagination
7540 * @extends Roo.bootstrap.Component
7541 * @children Roo.bootstrap.Pagination
7542 * Bootstrap Pagination class
7544 * @cfg {String} size (xs|sm|md|lg|xl)
7545 * @cfg {Boolean} inverse
7548 * Create a new Pagination
7549 * @param {Object} config The config object
7552 Roo.bootstrap.Pagination = function(config){
7553 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7556 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7562 getAutoCreate : function(){
7568 cfg.cls += ' inverse';
7574 cfg.cls += " " + this.cls;
7592 * @class Roo.bootstrap.PaginationItem
7593 * @extends Roo.bootstrap.Component
7594 * Bootstrap PaginationItem class
7595 * @cfg {String} html text
7596 * @cfg {String} href the link
7597 * @cfg {Boolean} preventDefault (true | false) default true
7598 * @cfg {Boolean} active (true | false) default false
7599 * @cfg {Boolean} disabled default false
7603 * Create a new PaginationItem
7604 * @param {Object} config The config object
7608 Roo.bootstrap.PaginationItem = function(config){
7609 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7614 * The raw click event for the entire grid.
7615 * @param {Roo.EventObject} e
7621 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7625 preventDefault: true,
7630 getAutoCreate : function(){
7636 href : this.href ? this.href : '#',
7637 html : this.html ? this.html : ''
7647 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7651 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7657 initEvents: function() {
7659 this.el.on('click', this.onClick, this);
7662 onClick : function(e)
7664 Roo.log('PaginationItem on click ');
7665 if(this.preventDefault){
7673 this.fireEvent('click', this, e);
7689 * @class Roo.bootstrap.Slider
7690 * @extends Roo.bootstrap.Component
7691 * Bootstrap Slider class
7694 * Create a new Slider
7695 * @param {Object} config The config object
7698 Roo.bootstrap.Slider = function(config){
7699 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7702 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7704 getAutoCreate : function(){
7708 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7712 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7724 * Ext JS Library 1.1.1
7725 * Copyright(c) 2006-2007, Ext JS, LLC.
7727 * Originally Released Under LGPL - original licence link has changed is not relivant.
7730 * <script type="text/javascript">
7733 * @extends Roo.dd.DDProxy
7734 * @class Roo.grid.SplitDragZone
7735 * Support for Column Header resizing
7737 * @param {Object} config
7740 // This is a support class used internally by the Grid components
7741 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7743 this.view = grid.getView();
7744 this.proxy = this.view.resizeProxy;
7745 Roo.grid.SplitDragZone.superclass.constructor.call(
7748 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7750 dragElId : Roo.id(this.proxy.dom),
7755 this.setHandleElId(Roo.id(hd));
7756 if (hd2 !== false) {
7757 this.setOuterHandleElId(Roo.id(hd2));
7760 this.scroll = false;
7762 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7763 fly: Roo.Element.fly,
7765 b4StartDrag : function(x, y){
7766 this.view.headersDisabled = true;
7767 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7768 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7770 this.proxy.setHeight(h);
7772 // for old system colWidth really stored the actual width?
7773 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7774 // which in reality did not work.. - it worked only for fixed sizes
7775 // for resizable we need to use actual sizes.
7776 var w = this.cm.getColumnWidth(this.cellIndex);
7777 if (!this.view.mainWrap) {
7779 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7784 // this was w-this.grid.minColumnWidth;
7785 // doesnt really make sense? - w = thie curren width or the rendered one?
7786 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7787 this.resetConstraints();
7788 this.setXConstraint(minw, 1000);
7789 this.setYConstraint(0, 0);
7790 this.minX = x - minw;
7791 this.maxX = x + 1000;
7793 if (!this.view.mainWrap) { // this is Bootstrap code..
7794 this.getDragEl().style.display='block';
7797 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7801 handleMouseDown : function(e){
7802 ev = Roo.EventObject.setEvent(e);
7803 var t = this.fly(ev.getTarget());
7804 if(t.hasClass("x-grid-split")){
7805 this.cellIndex = this.view.getCellIndex(t.dom);
7807 this.cm = this.grid.colModel;
7808 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7809 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7814 endDrag : function(e){
7815 this.view.headersDisabled = false;
7816 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7817 var diff = endX - this.startPos;
7819 var w = this.cm.getColumnWidth(this.cellIndex);
7820 if (!this.view.mainWrap) {
7823 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7826 autoOffset : function(){
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.grid.AbstractSelectionModel
7842 * @extends Roo.util.Observable
7844 * Abstract base class for grid SelectionModels. It provides the interface that should be
7845 * implemented by descendant classes. This class should not be directly instantiated.
7848 Roo.grid.AbstractSelectionModel = function(){
7849 this.locked = false;
7850 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7853 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7854 /** @ignore Called by the grid automatically. Do not call directly. */
7855 init : function(grid){
7861 * Locks the selections.
7868 * Unlocks the selections.
7870 unlock : function(){
7871 this.locked = false;
7875 * Returns true if the selections are locked.
7878 isLocked : function(){
7883 * Ext JS Library 1.1.1
7884 * Copyright(c) 2006-2007, Ext JS, LLC.
7886 * Originally Released Under LGPL - original licence link has changed is not relivant.
7889 * <script type="text/javascript">
7892 * @extends Roo.grid.AbstractSelectionModel
7893 * @class Roo.grid.RowSelectionModel
7894 * The default SelectionModel used by {@link Roo.grid.Grid}.
7895 * It supports multiple selections and keyboard selection/navigation.
7897 * @param {Object} config
7899 Roo.grid.RowSelectionModel = function(config){
7900 Roo.apply(this, config);
7901 this.selections = new Roo.util.MixedCollection(false, function(o){
7906 this.lastActive = false;
7910 * @event selectionchange
7911 * Fires when the selection changes
7912 * @param {SelectionModel} this
7914 "selectionchange" : true,
7916 * @event afterselectionchange
7917 * Fires after the selection changes (eg. by key press or clicking)
7918 * @param {SelectionModel} this
7920 "afterselectionchange" : true,
7922 * @event beforerowselect
7923 * Fires when a row is selected being selected, return false to cancel.
7924 * @param {SelectionModel} this
7925 * @param {Number} rowIndex The selected index
7926 * @param {Boolean} keepExisting False if other selections will be cleared
7928 "beforerowselect" : true,
7931 * Fires when a row is selected.
7932 * @param {SelectionModel} this
7933 * @param {Number} rowIndex The selected index
7934 * @param {Roo.data.Record} r The record
7938 * @event rowdeselect
7939 * Fires when a row is deselected.
7940 * @param {SelectionModel} this
7941 * @param {Number} rowIndex The selected index
7943 "rowdeselect" : true
7945 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7946 this.locked = false;
7949 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7951 * @cfg {Boolean} singleSelect
7952 * True to allow selection of only one row at a time (defaults to false)
7954 singleSelect : false,
7957 initEvents : function(){
7959 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7960 this.grid.on("mousedown", this.handleMouseDown, this);
7961 }else{ // allow click to work like normal
7962 this.grid.on("rowclick", this.handleDragableRowClick, this);
7964 // bootstrap does not have a view..
7965 var view = this.grid.view ? this.grid.view : this.grid;
7966 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7969 this.selectPrevious(e.shiftKey);
7970 }else if(this.last !== false && this.lastActive !== false){
7971 var last = this.last;
7972 this.selectRange(this.last, this.lastActive-1);
7973 view.focusRow(this.lastActive);
7978 this.selectFirstRow();
7980 this.fireEvent("afterselectionchange", this);
7982 "down" : function(e){
7984 this.selectNext(e.shiftKey);
7985 }else if(this.last !== false && this.lastActive !== false){
7986 var last = this.last;
7987 this.selectRange(this.last, this.lastActive+1);
7988 view.focusRow(this.lastActive);
7993 this.selectFirstRow();
7995 this.fireEvent("afterselectionchange", this);
8001 view.on("refresh", this.onRefresh, this);
8002 view.on("rowupdated", this.onRowUpdated, this);
8003 view.on("rowremoved", this.onRemove, this);
8007 onRefresh : function(){
8008 var ds = this.grid.ds, i, v = this.grid.view;
8009 var s = this.selections;
8011 if((i = ds.indexOfId(r.id)) != -1){
8013 s.add(ds.getAt(i)); // updating the selection relate data
8021 onRemove : function(v, index, r){
8022 this.selections.remove(r);
8026 onRowUpdated : function(v, index, r){
8027 if(this.isSelected(r)){
8028 v.onRowSelect(index);
8034 * @param {Array} records The records to select
8035 * @param {Boolean} keepExisting (optional) True to keep existing selections
8037 selectRecords : function(records, keepExisting){
8039 this.clearSelections();
8041 var ds = this.grid.ds;
8042 for(var i = 0, len = records.length; i < len; i++){
8043 this.selectRow(ds.indexOf(records[i]), true);
8048 * Gets the number of selected rows.
8051 getCount : function(){
8052 return this.selections.length;
8056 * Selects the first row in the grid.
8058 selectFirstRow : function(){
8063 * Select the last row.
8064 * @param {Boolean} keepExisting (optional) True to keep existing selections
8066 selectLastRow : function(keepExisting){
8067 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8071 * Selects the row immediately following the last selected row.
8072 * @param {Boolean} keepExisting (optional) True to keep existing selections
8074 selectNext : function(keepExisting){
8075 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8076 this.selectRow(this.last+1, keepExisting);
8077 var view = this.grid.view ? this.grid.view : this.grid;
8078 view.focusRow(this.last);
8083 * Selects the row that precedes the last selected row.
8084 * @param {Boolean} keepExisting (optional) True to keep existing selections
8086 selectPrevious : function(keepExisting){
8088 this.selectRow(this.last-1, keepExisting);
8089 var view = this.grid.view ? this.grid.view : this.grid;
8090 view.focusRow(this.last);
8095 * Returns the selected records
8096 * @return {Array} Array of selected records
8098 getSelections : function(){
8099 return [].concat(this.selections.items);
8103 * Returns the first selected record.
8106 getSelected : function(){
8107 return this.selections.itemAt(0);
8112 * Clears all selections.
8114 clearSelections : function(fast){
8119 var ds = this.grid.ds;
8120 var s = this.selections;
8122 this.deselectRow(ds.indexOfId(r.id));
8126 this.selections.clear();
8135 selectAll : function(){
8139 this.selections.clear();
8140 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8141 this.selectRow(i, true);
8146 * Returns True if there is a selection.
8149 hasSelection : function(){
8150 return this.selections.length > 0;
8154 * Returns True if the specified row is selected.
8155 * @param {Number/Record} record The record or index of the record to check
8158 isSelected : function(index){
8159 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8160 return (r && this.selections.key(r.id) ? true : false);
8164 * Returns True if the specified record id is selected.
8165 * @param {String} id The id of record to check
8168 isIdSelected : function(id){
8169 return (this.selections.key(id) ? true : false);
8173 handleMouseDown : function(e, t)
8175 var view = this.grid.view ? this.grid.view : this.grid;
8177 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8180 if(e.shiftKey && this.last !== false){
8181 var last = this.last;
8182 this.selectRange(last, rowIndex, e.ctrlKey);
8183 this.last = last; // reset the last
8184 view.focusRow(rowIndex);
8186 var isSelected = this.isSelected(rowIndex);
8187 if(e.button !== 0 && isSelected){
8188 view.focusRow(rowIndex);
8189 }else if(e.ctrlKey && isSelected){
8190 this.deselectRow(rowIndex);
8191 }else if(!isSelected){
8192 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8193 view.focusRow(rowIndex);
8196 this.fireEvent("afterselectionchange", this);
8199 handleDragableRowClick : function(grid, rowIndex, e)
8201 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8202 this.selectRow(rowIndex, false);
8203 var view = this.grid.view ? this.grid.view : this.grid;
8204 view.focusRow(rowIndex);
8205 this.fireEvent("afterselectionchange", this);
8210 * Selects multiple rows.
8211 * @param {Array} rows Array of the indexes of the row to select
8212 * @param {Boolean} keepExisting (optional) True to keep existing selections
8214 selectRows : function(rows, keepExisting){
8216 this.clearSelections();
8218 for(var i = 0, len = rows.length; i < len; i++){
8219 this.selectRow(rows[i], true);
8224 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8225 * @param {Number} startRow The index of the first row in the range
8226 * @param {Number} endRow The index of the last row in the range
8227 * @param {Boolean} keepExisting (optional) True to retain existing selections
8229 selectRange : function(startRow, endRow, keepExisting){
8234 this.clearSelections();
8236 if(startRow <= endRow){
8237 for(var i = startRow; i <= endRow; i++){
8238 this.selectRow(i, true);
8241 for(var i = startRow; i >= endRow; i--){
8242 this.selectRow(i, true);
8248 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8249 * @param {Number} startRow The index of the first row in the range
8250 * @param {Number} endRow The index of the last row in the range
8252 deselectRange : function(startRow, endRow, preventViewNotify){
8256 for(var i = startRow; i <= endRow; i++){
8257 this.deselectRow(i, preventViewNotify);
8263 * @param {Number} row The index of the row to select
8264 * @param {Boolean} keepExisting (optional) True to keep existing selections
8266 selectRow : function(index, keepExisting, preventViewNotify){
8267 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8270 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8271 if(!keepExisting || this.singleSelect){
8272 this.clearSelections();
8274 var r = this.grid.ds.getAt(index);
8275 this.selections.add(r);
8276 this.last = this.lastActive = index;
8277 if(!preventViewNotify){
8278 var view = this.grid.view ? this.grid.view : this.grid;
8279 view.onRowSelect(index);
8281 this.fireEvent("rowselect", this, index, r);
8282 this.fireEvent("selectionchange", this);
8288 * @param {Number} row The index of the row to deselect
8290 deselectRow : function(index, preventViewNotify){
8294 if(this.last == index){
8297 if(this.lastActive == index){
8298 this.lastActive = false;
8300 var r = this.grid.ds.getAt(index);
8301 this.selections.remove(r);
8302 if(!preventViewNotify){
8303 var view = this.grid.view ? this.grid.view : this.grid;
8304 view.onRowDeselect(index);
8306 this.fireEvent("rowdeselect", this, index);
8307 this.fireEvent("selectionchange", this);
8311 restoreLast : function(){
8313 this.last = this._last;
8318 acceptsNav : function(row, col, cm){
8319 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8323 onEditorKey : function(field, e){
8324 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8329 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8331 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8333 }else if(k == e.ENTER && !e.ctrlKey){
8337 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8339 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8341 }else if(k == e.ESC){
8345 g.startEditing(newCell[0], newCell[1]);
8350 * Ext JS Library 1.1.1
8351 * Copyright(c) 2006-2007, Ext JS, LLC.
8353 * Originally Released Under LGPL - original licence link has changed is not relivant.
8356 * <script type="text/javascript">
8361 * @class Roo.grid.ColumnModel
8362 * @extends Roo.util.Observable
8363 * This is the default implementation of a ColumnModel used by the Grid. It defines
8364 * the columns in the grid.
8367 var colModel = new Roo.grid.ColumnModel([
8368 {header: "Ticker", width: 60, sortable: true, locked: true},
8369 {header: "Company Name", width: 150, sortable: true},
8370 {header: "Market Cap.", width: 100, sortable: true},
8371 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8372 {header: "Employees", width: 100, sortable: true, resizable: false}
8377 * The config options listed for this class are options which may appear in each
8378 * individual column definition.
8379 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8381 * @param {Object} config An Array of column config objects. See this class's
8382 * config objects for details.
8384 Roo.grid.ColumnModel = function(config){
8386 * The config passed into the constructor
8388 this.config = []; //config;
8391 // if no id, create one
8392 // if the column does not have a dataIndex mapping,
8393 // map it to the order it is in the config
8394 for(var i = 0, len = config.length; i < len; i++){
8395 this.addColumn(config[i]);
8400 * The width of columns which have no width specified (defaults to 100)
8403 this.defaultWidth = 100;
8406 * Default sortable of columns which have no sortable specified (defaults to false)
8409 this.defaultSortable = false;
8413 * @event widthchange
8414 * Fires when the width of a column changes.
8415 * @param {ColumnModel} this
8416 * @param {Number} columnIndex The column index
8417 * @param {Number} newWidth The new width
8419 "widthchange": true,
8421 * @event headerchange
8422 * Fires when the text of a header changes.
8423 * @param {ColumnModel} this
8424 * @param {Number} columnIndex The column index
8425 * @param {Number} newText The new header text
8427 "headerchange": true,
8429 * @event hiddenchange
8430 * Fires when a column is hidden or "unhidden".
8431 * @param {ColumnModel} this
8432 * @param {Number} columnIndex The column index
8433 * @param {Boolean} hidden true if hidden, false otherwise
8435 "hiddenchange": true,
8437 * @event columnmoved
8438 * Fires when a column is moved.
8439 * @param {ColumnModel} this
8440 * @param {Number} oldIndex
8441 * @param {Number} newIndex
8443 "columnmoved" : true,
8445 * @event columlockchange
8446 * Fires when a column's locked state is changed
8447 * @param {ColumnModel} this
8448 * @param {Number} colIndex
8449 * @param {Boolean} locked true if locked
8451 "columnlockchange" : true
8453 Roo.grid.ColumnModel.superclass.constructor.call(this);
8455 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8457 * @cfg {String} header The header text to display in the Grid view.
8460 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8463 * @cfg {String} smHeader Header at Bootsrap Small width
8466 * @cfg {String} mdHeader Header at Bootsrap Medium width
8469 * @cfg {String} lgHeader Header at Bootsrap Large width
8472 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8475 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8476 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8477 * specified, the column's index is used as an index into the Record's data Array.
8480 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8481 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8484 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8485 * Defaults to the value of the {@link #defaultSortable} property.
8486 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8489 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8492 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8495 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8498 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8501 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8502 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8503 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8504 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8507 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8510 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8513 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8516 * @cfg {String} cursor (Optional)
8519 * @cfg {String} tooltip (Optional)
8522 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8525 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8528 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8531 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8534 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8537 * Returns the id of the column at the specified index.
8538 * @param {Number} index The column index
8539 * @return {String} the id
8541 getColumnId : function(index){
8542 return this.config[index].id;
8546 * Returns the column for a specified id.
8547 * @param {String} id The column id
8548 * @return {Object} the column
8550 getColumnById : function(id){
8551 return this.lookup[id];
8556 * Returns the column Object for a specified dataIndex.
8557 * @param {String} dataIndex The column dataIndex
8558 * @return {Object|Boolean} the column or false if not found
8560 getColumnByDataIndex: function(dataIndex){
8561 var index = this.findColumnIndex(dataIndex);
8562 return index > -1 ? this.config[index] : false;
8566 * Returns the index for a specified column id.
8567 * @param {String} id The column id
8568 * @return {Number} the index, or -1 if not found
8570 getIndexById : function(id){
8571 for(var i = 0, len = this.config.length; i < len; i++){
8572 if(this.config[i].id == id){
8580 * Returns the index for a specified column dataIndex.
8581 * @param {String} dataIndex The column dataIndex
8582 * @return {Number} the index, or -1 if not found
8585 findColumnIndex : function(dataIndex){
8586 for(var i = 0, len = this.config.length; i < len; i++){
8587 if(this.config[i].dataIndex == dataIndex){
8595 moveColumn : function(oldIndex, newIndex){
8596 var c = this.config[oldIndex];
8597 this.config.splice(oldIndex, 1);
8598 this.config.splice(newIndex, 0, c);
8599 this.dataMap = null;
8600 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8603 isLocked : function(colIndex){
8604 return this.config[colIndex].locked === true;
8607 setLocked : function(colIndex, value, suppressEvent){
8608 if(this.isLocked(colIndex) == value){
8611 this.config[colIndex].locked = value;
8613 this.fireEvent("columnlockchange", this, colIndex, value);
8617 getTotalLockedWidth : function(){
8619 for(var i = 0; i < this.config.length; i++){
8620 if(this.isLocked(i) && !this.isHidden(i)){
8621 this.totalWidth += this.getColumnWidth(i);
8627 getLockedCount : function(){
8628 for(var i = 0, len = this.config.length; i < len; i++){
8629 if(!this.isLocked(i)){
8634 return this.config.length;
8638 * Returns the number of columns.
8641 getColumnCount : function(visibleOnly){
8642 if(visibleOnly === true){
8644 for(var i = 0, len = this.config.length; i < len; i++){
8645 if(!this.isHidden(i)){
8651 return this.config.length;
8655 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8656 * @param {Function} fn
8657 * @param {Object} scope (optional)
8658 * @return {Array} result
8660 getColumnsBy : function(fn, scope){
8662 for(var i = 0, len = this.config.length; i < len; i++){
8663 var c = this.config[i];
8664 if(fn.call(scope||this, c, i) === true){
8672 * Returns true if the specified column is sortable.
8673 * @param {Number} col The column index
8676 isSortable : function(col){
8677 if(typeof this.config[col].sortable == "undefined"){
8678 return this.defaultSortable;
8680 return this.config[col].sortable;
8684 * Returns the rendering (formatting) function defined for the column.
8685 * @param {Number} col The column index.
8686 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8688 getRenderer : function(col){
8689 if(!this.config[col].renderer){
8690 return Roo.grid.ColumnModel.defaultRenderer;
8692 return this.config[col].renderer;
8696 * Sets the rendering (formatting) function for a column.
8697 * @param {Number} col The column index
8698 * @param {Function} fn The function to use to process the cell's raw data
8699 * to return HTML markup for the grid view. The render function is called with
8700 * the following parameters:<ul>
8701 * <li>Data value.</li>
8702 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8703 * <li>css A CSS style string to apply to the table cell.</li>
8704 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8705 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8706 * <li>Row index</li>
8707 * <li>Column index</li>
8708 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8710 setRenderer : function(col, fn){
8711 this.config[col].renderer = fn;
8715 * Returns the width for the specified column.
8716 * @param {Number} col The column index
8717 * @param (optional) {String} gridSize bootstrap width size.
8720 getColumnWidth : function(col, gridSize)
8722 var cfg = this.config[col];
8724 if (typeof(gridSize) == 'undefined') {
8725 return cfg.width * 1 || this.defaultWidth;
8727 if (gridSize === false) { // if we set it..
8728 return cfg.width || false;
8730 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8732 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8733 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8736 return cfg[ sizes[i] ];
8743 * Sets the width for a column.
8744 * @param {Number} col The column index
8745 * @param {Number} width The new width
8747 setColumnWidth : function(col, width, suppressEvent){
8748 this.config[col].width = width;
8749 this.totalWidth = null;
8751 this.fireEvent("widthchange", this, col, width);
8756 * Returns the total width of all columns.
8757 * @param {Boolean} includeHidden True to include hidden column widths
8760 getTotalWidth : function(includeHidden){
8761 if(!this.totalWidth){
8762 this.totalWidth = 0;
8763 for(var i = 0, len = this.config.length; i < len; i++){
8764 if(includeHidden || !this.isHidden(i)){
8765 this.totalWidth += this.getColumnWidth(i);
8769 return this.totalWidth;
8773 * Returns the header for the specified column.
8774 * @param {Number} col The column index
8777 getColumnHeader : function(col){
8778 return this.config[col].header;
8782 * Sets the header for a column.
8783 * @param {Number} col The column index
8784 * @param {String} header The new header
8786 setColumnHeader : function(col, header){
8787 this.config[col].header = header;
8788 this.fireEvent("headerchange", this, col, header);
8792 * Returns the tooltip for the specified column.
8793 * @param {Number} col The column index
8796 getColumnTooltip : function(col){
8797 return this.config[col].tooltip;
8800 * Sets the tooltip for a column.
8801 * @param {Number} col The column index
8802 * @param {String} tooltip The new tooltip
8804 setColumnTooltip : function(col, tooltip){
8805 this.config[col].tooltip = tooltip;
8809 * Returns the dataIndex for the specified column.
8810 * @param {Number} col The column index
8813 getDataIndex : function(col){
8814 return this.config[col].dataIndex;
8818 * Sets the dataIndex for a column.
8819 * @param {Number} col The column index
8820 * @param {Number} dataIndex The new dataIndex
8822 setDataIndex : function(col, dataIndex){
8823 this.config[col].dataIndex = dataIndex;
8829 * Returns true if the cell is editable.
8830 * @param {Number} colIndex The column index
8831 * @param {Number} rowIndex The row index - this is nto actually used..?
8834 isCellEditable : function(colIndex, rowIndex){
8835 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8839 * Returns the editor defined for the cell/column.
8840 * return false or null to disable editing.
8841 * @param {Number} colIndex The column index
8842 * @param {Number} rowIndex The row index
8845 getCellEditor : function(colIndex, rowIndex){
8846 return this.config[colIndex].editor;
8850 * Sets if a column is editable.
8851 * @param {Number} col The column index
8852 * @param {Boolean} editable True if the column is editable
8854 setEditable : function(col, editable){
8855 this.config[col].editable = editable;
8860 * Returns true if the column is hidden.
8861 * @param {Number} colIndex The column index
8864 isHidden : function(colIndex){
8865 return this.config[colIndex].hidden;
8870 * Returns true if the column width cannot be changed
8872 isFixed : function(colIndex){
8873 return this.config[colIndex].fixed;
8877 * Returns true if the column can be resized
8880 isResizable : function(colIndex){
8881 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8884 * Sets if a column is hidden.
8885 * @param {Number} colIndex The column index
8886 * @param {Boolean} hidden True if the column is hidden
8888 setHidden : function(colIndex, hidden){
8889 this.config[colIndex].hidden = hidden;
8890 this.totalWidth = null;
8891 this.fireEvent("hiddenchange", this, colIndex, hidden);
8895 * Sets the editor for a column.
8896 * @param {Number} col The column index
8897 * @param {Object} editor The editor object
8899 setEditor : function(col, editor){
8900 this.config[col].editor = editor;
8903 * Add a column (experimental...) - defaults to adding to the end..
8904 * @param {Object} config
8906 addColumn : function(c)
8909 var i = this.config.length;
8912 if(typeof c.dataIndex == "undefined"){
8915 if(typeof c.renderer == "string"){
8916 c.renderer = Roo.util.Format[c.renderer];
8918 if(typeof c.id == "undefined"){
8921 if(c.editor && c.editor.xtype){
8922 c.editor = Roo.factory(c.editor, Roo.grid);
8924 if(c.editor && c.editor.isFormField){
8925 c.editor = new Roo.grid.GridEditor(c.editor);
8927 this.lookup[c.id] = c;
8932 Roo.grid.ColumnModel.defaultRenderer = function(value)
8934 if(typeof value == "object") {
8937 if(typeof value == "string" && value.length < 1){
8941 return String.format("{0}", value);
8944 // Alias for backwards compatibility
8945 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8948 * Ext JS Library 1.1.1
8949 * Copyright(c) 2006-2007, Ext JS, LLC.
8951 * Originally Released Under LGPL - original licence link has changed is not relivant.
8954 * <script type="text/javascript">
8958 * @class Roo.LoadMask
8959 * A simple utility class for generically masking elements while loading data. If the element being masked has
8960 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8961 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8962 * element's UpdateManager load indicator and will be destroyed after the initial load.
8964 * Create a new LoadMask
8965 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8966 * @param {Object} config The config object
8968 Roo.LoadMask = function(el, config){
8969 this.el = Roo.get(el);
8970 Roo.apply(this, config);
8972 this.store.on('beforeload', this.onBeforeLoad, this);
8973 this.store.on('load', this.onLoad, this);
8974 this.store.on('loadexception', this.onLoadException, this);
8975 this.removeMask = false;
8977 var um = this.el.getUpdateManager();
8978 um.showLoadIndicator = false; // disable the default indicator
8979 um.on('beforeupdate', this.onBeforeLoad, this);
8980 um.on('update', this.onLoad, this);
8981 um.on('failure', this.onLoad, this);
8982 this.removeMask = true;
8986 Roo.LoadMask.prototype = {
8988 * @cfg {Boolean} removeMask
8989 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8990 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8995 * The text to display in a centered loading message box (defaults to 'Loading...')
8999 * @cfg {String} msgCls
9000 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9002 msgCls : 'x-mask-loading',
9005 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9011 * Disables the mask to prevent it from being displayed
9013 disable : function(){
9014 this.disabled = true;
9018 * Enables the mask so that it can be displayed
9020 enable : function(){
9021 this.disabled = false;
9024 onLoadException : function()
9028 if (typeof(arguments[3]) != 'undefined') {
9029 Roo.MessageBox.alert("Error loading",arguments[3]);
9033 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9034 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9041 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9046 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9050 onBeforeLoad : function(){
9052 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9057 destroy : function(){
9059 this.store.un('beforeload', this.onBeforeLoad, this);
9060 this.store.un('load', this.onLoad, this);
9061 this.store.un('loadexception', this.onLoadException, this);
9063 var um = this.el.getUpdateManager();
9064 um.un('beforeupdate', this.onBeforeLoad, this);
9065 um.un('update', this.onLoad, this);
9066 um.un('failure', this.onLoad, this);
9070 * @class Roo.bootstrap.Table
9072 * @extends Roo.bootstrap.Component
9073 * @children Roo.bootstrap.TableBody
9074 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9075 * Similar to Roo.grid.Grid
9077 var table = Roo.factory({
9079 xns : Roo.bootstrap,
9080 autoSizeColumns: true,
9087 sortInfo : { direction : 'ASC', field: 'name' },
9089 xtype : 'HttpProxy',
9092 url : 'https://example.com/some.data.url.json'
9095 xtype : 'JsonReader',
9097 fields : [ 'id', 'name', whatever' ],
9104 xtype : 'ColumnModel',
9108 dataIndex : 'is_in_group',
9111 renderer : function(v, x , r) {
9113 return String.format("{0}", v)
9119 xtype : 'RowSelectionModel',
9120 xns : Roo.bootstrap.Table
9121 // you can add listeners to catch selection change here....
9127 grid.render(Roo.get("some-div"));
9130 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9135 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9136 * @cfg {Roo.data.Store} store The data store to use
9137 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9139 * @cfg {String} cls table class
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
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 (drag/drop)
9158 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9161 * Create a new Table
9162 * @param {Object} config The config object
9165 Roo.bootstrap.Table = function(config)
9167 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9170 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9171 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9172 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9173 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9175 this.view = this; // compat with grid.
9177 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9179 this.sm.grid = this;
9180 this.selModel = Roo.factory(this.sm, Roo.grid);
9181 this.sm = this.selModel;
9182 this.sm.xmodule = this.xmodule || false;
9185 if (this.cm && typeof(this.cm.config) == 'undefined') {
9186 this.colModel = new Roo.grid.ColumnModel(this.cm);
9187 this.cm = this.colModel;
9188 this.cm.xmodule = this.xmodule || false;
9191 this.store= Roo.factory(this.store, Roo.data);
9192 this.ds = this.store;
9193 this.ds.xmodule = this.xmodule || false;
9196 if (this.footer && this.store) {
9197 this.footer.dataSource = this.ds;
9198 this.footer = Roo.factory(this.footer);
9205 * Fires when a cell is clicked
9206 * @param {Roo.bootstrap.Table} this
9207 * @param {Roo.Element} el
9208 * @param {Number} rowIndex
9209 * @param {Number} columnIndex
9210 * @param {Roo.EventObject} e
9214 * @event celldblclick
9215 * Fires when a cell is double clicked
9216 * @param {Roo.bootstrap.Table} this
9217 * @param {Roo.Element} el
9218 * @param {Number} rowIndex
9219 * @param {Number} columnIndex
9220 * @param {Roo.EventObject} e
9222 "celldblclick" : true,
9225 * Fires when a row is clicked
9226 * @param {Roo.bootstrap.Table} this
9227 * @param {Roo.Element} el
9228 * @param {Number} rowIndex
9229 * @param {Roo.EventObject} e
9233 * @event rowdblclick
9234 * Fires when a row is double clicked
9235 * @param {Roo.bootstrap.Table} this
9236 * @param {Roo.Element} el
9237 * @param {Number} rowIndex
9238 * @param {Roo.EventObject} e
9240 "rowdblclick" : true,
9243 * Fires when a mouseover occur
9244 * @param {Roo.bootstrap.Table} this
9245 * @param {Roo.Element} el
9246 * @param {Number} rowIndex
9247 * @param {Number} columnIndex
9248 * @param {Roo.EventObject} e
9253 * Fires when a mouseout occur
9254 * @param {Roo.bootstrap.Table} this
9255 * @param {Roo.Element} el
9256 * @param {Number} rowIndex
9257 * @param {Number} columnIndex
9258 * @param {Roo.EventObject} e
9263 * Fires when a row is rendered, so you can change add a style to it.
9264 * @param {Roo.bootstrap.Table} this
9265 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9269 * @event rowsrendered
9270 * Fires when all the rows have been rendered
9271 * @param {Roo.bootstrap.Table} this
9273 'rowsrendered' : true,
9275 * @event contextmenu
9276 * The raw contextmenu event for the entire grid.
9277 * @param {Roo.EventObject} e
9279 "contextmenu" : true,
9281 * @event rowcontextmenu
9282 * Fires when a row is right clicked
9283 * @param {Roo.bootstrap.Table} this
9284 * @param {Number} rowIndex
9285 * @param {Roo.EventObject} e
9287 "rowcontextmenu" : true,
9289 * @event cellcontextmenu
9290 * Fires when a cell is right clicked
9291 * @param {Roo.bootstrap.Table} this
9292 * @param {Number} rowIndex
9293 * @param {Number} cellIndex
9294 * @param {Roo.EventObject} e
9296 "cellcontextmenu" : true,
9298 * @event headercontextmenu
9299 * Fires when a header is right clicked
9300 * @param {Roo.bootstrap.Table} this
9301 * @param {Number} columnIndex
9302 * @param {Roo.EventObject} e
9304 "headercontextmenu" : true,
9307 * The raw mousedown event for the entire grid.
9308 * @param {Roo.EventObject} e
9315 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9331 enableColumnResize: true,
9333 rowSelection : false,
9334 cellSelection : false,
9337 minColumnWidth : 50,
9339 // Roo.Element - the tbody
9340 bodyEl: false, // <tbody> Roo.Element - thead element
9341 headEl: false, // <thead> Roo.Element - thead element
9342 resizeProxy : false, // proxy element for dragging?
9346 container: false, // used by gridpanel...
9352 auto_hide_footer : false,
9354 view: false, // actually points to this..
9356 getAutoCreate : function()
9358 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9365 // this get's auto added by panel.Grid
9366 if (this.scrollBody) {
9367 cfg.cls += ' table-body-fixed';
9370 cfg.cls += ' table-striped';
9374 cfg.cls += ' table-hover';
9376 if (this.bordered) {
9377 cfg.cls += ' table-bordered';
9379 if (this.condensed) {
9380 cfg.cls += ' table-condensed';
9383 if (this.responsive) {
9384 cfg.cls += ' table-responsive';
9388 cfg.cls+= ' ' +this.cls;
9394 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9397 if(this.store || this.cm){
9398 if(this.headerShow){
9399 cfg.cn.push(this.renderHeader());
9402 cfg.cn.push(this.renderBody());
9404 if(this.footerShow){
9405 cfg.cn.push(this.renderFooter());
9407 // where does this come from?
9408 //cfg.cls+= ' TableGrid';
9411 return { cn : [ cfg ] };
9414 initEvents : function()
9416 if(!this.store || !this.cm){
9419 if (this.selModel) {
9420 this.selModel.initEvents();
9424 //Roo.log('initEvents with ds!!!!');
9426 this.bodyEl = this.el.select('tbody', true).first();
9427 this.headEl = this.el.select('thead', true).first();
9428 this.mainFoot = this.el.select('tfoot', true).first();
9433 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9434 e.on('click', this.sort, this);
9438 // why is this done????? = it breaks dialogs??
9439 //this.parent().el.setStyle('position', 'relative');
9443 this.footer.parentId = this.id;
9444 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9447 this.el.select('tfoot tr td').first().addClass('hide');
9452 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9455 this.store.on('load', this.onLoad, this);
9456 this.store.on('beforeload', this.onBeforeLoad, this);
9457 this.store.on('update', this.onUpdate, this);
9458 this.store.on('add', this.onAdd, this);
9459 this.store.on("clear", this.clear, this);
9461 this.el.on("contextmenu", this.onContextMenu, this);
9464 this.cm.on("headerchange", this.onHeaderChange, this);
9465 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9467 //?? does bodyEl get replaced on render?
9468 this.bodyEl.on("click", this.onClick, this);
9469 this.bodyEl.on("dblclick", this.onDblClick, this);
9470 this.bodyEl.on('scroll', this.onBodyScroll, this);
9472 // guessing mainbody will work - this relays usually caught by selmodel at present.
9473 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9476 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9479 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9480 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9485 // Compatibility with grid - we implement all the view features at present.
9486 getView : function()
9491 initCSS : function()
9495 var cm = this.cm, styles = [];
9496 this.CSS.removeStyleSheet(this.id + '-cssrules');
9497 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9498 // we can honour xs/sm/md/xl as widths...
9499 // we first have to decide what widht we are currently at...
9500 var sz = Roo.getGridSize();
9504 var cols = []; // visable cols.
9506 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9507 var w = cm.getColumnWidth(i, false);
9509 cols.push( { rel : false, abs : 0 });
9513 cols.push( { rel : false, abs : w });
9515 last = i; // not really..
9518 var w = cm.getColumnWidth(i, sz);
9523 cols.push( { rel : w, abs : false });
9526 var avail = this.bodyEl.dom.clientWidth - total_abs;
9528 var unitWidth = Math.floor(avail / total);
9529 var rem = avail - (unitWidth * total);
9531 var hidden, width, pos = 0 , splithide , left;
9532 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9534 hidden = 'display:none;';
9536 width = 'width:0px;';
9538 if(!cm.isHidden(i)){
9542 // we can honour xs/sm/md/xl ?
9543 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9545 hidden = 'display:none;';
9547 // width should return a small number...
9549 w+=rem; // add the remaining with..
9552 left = "left:" + (pos -4) + "px;";
9553 width = "width:" + w+ "px;";
9556 if (this.responsive) {
9559 hidden = cm.isHidden(i) ? 'display:none;' : '';
9560 splithide = 'display: none;';
9563 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9566 splithide = 'display:none;';
9569 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9570 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9575 //Roo.log(styles.join(''));
9576 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9582 onContextMenu : function(e, t)
9584 this.processEvent("contextmenu", e);
9587 processEvent : function(name, e)
9589 if (name != 'touchstart' ) {
9590 this.fireEvent(name, e);
9593 var t = e.getTarget();
9595 var cell = Roo.get(t);
9601 if(cell.findParent('tfoot', false, true)){
9605 if(cell.findParent('thead', false, true)){
9607 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9608 cell = Roo.get(t).findParent('th', false, true);
9610 Roo.log("failed to find th in thead?");
9611 Roo.log(e.getTarget());
9616 var cellIndex = cell.dom.cellIndex;
9618 var ename = name == 'touchstart' ? 'click' : name;
9619 this.fireEvent("header" + ename, this, cellIndex, e);
9624 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9625 cell = Roo.get(t).findParent('td', false, true);
9627 Roo.log("failed to find th in tbody?");
9628 Roo.log(e.getTarget());
9633 var row = cell.findParent('tr', false, true);
9634 var cellIndex = cell.dom.cellIndex;
9635 var rowIndex = row.dom.rowIndex - 1;
9639 this.fireEvent("row" + name, this, rowIndex, e);
9643 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9649 onMouseover : function(e, el)
9651 var cell = Roo.get(el);
9657 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9658 cell = cell.findParent('td', false, true);
9661 var row = cell.findParent('tr', false, true);
9662 var cellIndex = cell.dom.cellIndex;
9663 var rowIndex = row.dom.rowIndex - 1; // start from 0
9665 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9669 onMouseout : function(e, el)
9671 var cell = Roo.get(el);
9677 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9678 cell = cell.findParent('td', false, true);
9681 var row = cell.findParent('tr', false, true);
9682 var cellIndex = cell.dom.cellIndex;
9683 var rowIndex = row.dom.rowIndex - 1; // start from 0
9685 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9689 onClick : function(e, el)
9691 var cell = Roo.get(el);
9693 if(!cell || (!this.cellSelection && !this.rowSelection)){
9697 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9698 cell = cell.findParent('td', false, true);
9701 if(!cell || typeof(cell) == 'undefined'){
9705 var row = cell.findParent('tr', false, true);
9707 if(!row || typeof(row) == 'undefined'){
9711 var cellIndex = cell.dom.cellIndex;
9712 var rowIndex = this.getRowIndex(row);
9714 // why??? - should these not be based on SelectionModel?
9715 //if(this.cellSelection){
9716 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9719 //if(this.rowSelection){
9720 this.fireEvent('rowclick', this, row, rowIndex, e);
9725 onDblClick : function(e,el)
9727 var cell = Roo.get(el);
9729 if(!cell || (!this.cellSelection && !this.rowSelection)){
9733 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9734 cell = cell.findParent('td', false, true);
9737 if(!cell || typeof(cell) == 'undefined'){
9741 var row = cell.findParent('tr', false, true);
9743 if(!row || typeof(row) == 'undefined'){
9747 var cellIndex = cell.dom.cellIndex;
9748 var rowIndex = this.getRowIndex(row);
9750 if(this.cellSelection){
9751 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9754 if(this.rowSelection){
9755 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9758 findRowIndex : function(el)
9760 var cell = Roo.get(el);
9764 var row = cell.findParent('tr', false, true);
9766 if(!row || typeof(row) == 'undefined'){
9769 return this.getRowIndex(row);
9771 sort : function(e,el)
9773 var col = Roo.get(el);
9775 if(!col.hasClass('sortable')){
9779 var sort = col.attr('sort');
9782 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9786 this.store.sortInfo = {field : sort, direction : dir};
9789 Roo.log("calling footer first");
9790 this.footer.onClick('first');
9793 this.store.load({ params : { start : 0 } });
9797 renderHeader : function()
9805 this.totalWidth = 0;
9807 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9809 var config = cm.config[i];
9813 cls : 'x-hcol-' + i,
9816 html: cm.getColumnHeader(i)
9819 var tooltip = cm.getColumnTooltip(i);
9821 c.tooltip = tooltip;
9827 if(typeof(config.sortable) != 'undefined' && config.sortable){
9828 c.cls += ' sortable';
9829 c.html = '<i class="fa"></i>' + c.html;
9832 // could use BS4 hidden-..-down
9834 if(typeof(config.lgHeader) != 'undefined'){
9835 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9838 if(typeof(config.mdHeader) != 'undefined'){
9839 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9842 if(typeof(config.smHeader) != 'undefined'){
9843 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9846 if(typeof(config.xsHeader) != 'undefined'){
9847 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9854 if(typeof(config.tooltip) != 'undefined'){
9855 c.tooltip = config.tooltip;
9858 if(typeof(config.colspan) != 'undefined'){
9859 c.colspan = config.colspan;
9862 // hidden is handled by CSS now
9864 if(typeof(config.dataIndex) != 'undefined'){
9865 c.sort = config.dataIndex;
9870 if(typeof(config.align) != 'undefined' && config.align.length){
9871 c.style += ' text-align:' + config.align + ';';
9874 /* width is done in CSS
9875 *if(typeof(config.width) != 'undefined'){
9876 c.style += ' width:' + config.width + 'px;';
9877 this.totalWidth += config.width;
9879 this.totalWidth += 100; // assume minimum of 100 per column?
9883 if(typeof(config.cls) != 'undefined'){
9884 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9886 // this is the bit that doesnt reall work at all...
9888 if (this.responsive) {
9891 ['xs','sm','md','lg'].map(function(size){
9893 if(typeof(config[size]) == 'undefined'){
9897 if (!config[size]) { // 0 = hidden
9898 // BS 4 '0' is treated as hide that column and below.
9899 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9903 c.cls += ' col-' + size + '-' + config[size] + (
9904 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9912 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9923 renderBody : function()
9933 colspan : this.cm.getColumnCount()
9943 renderFooter : function()
9953 colspan : this.cm.getColumnCount()
9967 // Roo.log('ds onload');
9972 var ds = this.store;
9974 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9975 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9976 if (_this.store.sortInfo) {
9978 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9979 e.select('i', true).addClass(['fa-arrow-up']);
9982 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9983 e.select('i', true).addClass(['fa-arrow-down']);
9988 var tbody = this.bodyEl;
9990 if(ds.getCount() > 0){
9991 ds.data.each(function(d,rowIndex){
9992 var row = this.renderRow(cm, ds, rowIndex);
9994 tbody.createChild(row);
9998 if(row.cellObjects.length){
9999 Roo.each(row.cellObjects, function(r){
10000 _this.renderCellObject(r);
10007 var tfoot = this.el.select('tfoot', true).first();
10009 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10011 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10013 var total = this.ds.getTotalCount();
10015 if(this.footer.pageSize < total){
10016 this.mainFoot.show();
10020 Roo.each(this.el.select('tbody td', true).elements, function(e){
10021 e.on('mouseover', _this.onMouseover, _this);
10024 Roo.each(this.el.select('tbody td', true).elements, function(e){
10025 e.on('mouseout', _this.onMouseout, _this);
10027 this.fireEvent('rowsrendered', this);
10031 this.initCSS(); /// resize cols
10037 onUpdate : function(ds,record)
10039 this.refreshRow(record);
10043 onRemove : function(ds, record, index, isUpdate){
10044 if(isUpdate !== true){
10045 this.fireEvent("beforerowremoved", this, index, record);
10047 var bt = this.bodyEl.dom;
10049 var rows = this.el.select('tbody > tr', true).elements;
10051 if(typeof(rows[index]) != 'undefined'){
10052 bt.removeChild(rows[index].dom);
10055 // if(bt.rows[index]){
10056 // bt.removeChild(bt.rows[index]);
10059 if(isUpdate !== true){
10060 //this.stripeRows(index);
10061 //this.syncRowHeights(index, index);
10063 this.fireEvent("rowremoved", this, index, record);
10067 onAdd : function(ds, records, rowIndex)
10069 //Roo.log('on Add called');
10070 // - note this does not handle multiple adding very well..
10071 var bt = this.bodyEl.dom;
10072 for (var i =0 ; i < records.length;i++) {
10073 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10074 //Roo.log(records[i]);
10075 //Roo.log(this.store.getAt(rowIndex+i));
10076 this.insertRow(this.store, rowIndex + i, false);
10083 refreshRow : function(record){
10084 var ds = this.store, index;
10085 if(typeof record == 'number'){
10087 record = ds.getAt(index);
10089 index = ds.indexOf(record);
10091 return; // should not happen - but seems to
10094 this.insertRow(ds, index, true);
10096 this.onRemove(ds, record, index+1, true);
10098 //this.syncRowHeights(index, index);
10100 this.fireEvent("rowupdated", this, index, record);
10102 // private - called by RowSelection
10103 onRowSelect : function(rowIndex){
10104 var row = this.getRowDom(rowIndex);
10105 row.addClass(['bg-info','info']);
10107 // private - called by RowSelection
10108 onRowDeselect : function(rowIndex)
10110 if (rowIndex < 0) {
10113 var row = this.getRowDom(rowIndex);
10114 row.removeClass(['bg-info','info']);
10117 * Focuses the specified row.
10118 * @param {Number} row The row index
10120 focusRow : function(row)
10122 //Roo.log('GridView.focusRow');
10123 var x = this.bodyEl.dom.scrollLeft;
10124 this.focusCell(row, 0, false);
10125 this.bodyEl.dom.scrollLeft = x;
10129 * Focuses the specified cell.
10130 * @param {Number} row The row index
10131 * @param {Number} col The column index
10132 * @param {Boolean} hscroll false to disable horizontal scrolling
10134 focusCell : function(row, col, hscroll)
10136 //Roo.log('GridView.focusCell');
10137 var el = this.ensureVisible(row, col, hscroll);
10138 // not sure what focusEL achives = it's a <a> pos relative
10139 //this.focusEl.alignTo(el, "tl-tl");
10141 // this.focusEl.focus();
10143 // this.focusEl.focus.defer(1, this.focusEl);
10148 * Scrolls the specified cell into view
10149 * @param {Number} row The row index
10150 * @param {Number} col The column index
10151 * @param {Boolean} hscroll false to disable horizontal scrolling
10153 ensureVisible : function(row, col, hscroll)
10155 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10156 //return null; //disable for testing.
10157 if(typeof row != "number"){
10158 row = row.rowIndex;
10160 if(row < 0 && row >= this.ds.getCount()){
10163 col = (col !== undefined ? col : 0);
10165 while(cm.isHidden(col)){
10169 var el = this.getCellDom(row, col);
10173 var c = this.bodyEl.dom;
10175 var ctop = parseInt(el.offsetTop, 10);
10176 var cleft = parseInt(el.offsetLeft, 10);
10177 var cbot = ctop + el.offsetHeight;
10178 var cright = cleft + el.offsetWidth;
10180 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10181 var ch = 0; //?? header is not withing the area?
10182 var stop = parseInt(c.scrollTop, 10);
10183 var sleft = parseInt(c.scrollLeft, 10);
10184 var sbot = stop + ch;
10185 var sright = sleft + c.clientWidth;
10187 Roo.log('GridView.ensureVisible:' +
10189 ' c.clientHeight:' + c.clientHeight +
10190 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10198 c.scrollTop = ctop;
10199 //Roo.log("set scrolltop to ctop DISABLE?");
10200 }else if(cbot > sbot){
10201 //Roo.log("set scrolltop to cbot-ch");
10202 c.scrollTop = cbot-ch;
10205 if(hscroll !== false){
10207 c.scrollLeft = cleft;
10208 }else if(cright > sright){
10209 c.scrollLeft = cright-c.clientWidth;
10217 insertRow : function(dm, rowIndex, isUpdate){
10220 this.fireEvent("beforerowsinserted", this, rowIndex);
10222 //var s = this.getScrollState();
10223 var row = this.renderRow(this.cm, this.store, rowIndex);
10224 // insert before rowIndex..
10225 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10229 if(row.cellObjects.length){
10230 Roo.each(row.cellObjects, function(r){
10231 _this.renderCellObject(r);
10236 this.fireEvent("rowsinserted", this, rowIndex);
10237 //this.syncRowHeights(firstRow, lastRow);
10238 //this.stripeRows(firstRow);
10245 getRowDom : function(rowIndex)
10247 var rows = this.el.select('tbody > tr', true).elements;
10249 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10252 getCellDom : function(rowIndex, colIndex)
10254 var row = this.getRowDom(rowIndex);
10255 if (row === false) {
10258 var cols = row.select('td', true).elements;
10259 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10263 // returns the object tree for a tr..
10266 renderRow : function(cm, ds, rowIndex)
10268 var d = ds.getAt(rowIndex);
10272 cls : 'x-row-' + rowIndex,
10276 var cellObjects = [];
10278 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10279 var config = cm.config[i];
10281 var renderer = cm.getRenderer(i);
10285 if(typeof(renderer) !== 'undefined'){
10286 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10288 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10289 // and are rendered into the cells after the row is rendered - using the id for the element.
10291 if(typeof(value) === 'object'){
10301 rowIndex : rowIndex,
10306 this.fireEvent('rowclass', this, rowcfg);
10310 // this might end up displaying HTML?
10311 // this is too messy... - better to only do it on columsn you know are going to be too long
10312 //tooltip : (typeof(value) === 'object') ? '' : value,
10313 cls : rowcfg.rowClass + ' x-col-' + i,
10315 html: (typeof(value) === 'object') ? '' : value
10322 if(typeof(config.colspan) != 'undefined'){
10323 td.colspan = config.colspan;
10328 if(typeof(config.align) != 'undefined' && config.align.length){
10329 td.style += ' text-align:' + config.align + ';';
10331 if(typeof(config.valign) != 'undefined' && config.valign.length){
10332 td.style += ' vertical-align:' + config.valign + ';';
10335 if(typeof(config.width) != 'undefined'){
10336 td.style += ' width:' + config.width + 'px;';
10340 if(typeof(config.cursor) != 'undefined'){
10341 td.style += ' cursor:' + config.cursor + ';';
10344 if(typeof(config.cls) != 'undefined'){
10345 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10347 if (this.responsive) {
10348 ['xs','sm','md','lg'].map(function(size){
10350 if(typeof(config[size]) == 'undefined'){
10356 if (!config[size]) { // 0 = hidden
10357 // BS 4 '0' is treated as hide that column and below.
10358 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10362 td.cls += ' col-' + size + '-' + config[size] + (
10363 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10373 row.cellObjects = cellObjects;
10381 onBeforeLoad : function()
10390 this.el.select('tbody', true).first().dom.innerHTML = '';
10393 * Show or hide a row.
10394 * @param {Number} rowIndex to show or hide
10395 * @param {Boolean} state hide
10397 setRowVisibility : function(rowIndex, state)
10399 var bt = this.bodyEl.dom;
10401 var rows = this.el.select('tbody > tr', true).elements;
10403 if(typeof(rows[rowIndex]) == 'undefined'){
10406 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10411 getSelectionModel : function(){
10412 if(!this.selModel){
10413 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10415 return this.selModel;
10418 * Render the Roo.bootstrap object from renderder
10420 renderCellObject : function(r)
10424 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10426 var t = r.cfg.render(r.container);
10429 Roo.each(r.cfg.cn, function(c){
10431 container: t.getChildContainer(),
10434 _this.renderCellObject(child);
10439 * get the Row Index from a dom element.
10440 * @param {Roo.Element} row The row to look for
10441 * @returns {Number} the row
10443 getRowIndex : function(row)
10447 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10458 * get the header TH element for columnIndex
10459 * @param {Number} columnIndex
10460 * @returns {Roo.Element}
10462 getHeaderIndex: function(colIndex)
10464 var cols = this.headEl.select('th', true).elements;
10465 return cols[colIndex];
10468 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10469 * @param {domElement} cell to look for
10470 * @returns {Number} the column
10472 getCellIndex : function(cell)
10474 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10476 return parseInt(id[1], 10);
10481 * Returns the grid's underlying element = used by panel.Grid
10482 * @return {Element} The element
10484 getGridEl : function(){
10488 * Forces a resize - used by panel.Grid
10489 * @return {Element} The element
10491 autoSize : function()
10493 //var ctr = Roo.get(this.container.dom.parentElement);
10494 var ctr = Roo.get(this.el.dom);
10496 var thd = this.getGridEl().select('thead',true).first();
10497 var tbd = this.getGridEl().select('tbody', true).first();
10498 var tfd = this.getGridEl().select('tfoot', true).first();
10500 var cw = ctr.getWidth();
10501 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10505 tbd.setWidth(ctr.getWidth());
10506 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10507 // this needs fixing for various usage - currently only hydra job advers I think..
10509 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10511 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10514 cw = Math.max(cw, this.totalWidth);
10515 this.getGridEl().select('tbody tr',true).setWidth(cw);
10518 // resize 'expandable coloumn?
10520 return; // we doe not have a view in this design..
10523 onBodyScroll: function()
10525 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10527 this.headEl.setStyle({
10528 'position' : 'relative',
10529 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10535 var scrollHeight = this.bodyEl.dom.scrollHeight;
10537 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10539 var height = this.bodyEl.getHeight();
10541 if(scrollHeight - height == scrollTop) {
10543 var total = this.ds.getTotalCount();
10545 if(this.footer.cursor + this.footer.pageSize < total){
10547 this.footer.ds.load({
10549 start : this.footer.cursor + this.footer.pageSize,
10550 limit : this.footer.pageSize
10559 onColumnSplitterMoved : function(i, diff)
10561 this.userResized = true;
10563 var cm = this.colModel;
10565 var w = this.getHeaderIndex(i).getWidth() + diff;
10568 cm.setColumnWidth(i, w, true);
10570 //var cid = cm.getColumnId(i); << not used in this version?
10571 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10573 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10574 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10575 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10577 //this.updateSplitters();
10578 //this.layout(); << ??
10579 this.fireEvent("columnresize", i, w);
10581 onHeaderChange : function()
10583 var header = this.renderHeader();
10584 var table = this.el.select('table', true).first();
10586 this.headEl.remove();
10587 this.headEl = table.createChild(header, this.bodyEl, false);
10589 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10590 e.on('click', this.sort, this);
10593 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10594 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10599 onHiddenChange : function(colModel, colIndex, hidden)
10602 this.cm.setHidden()
10603 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10604 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10606 this.CSS.updateRule(thSelector, "display", "");
10607 this.CSS.updateRule(tdSelector, "display", "");
10610 this.CSS.updateRule(thSelector, "display", "none");
10611 this.CSS.updateRule(tdSelector, "display", "none");
10614 // onload calls initCSS()
10615 this.onHeaderChange();
10619 setColumnWidth: function(col_index, width)
10621 // width = "md-2 xs-2..."
10622 if(!this.colModel.config[col_index]) {
10626 var w = width.split(" ");
10628 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10630 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10633 for(var j = 0; j < w.length; j++) {
10639 var size_cls = w[j].split("-");
10641 if(!Number.isInteger(size_cls[1] * 1)) {
10645 if(!this.colModel.config[col_index][size_cls[0]]) {
10649 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10653 h_row[0].classList.replace(
10654 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10655 "col-"+size_cls[0]+"-"+size_cls[1]
10658 for(var i = 0; i < rows.length; i++) {
10660 var size_cls = w[j].split("-");
10662 if(!Number.isInteger(size_cls[1] * 1)) {
10666 if(!this.colModel.config[col_index][size_cls[0]]) {
10670 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674 rows[i].classList.replace(
10675 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10676 "col-"+size_cls[0]+"-"+size_cls[1]
10680 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10685 // currently only used to find the split on drag..
10686 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10691 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10692 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10701 * @class Roo.bootstrap.TableCell
10702 * @extends Roo.bootstrap.Component
10703 * @children Roo.bootstrap.Component
10704 * @parent Roo.bootstrap.TableRow
10705 * Bootstrap TableCell class
10707 * @cfg {String} html cell contain text
10708 * @cfg {String} cls cell class
10709 * @cfg {String} tag cell tag (td|th) default td
10710 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10711 * @cfg {String} align Aligns the content in a cell
10712 * @cfg {String} axis Categorizes cells
10713 * @cfg {String} bgcolor Specifies the background color of a cell
10714 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10715 * @cfg {Number} colspan Specifies the number of columns a cell should span
10716 * @cfg {String} headers Specifies one or more header cells a cell is related to
10717 * @cfg {Number} height Sets the height of a cell
10718 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10719 * @cfg {Number} rowspan Sets the number of rows a cell should span
10720 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10721 * @cfg {String} valign Vertical aligns the content in a cell
10722 * @cfg {Number} width Specifies the width of a cell
10725 * Create a new TableCell
10726 * @param {Object} config The config object
10729 Roo.bootstrap.TableCell = function(config){
10730 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10733 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10753 getAutoCreate : function(){
10754 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10761 cfg.tag = this.tag;
10774 cfg.align=this.align
10779 if (this.bgcolor) {
10780 cfg.bgcolor=this.bgcolor
10782 if (this.charoff) {
10783 cfg.charoff=this.charoff
10785 if (this.colspan) {
10786 cfg.colspan=this.colspan
10788 if (this.headers) {
10789 cfg.headers=this.headers
10792 cfg.height=this.height
10795 cfg.nowrap=this.nowrap
10797 if (this.rowspan) {
10798 cfg.rowspan=this.rowspan
10801 cfg.scope=this.scope
10804 cfg.valign=this.valign
10807 cfg.width=this.width
10826 * @class Roo.bootstrap.TableRow
10827 * @extends Roo.bootstrap.Component
10828 * @children Roo.bootstrap.TableCell
10829 * @parent Roo.bootstrap.TableBody
10830 * Bootstrap TableRow class
10831 * @cfg {String} cls row class
10832 * @cfg {String} align Aligns the content in a table row
10833 * @cfg {String} bgcolor Specifies a background color for a table row
10834 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10835 * @cfg {String} valign Vertical aligns the content in a table row
10838 * Create a new TableRow
10839 * @param {Object} config The config object
10842 Roo.bootstrap.TableRow = function(config){
10843 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10846 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10854 getAutoCreate : function(){
10855 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10862 cfg.cls = this.cls;
10865 cfg.align = this.align;
10868 cfg.bgcolor = this.bgcolor;
10871 cfg.charoff = this.charoff;
10874 cfg.valign = this.valign;
10892 * @class Roo.bootstrap.TableBody
10893 * @extends Roo.bootstrap.Component
10894 * @children Roo.bootstrap.TableRow
10895 * @parent Roo.bootstrap.Table
10896 * Bootstrap TableBody class
10897 * @cfg {String} cls element class
10898 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10899 * @cfg {String} align Aligns the content inside the element
10900 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10901 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10904 * Create a new TableBody
10905 * @param {Object} config The config object
10908 Roo.bootstrap.TableBody = function(config){
10909 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10912 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10920 getAutoCreate : function(){
10921 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10931 cfg.tag = this.tag;
10935 cfg.align = this.align;
10938 cfg.charoff = this.charoff;
10941 cfg.valign = this.valign;
10948 // initEvents : function()
10951 // if(!this.store){
10955 // this.store = Roo.factory(this.store, Roo.data);
10956 // this.store.on('load', this.onLoad, this);
10958 // this.store.load();
10962 // onLoad: function ()
10964 // this.fireEvent('load', this);
10974 * Ext JS Library 1.1.1
10975 * Copyright(c) 2006-2007, Ext JS, LLC.
10977 * Originally Released Under LGPL - original licence link has changed is not relivant.
10980 * <script type="text/javascript">
10983 // as we use this in bootstrap.
10984 Roo.namespace('Roo.form');
10986 * @class Roo.form.Action
10987 * Internal Class used to handle form actions
10989 * @param {Roo.form.BasicForm} el The form element or its id
10990 * @param {Object} config Configuration options
10995 // define the action interface
10996 Roo.form.Action = function(form, options){
10998 this.options = options || {};
11001 * Client Validation Failed
11004 Roo.form.Action.CLIENT_INVALID = 'client';
11006 * Server Validation Failed
11009 Roo.form.Action.SERVER_INVALID = 'server';
11011 * Connect to Server Failed
11014 Roo.form.Action.CONNECT_FAILURE = 'connect';
11016 * Reading Data from Server Failed
11019 Roo.form.Action.LOAD_FAILURE = 'load';
11021 Roo.form.Action.prototype = {
11023 failureType : undefined,
11024 response : undefined,
11025 result : undefined,
11027 // interface method
11028 run : function(options){
11032 // interface method
11033 success : function(response){
11037 // interface method
11038 handleResponse : function(response){
11042 // default connection failure
11043 failure : function(response){
11045 this.response = response;
11046 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11047 this.form.afterAction(this, false);
11050 processResponse : function(response){
11051 this.response = response;
11052 if(!response.responseText){
11055 this.result = this.handleResponse(response);
11056 return this.result;
11059 // utility functions used internally
11060 getUrl : function(appendParams){
11061 var url = this.options.url || this.form.url || this.form.el.dom.action;
11063 var p = this.getParams();
11065 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11071 getMethod : function(){
11072 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11075 getParams : function(){
11076 var bp = this.form.baseParams;
11077 var p = this.options.params;
11079 if(typeof p == "object"){
11080 p = Roo.urlEncode(Roo.applyIf(p, bp));
11081 }else if(typeof p == 'string' && bp){
11082 p += '&' + Roo.urlEncode(bp);
11085 p = Roo.urlEncode(bp);
11090 createCallback : function(){
11092 success: this.success,
11093 failure: this.failure,
11095 timeout: (this.form.timeout*1000),
11096 upload: this.form.fileUpload ? this.success : undefined
11101 Roo.form.Action.Submit = function(form, options){
11102 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11105 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11108 haveProgress : false,
11109 uploadComplete : false,
11111 // uploadProgress indicator.
11112 uploadProgress : function()
11114 if (!this.form.progressUrl) {
11118 if (!this.haveProgress) {
11119 Roo.MessageBox.progress("Uploading", "Uploading");
11121 if (this.uploadComplete) {
11122 Roo.MessageBox.hide();
11126 this.haveProgress = true;
11128 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11130 var c = new Roo.data.Connection();
11132 url : this.form.progressUrl,
11137 success : function(req){
11138 //console.log(data);
11142 rdata = Roo.decode(req.responseText)
11144 Roo.log("Invalid data from server..");
11148 if (!rdata || !rdata.success) {
11150 Roo.MessageBox.alert(Roo.encode(rdata));
11153 var data = rdata.data;
11155 if (this.uploadComplete) {
11156 Roo.MessageBox.hide();
11161 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11162 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11165 this.uploadProgress.defer(2000,this);
11168 failure: function(data) {
11169 Roo.log('progress url failed ');
11180 // run get Values on the form, so it syncs any secondary forms.
11181 this.form.getValues();
11183 var o = this.options;
11184 var method = this.getMethod();
11185 var isPost = method == 'POST';
11186 if(o.clientValidation === false || this.form.isValid()){
11188 if (this.form.progressUrl) {
11189 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11190 (new Date() * 1) + '' + Math.random());
11195 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11196 form:this.form.el.dom,
11197 url:this.getUrl(!isPost),
11199 params:isPost ? this.getParams() : null,
11200 isUpload: this.form.fileUpload,
11201 formData : this.form.formData
11204 this.uploadProgress();
11206 }else if (o.clientValidation !== false){ // client validation failed
11207 this.failureType = Roo.form.Action.CLIENT_INVALID;
11208 this.form.afterAction(this, false);
11212 success : function(response)
11214 this.uploadComplete= true;
11215 if (this.haveProgress) {
11216 Roo.MessageBox.hide();
11220 var result = this.processResponse(response);
11221 if(result === true || result.success){
11222 this.form.afterAction(this, true);
11226 this.form.markInvalid(result.errors);
11227 this.failureType = Roo.form.Action.SERVER_INVALID;
11229 this.form.afterAction(this, false);
11231 failure : function(response)
11233 this.uploadComplete= true;
11234 if (this.haveProgress) {
11235 Roo.MessageBox.hide();
11238 this.response = response;
11239 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11240 this.form.afterAction(this, false);
11243 handleResponse : function(response){
11244 if(this.form.errorReader){
11245 var rs = this.form.errorReader.read(response);
11248 for(var i = 0, len = rs.records.length; i < len; i++) {
11249 var r = rs.records[i];
11250 errors[i] = r.data;
11253 if(errors.length < 1){
11257 success : rs.success,
11263 ret = Roo.decode(response.responseText);
11267 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11277 Roo.form.Action.Load = function(form, options){
11278 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11279 this.reader = this.form.reader;
11282 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11287 Roo.Ajax.request(Roo.apply(
11288 this.createCallback(), {
11289 method:this.getMethod(),
11290 url:this.getUrl(false),
11291 params:this.getParams()
11295 success : function(response){
11297 var result = this.processResponse(response);
11298 if(result === true || !result.success || !result.data){
11299 this.failureType = Roo.form.Action.LOAD_FAILURE;
11300 this.form.afterAction(this, false);
11303 this.form.clearInvalid();
11304 this.form.setValues(result.data);
11305 this.form.afterAction(this, true);
11308 handleResponse : function(response){
11309 if(this.form.reader){
11310 var rs = this.form.reader.read(response);
11311 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11313 success : rs.success,
11317 return Roo.decode(response.responseText);
11321 Roo.form.Action.ACTION_TYPES = {
11322 'load' : Roo.form.Action.Load,
11323 'submit' : Roo.form.Action.Submit
11332 * @class Roo.bootstrap.form.Form
11333 * @extends Roo.bootstrap.Component
11334 * @children Roo.bootstrap.Component
11335 * Bootstrap Form class
11336 * @cfg {String} method GET | POST (default POST)
11337 * @cfg {String} labelAlign top | left (default top)
11338 * @cfg {String} align left | right - for navbars
11339 * @cfg {Boolean} loadMask load mask when submit (default true)
11343 * Create a new Form
11344 * @param {Object} config The config object
11348 Roo.bootstrap.form.Form = function(config){
11350 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11352 Roo.bootstrap.form.Form.popover.apply();
11356 * @event clientvalidation
11357 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11358 * @param {Form} this
11359 * @param {Boolean} valid true if the form has passed client-side validation
11361 clientvalidation: true,
11363 * @event beforeaction
11364 * Fires before any action is performed. Return false to cancel the action.
11365 * @param {Form} this
11366 * @param {Action} action The action to be performed
11368 beforeaction: true,
11370 * @event actionfailed
11371 * Fires when an action fails.
11372 * @param {Form} this
11373 * @param {Action} action The action that failed
11375 actionfailed : true,
11377 * @event actioncomplete
11378 * Fires when an action is completed.
11379 * @param {Form} this
11380 * @param {Action} action The action that completed
11382 actioncomplete : true
11386 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11389 * @cfg {String} method
11390 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11394 * @cfg {String} url
11395 * The URL to use for form actions if one isn't supplied in the action options.
11398 * @cfg {Boolean} fileUpload
11399 * Set to true if this form is a file upload.
11403 * @cfg {Object} baseParams
11404 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11408 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11412 * @cfg {Sting} align (left|right) for navbar forms
11417 activeAction : null,
11420 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11421 * element by passing it or its id or mask the form itself by passing in true.
11424 waitMsgTarget : false,
11429 * @cfg {Boolean} errorMask (true|false) default false
11434 * @cfg {Number} maskOffset Default 100
11439 * @cfg {Boolean} maskBody
11443 getAutoCreate : function(){
11447 method : this.method || 'POST',
11448 id : this.id || Roo.id(),
11451 if (this.parent().xtype.match(/^Nav/)) {
11452 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11456 if (this.labelAlign == 'left' ) {
11457 cfg.cls += ' form-horizontal';
11463 initEvents : function()
11465 this.el.on('submit', this.onSubmit, this);
11466 // this was added as random key presses on the form where triggering form submit.
11467 this.el.on('keypress', function(e) {
11468 if (e.getCharCode() != 13) {
11471 // we might need to allow it for textareas.. and some other items.
11472 // check e.getTarget().
11474 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11478 Roo.log("keypress blocked");
11480 e.preventDefault();
11486 onSubmit : function(e){
11491 * Returns true if client-side validation on the form is successful.
11494 isValid : function(){
11495 var items = this.getItems();
11497 var target = false;
11499 items.each(function(f){
11505 Roo.log('invalid field: ' + f.name);
11509 if(!target && f.el.isVisible(true)){
11515 if(this.errorMask && !valid){
11516 Roo.bootstrap.form.Form.popover.mask(this, target);
11523 * Returns true if any fields in this form have changed since their original load.
11526 isDirty : function(){
11528 var items = this.getItems();
11529 items.each(function(f){
11539 * Performs a predefined action (submit or load) or custom actions you define on this form.
11540 * @param {String} actionName The name of the action type
11541 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11542 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11543 * accept other config options):
11545 Property Type Description
11546 ---------------- --------------- ----------------------------------------------------------------------------------
11547 url String The url for the action (defaults to the form's url)
11548 method String The form method to use (defaults to the form's method, or POST if not defined)
11549 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11550 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11551 validate the form on the client (defaults to false)
11553 * @return {BasicForm} this
11555 doAction : function(action, options){
11556 if(typeof action == 'string'){
11557 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11559 if(this.fireEvent('beforeaction', this, action) !== false){
11560 this.beforeAction(action);
11561 action.run.defer(100, action);
11567 beforeAction : function(action){
11568 var o = action.options;
11573 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11575 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11578 // not really supported yet.. ??
11580 //if(this.waitMsgTarget === true){
11581 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582 //}else if(this.waitMsgTarget){
11583 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11584 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11586 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11592 afterAction : function(action, success){
11593 this.activeAction = null;
11594 var o = action.options;
11599 Roo.get(document.body).unmask();
11605 //if(this.waitMsgTarget === true){
11606 // this.el.unmask();
11607 //}else if(this.waitMsgTarget){
11608 // this.waitMsgTarget.unmask();
11610 // Roo.MessageBox.updateProgress(1);
11611 // Roo.MessageBox.hide();
11618 Roo.callback(o.success, o.scope, [this, action]);
11619 this.fireEvent('actioncomplete', this, action);
11623 // failure condition..
11624 // we have a scenario where updates need confirming.
11625 // eg. if a locking scenario exists..
11626 // we look for { errors : { needs_confirm : true }} in the response.
11628 (typeof(action.result) != 'undefined') &&
11629 (typeof(action.result.errors) != 'undefined') &&
11630 (typeof(action.result.errors.needs_confirm) != 'undefined')
11633 Roo.log("not supported yet");
11636 Roo.MessageBox.confirm(
11637 "Change requires confirmation",
11638 action.result.errorMsg,
11643 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11653 Roo.callback(o.failure, o.scope, [this, action]);
11654 // show an error message if no failed handler is set..
11655 if (!this.hasListener('actionfailed')) {
11656 Roo.log("need to add dialog support");
11658 Roo.MessageBox.alert("Error",
11659 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11660 action.result.errorMsg :
11661 "Saving Failed, please check your entries or try again"
11666 this.fireEvent('actionfailed', this, action);
11671 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11672 * @param {String} id The value to search for
11675 findField : function(id){
11676 var items = this.getItems();
11677 var field = items.get(id);
11679 items.each(function(f){
11680 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11687 return field || null;
11690 * Mark fields in this form invalid in bulk.
11691 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11692 * @return {BasicForm} this
11694 markInvalid : function(errors){
11695 if(errors instanceof Array){
11696 for(var i = 0, len = errors.length; i < len; i++){
11697 var fieldError = errors[i];
11698 var f = this.findField(fieldError.id);
11700 f.markInvalid(fieldError.msg);
11706 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11707 field.markInvalid(errors[id]);
11711 //Roo.each(this.childForms || [], function (f) {
11712 // f.markInvalid(errors);
11719 * Set values for fields in this form in bulk.
11720 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11721 * @return {BasicForm} this
11723 setValues : function(values){
11724 if(values instanceof Array){ // array of objects
11725 for(var i = 0, len = values.length; i < len; i++){
11727 var f = this.findField(v.id);
11729 f.setValue(v.value);
11730 if(this.trackResetOnLoad){
11731 f.originalValue = f.getValue();
11735 }else{ // object hash
11738 if(typeof values[id] != 'function' && (field = this.findField(id))){
11740 if (field.setFromData &&
11741 field.valueField &&
11742 field.displayField &&
11743 // combos' with local stores can
11744 // be queried via setValue()
11745 // to set their value..
11746 (field.store && !field.store.isLocal)
11750 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11751 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11752 field.setFromData(sd);
11754 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11756 field.setFromData(values);
11759 field.setValue(values[id]);
11763 if(this.trackResetOnLoad){
11764 field.originalValue = field.getValue();
11770 //Roo.each(this.childForms || [], function (f) {
11771 // f.setValues(values);
11778 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11779 * they are returned as an array.
11780 * @param {Boolean} asString
11783 getValues : function(asString){
11784 //if (this.childForms) {
11785 // copy values from the child forms
11786 // Roo.each(this.childForms, function (f) {
11787 // this.setValues(f.getValues());
11793 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11794 if(asString === true){
11797 return Roo.urlDecode(fs);
11801 * Returns the fields in this form as an object with key/value pairs.
11802 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11805 getFieldValues : function(with_hidden)
11807 var items = this.getItems();
11809 items.each(function(f){
11811 if (!f.getName()) {
11815 var v = f.getValue();
11817 if (f.inputType =='radio') {
11818 if (typeof(ret[f.getName()]) == 'undefined') {
11819 ret[f.getName()] = ''; // empty..
11822 if (!f.el.dom.checked) {
11826 v = f.el.dom.value;
11830 if(f.xtype == 'MoneyField'){
11831 ret[f.currencyName] = f.getCurrency();
11834 // not sure if this supported any more..
11835 if ((typeof(v) == 'object') && f.getRawValue) {
11836 v = f.getRawValue() ; // dates..
11838 // combo boxes where name != hiddenName...
11839 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11840 ret[f.name] = f.getRawValue();
11842 ret[f.getName()] = v;
11849 * Clears all invalid messages in this form.
11850 * @return {BasicForm} this
11852 clearInvalid : function(){
11853 var items = this.getItems();
11855 items.each(function(f){
11863 * Resets this form.
11864 * @return {BasicForm} this
11866 reset : function(){
11867 var items = this.getItems();
11868 items.each(function(f){
11872 Roo.each(this.childForms || [], function (f) {
11880 getItems : function()
11882 var r=new Roo.util.MixedCollection(false, function(o){
11883 return o.id || (o.id = Roo.id());
11885 var iter = function(el) {
11892 Roo.each(el.items,function(e) {
11901 hideFields : function(items)
11903 Roo.each(items, function(i){
11905 var f = this.findField(i);
11916 showFields : function(items)
11918 Roo.each(items, function(i){
11920 var f = this.findField(i);
11933 Roo.apply(Roo.bootstrap.form.Form, {
11949 intervalID : false,
11955 if(this.isApplied){
11960 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11961 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11962 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11963 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11966 this.maskEl.top.enableDisplayMode("block");
11967 this.maskEl.left.enableDisplayMode("block");
11968 this.maskEl.bottom.enableDisplayMode("block");
11969 this.maskEl.right.enableDisplayMode("block");
11971 this.toolTip = new Roo.bootstrap.Tooltip({
11972 cls : 'roo-form-error-popover',
11974 'left' : ['r-l', [-2,0], 'right'],
11975 'right' : ['l-r', [2,0], 'left'],
11976 'bottom' : ['tl-bl', [0,2], 'top'],
11977 'top' : [ 'bl-tl', [0,-2], 'bottom']
11981 this.toolTip.render(Roo.get(document.body));
11983 this.toolTip.el.enableDisplayMode("block");
11985 Roo.get(document.body).on('click', function(){
11989 Roo.get(document.body).on('touchstart', function(){
11993 this.isApplied = true
11996 mask : function(form, target)
12000 this.target = target;
12002 if(!this.form.errorMask || !target.el){
12006 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12008 Roo.log(scrollable);
12010 var ot = this.target.el.calcOffsetsTo(scrollable);
12012 var scrollTo = ot[1] - this.form.maskOffset;
12014 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12016 scrollable.scrollTo('top', scrollTo);
12018 var box = this.target.el.getBox();
12020 var zIndex = Roo.bootstrap.Modal.zIndex++;
12023 this.maskEl.top.setStyle('position', 'absolute');
12024 this.maskEl.top.setStyle('z-index', zIndex);
12025 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12026 this.maskEl.top.setLeft(0);
12027 this.maskEl.top.setTop(0);
12028 this.maskEl.top.show();
12030 this.maskEl.left.setStyle('position', 'absolute');
12031 this.maskEl.left.setStyle('z-index', zIndex);
12032 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12033 this.maskEl.left.setLeft(0);
12034 this.maskEl.left.setTop(box.y - this.padding);
12035 this.maskEl.left.show();
12037 this.maskEl.bottom.setStyle('position', 'absolute');
12038 this.maskEl.bottom.setStyle('z-index', zIndex);
12039 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12040 this.maskEl.bottom.setLeft(0);
12041 this.maskEl.bottom.setTop(box.bottom + this.padding);
12042 this.maskEl.bottom.show();
12044 this.maskEl.right.setStyle('position', 'absolute');
12045 this.maskEl.right.setStyle('z-index', zIndex);
12046 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12047 this.maskEl.right.setLeft(box.right + this.padding);
12048 this.maskEl.right.setTop(box.y - this.padding);
12049 this.maskEl.right.show();
12051 this.toolTip.bindEl = this.target.el;
12053 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12055 var tip = this.target.blankText;
12057 if(this.target.getValue() !== '' ) {
12059 if (this.target.invalidText.length) {
12060 tip = this.target.invalidText;
12061 } else if (this.target.regexText.length){
12062 tip = this.target.regexText;
12066 this.toolTip.show(tip);
12068 this.intervalID = window.setInterval(function() {
12069 Roo.bootstrap.form.Form.popover.unmask();
12072 window.onwheel = function(){ return false;};
12074 (function(){ this.isMasked = true; }).defer(500, this);
12078 unmask : function()
12080 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12084 this.maskEl.top.setStyle('position', 'absolute');
12085 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12086 this.maskEl.top.hide();
12088 this.maskEl.left.setStyle('position', 'absolute');
12089 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12090 this.maskEl.left.hide();
12092 this.maskEl.bottom.setStyle('position', 'absolute');
12093 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12094 this.maskEl.bottom.hide();
12096 this.maskEl.right.setStyle('position', 'absolute');
12097 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12098 this.maskEl.right.hide();
12100 this.toolTip.hide();
12102 this.toolTip.el.hide();
12104 window.onwheel = function(){ return true;};
12106 if(this.intervalID){
12107 window.clearInterval(this.intervalID);
12108 this.intervalID = false;
12111 this.isMasked = false;
12121 * Ext JS Library 1.1.1
12122 * Copyright(c) 2006-2007, Ext JS, LLC.
12124 * Originally Released Under LGPL - original licence link has changed is not relivant.
12127 * <script type="text/javascript">
12130 * @class Roo.form.VTypes
12131 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12134 Roo.form.VTypes = function(){
12135 // closure these in so they are only created once.
12136 var alpha = /^[a-zA-Z_]+$/;
12137 var alphanum = /^[a-zA-Z0-9_]+$/;
12138 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12139 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12141 // All these messages and functions are configurable
12144 * The function used to validate email addresses
12145 * @param {String} value The email address
12147 'email' : function(v){
12148 return email.test(v);
12151 * The error text to display when the email validation function returns false
12154 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12156 * The keystroke filter mask to be applied on email input
12159 'emailMask' : /[a-z0-9_\.\-@]/i,
12162 * The function used to validate URLs
12163 * @param {String} value The URL
12165 'url' : function(v){
12166 return url.test(v);
12169 * The error text to display when the url validation function returns false
12172 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12175 * The function used to validate alpha values
12176 * @param {String} value The value
12178 'alpha' : function(v){
12179 return alpha.test(v);
12182 * The error text to display when the alpha validation function returns false
12185 'alphaText' : 'This field should only contain letters and _',
12187 * The keystroke filter mask to be applied on alpha input
12190 'alphaMask' : /[a-z_]/i,
12193 * The function used to validate alphanumeric values
12194 * @param {String} value The value
12196 'alphanum' : function(v){
12197 return alphanum.test(v);
12200 * The error text to display when the alphanumeric validation function returns false
12203 'alphanumText' : 'This field should only contain letters, numbers and _',
12205 * The keystroke filter mask to be applied on alphanumeric input
12208 'alphanumMask' : /[a-z0-9_]/i
12218 * @class Roo.bootstrap.form.Input
12219 * @extends Roo.bootstrap.Component
12220 * Bootstrap Input class
12221 * @cfg {Boolean} disabled is it disabled
12222 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12223 * @cfg {String} name name of the input
12224 * @cfg {string} fieldLabel - the label associated
12225 * @cfg {string} placeholder - placeholder to put in text.
12226 * @cfg {string} before - input group add on before
12227 * @cfg {string} after - input group add on after
12228 * @cfg {string} size - (lg|sm) or leave empty..
12229 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12230 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12231 * @cfg {Number} md colspan out of 12 for computer-sized screens
12232 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12233 * @cfg {string} value default value of the input
12234 * @cfg {Number} labelWidth set the width of label
12235 * @cfg {Number} labellg set the width of label (1-12)
12236 * @cfg {Number} labelmd set the width of label (1-12)
12237 * @cfg {Number} labelsm set the width of label (1-12)
12238 * @cfg {Number} labelxs set the width of label (1-12)
12239 * @cfg {String} labelAlign (top|left)
12240 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12241 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12242 * @cfg {String} indicatorpos (left|right) default left
12243 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12244 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12245 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12246 * @cfg {Roo.bootstrap.Button} before Button to show before
12247 * @cfg {Roo.bootstrap.Button} afterButton to show before
12248 * @cfg {String} align (left|center|right) Default left
12249 * @cfg {Boolean} forceFeedback (true|false) Default false
12252 * Create a new Input
12253 * @param {Object} config The config object
12256 Roo.bootstrap.form.Input = function(config){
12258 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12263 * Fires when this field receives input focus.
12264 * @param {Roo.form.Field} this
12269 * Fires when this field loses input focus.
12270 * @param {Roo.form.Field} this
12274 * @event specialkey
12275 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12276 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12277 * @param {Roo.form.Field} this
12278 * @param {Roo.EventObject} e The event object
12283 * Fires just before the field blurs if the field value has changed.
12284 * @param {Roo.form.Field} this
12285 * @param {Mixed} newValue The new value
12286 * @param {Mixed} oldValue The original value
12291 * Fires after the field has been marked as invalid.
12292 * @param {Roo.form.Field} this
12293 * @param {String} msg The validation message
12298 * Fires after the field has been validated with no errors.
12299 * @param {Roo.form.Field} this
12304 * Fires after the key up
12305 * @param {Roo.form.Field} this
12306 * @param {Roo.EventObject} e The event Object
12311 * Fires after the user pastes into input
12312 * @param {Roo.form.Field} this
12313 * @param {Roo.EventObject} e The event Object
12319 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12321 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12322 automatic validation (defaults to "keyup").
12324 validationEvent : "keyup",
12326 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12328 validateOnBlur : true,
12330 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12332 validationDelay : 250,
12334 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12336 focusClass : "x-form-focus", // not needed???
12340 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12342 invalidClass : "has-warning",
12345 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12347 validClass : "has-success",
12350 * @cfg {Boolean} hasFeedback (true|false) default true
12352 hasFeedback : true,
12355 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12357 invalidFeedbackClass : "glyphicon-warning-sign",
12360 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12362 validFeedbackClass : "glyphicon-ok",
12365 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12367 selectOnFocus : false,
12370 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12374 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12379 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12381 disableKeyFilter : false,
12384 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12388 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12392 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12394 blankText : "Please complete this mandatory field",
12397 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12401 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12403 maxLength : Number.MAX_VALUE,
12405 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12407 minLengthText : "The minimum length for this field is {0}",
12409 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12411 maxLengthText : "The maximum length for this field is {0}",
12415 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12416 * If available, this function will be called only after the basic validators all return true, and will be passed the
12417 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12421 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12422 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12423 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12427 * @cfg {String} regexText -- Depricated - use Invalid Text
12432 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12438 autocomplete: false,
12442 inputType : 'text',
12445 placeholder: false,
12450 preventMark: false,
12451 isFormField : true,
12454 labelAlign : false,
12457 formatedValue : false,
12458 forceFeedback : false,
12460 indicatorpos : 'left',
12470 parentLabelAlign : function()
12473 while (parent.parent()) {
12474 parent = parent.parent();
12475 if (typeof(parent.labelAlign) !='undefined') {
12476 return parent.labelAlign;
12483 getAutoCreate : function()
12485 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12491 if(this.inputType != 'hidden'){
12492 cfg.cls = 'form-group' //input-group
12498 type : this.inputType,
12499 value : this.value,
12500 cls : 'form-control',
12501 placeholder : this.placeholder || '',
12502 autocomplete : this.autocomplete || 'new-password'
12504 if (this.inputType == 'file') {
12505 input.style = 'overflow:hidden'; // why not in CSS?
12508 if(this.capture.length){
12509 input.capture = this.capture;
12512 if(this.accept.length){
12513 input.accept = this.accept + "/*";
12517 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12520 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12521 input.maxLength = this.maxLength;
12524 if (this.disabled) {
12525 input.disabled=true;
12528 if (this.readOnly) {
12529 input.readonly=true;
12533 input.name = this.name;
12537 input.cls += ' input-' + this.size;
12541 ['xs','sm','md','lg'].map(function(size){
12542 if (settings[size]) {
12543 cfg.cls += ' col-' + size + '-' + settings[size];
12547 var inputblock = input;
12551 cls: 'glyphicon form-control-feedback'
12554 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12557 cls : 'has-feedback',
12565 if (this.before || this.after) {
12568 cls : 'input-group',
12572 if (this.before && typeof(this.before) == 'string') {
12574 inputblock.cn.push({
12576 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12580 if (this.before && typeof(this.before) == 'object') {
12581 this.before = Roo.factory(this.before);
12583 inputblock.cn.push({
12585 cls : 'roo-input-before input-group-prepend input-group-' +
12586 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12590 inputblock.cn.push(input);
12592 if (this.after && typeof(this.after) == 'string') {
12593 inputblock.cn.push({
12595 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12599 if (this.after && typeof(this.after) == 'object') {
12600 this.after = Roo.factory(this.after);
12602 inputblock.cn.push({
12604 cls : 'roo-input-after input-group-append input-group-' +
12605 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12609 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12610 inputblock.cls += ' has-feedback';
12611 inputblock.cn.push(feedback);
12616 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12617 tooltip : 'This field is required'
12619 if (this.allowBlank ) {
12620 indicator.style = this.allowBlank ? ' display:none' : '';
12622 if (align ==='left' && this.fieldLabel.length) {
12624 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12631 cls : 'control-label col-form-label',
12632 html : this.fieldLabel
12643 var labelCfg = cfg.cn[1];
12644 var contentCfg = cfg.cn[2];
12646 if(this.indicatorpos == 'right'){
12651 cls : 'control-label col-form-label',
12655 html : this.fieldLabel
12669 labelCfg = cfg.cn[0];
12670 contentCfg = cfg.cn[1];
12674 if(this.labelWidth > 12){
12675 labelCfg.style = "width: " + this.labelWidth + 'px';
12678 if(this.labelWidth < 13 && this.labelmd == 0){
12679 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12682 if(this.labellg > 0){
12683 labelCfg.cls += ' col-lg-' + this.labellg;
12684 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12687 if(this.labelmd > 0){
12688 labelCfg.cls += ' col-md-' + this.labelmd;
12689 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12692 if(this.labelsm > 0){
12693 labelCfg.cls += ' col-sm-' + this.labelsm;
12694 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12697 if(this.labelxs > 0){
12698 labelCfg.cls += ' col-xs-' + this.labelxs;
12699 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12703 } else if ( this.fieldLabel.length) {
12710 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12711 tooltip : 'This field is required',
12712 style : this.allowBlank ? ' display:none' : ''
12716 //cls : 'input-group-addon',
12717 html : this.fieldLabel
12725 if(this.indicatorpos == 'right'){
12730 //cls : 'input-group-addon',
12731 html : this.fieldLabel
12736 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12737 tooltip : 'This field is required',
12738 style : this.allowBlank ? ' display:none' : ''
12758 if (this.parentType === 'Navbar' && this.parent().bar) {
12759 cfg.cls += ' navbar-form';
12762 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12763 // on BS4 we do this only if not form
12764 cfg.cls += ' navbar-form';
12772 * return the real input element.
12774 inputEl: function ()
12776 return this.el.select('input.form-control',true).first();
12779 tooltipEl : function()
12781 return this.inputEl();
12784 indicatorEl : function()
12786 if (Roo.bootstrap.version == 4) {
12787 return false; // not enabled in v4 yet.
12790 var indicator = this.el.select('i.roo-required-indicator',true).first();
12800 setDisabled : function(v)
12802 var i = this.inputEl().dom;
12804 i.removeAttribute('disabled');
12808 i.setAttribute('disabled','true');
12810 initEvents : function()
12813 this.inputEl().on("keydown" , this.fireKey, this);
12814 this.inputEl().on("focus", this.onFocus, this);
12815 this.inputEl().on("blur", this.onBlur, this);
12817 this.inputEl().relayEvent('keyup', this);
12818 this.inputEl().relayEvent('paste', this);
12820 this.indicator = this.indicatorEl();
12822 if(this.indicator){
12823 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12826 // reference to original value for reset
12827 this.originalValue = this.getValue();
12828 //Roo.form.TextField.superclass.initEvents.call(this);
12829 if(this.validationEvent == 'keyup'){
12830 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12831 this.inputEl().on('keyup', this.filterValidation, this);
12833 else if(this.validationEvent !== false){
12834 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12837 if(this.selectOnFocus){
12838 this.on("focus", this.preFocus, this);
12841 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12842 this.inputEl().on("keypress", this.filterKeys, this);
12844 this.inputEl().relayEvent('keypress', this);
12847 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12848 this.el.on("click", this.autoSize, this);
12851 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12852 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12855 if (typeof(this.before) == 'object') {
12856 this.before.render(this.el.select('.roo-input-before',true).first());
12858 if (typeof(this.after) == 'object') {
12859 this.after.render(this.el.select('.roo-input-after',true).first());
12862 this.inputEl().on('change', this.onChange, this);
12865 filterValidation : function(e){
12866 if(!e.isNavKeyPress()){
12867 this.validationTask.delay(this.validationDelay);
12871 * Validates the field value
12872 * @return {Boolean} True if the value is valid, else false
12874 validate : function(){
12875 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12876 if(this.disabled || this.validateValue(this.getRawValue())){
12881 this.markInvalid();
12887 * Validates a value according to the field's validation rules and marks the field as invalid
12888 * if the validation fails
12889 * @param {Mixed} value The value to validate
12890 * @return {Boolean} True if the value is valid, else false
12892 validateValue : function(value)
12894 if(this.getVisibilityEl().hasClass('hidden')){
12898 if(value.length < 1) { // if it's blank
12899 if(this.allowBlank){
12905 if(value.length < this.minLength){
12908 if(value.length > this.maxLength){
12912 var vt = Roo.form.VTypes;
12913 if(!vt[this.vtype](value, this)){
12917 if(typeof this.validator == "function"){
12918 var msg = this.validator(value);
12922 if (typeof(msg) == 'string') {
12923 this.invalidText = msg;
12927 if(this.regex && !this.regex.test(value)){
12935 fireKey : function(e){
12936 //Roo.log('field ' + e.getKey());
12937 if(e.isNavKeyPress()){
12938 this.fireEvent("specialkey", this, e);
12941 focus : function (selectText){
12943 this.inputEl().focus();
12944 if(selectText === true){
12945 this.inputEl().dom.select();
12951 onFocus : function(){
12952 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12953 // this.el.addClass(this.focusClass);
12955 if(!this.hasFocus){
12956 this.hasFocus = true;
12957 this.startValue = this.getValue();
12958 this.fireEvent("focus", this);
12962 beforeBlur : Roo.emptyFn,
12966 onBlur : function(){
12968 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12969 //this.el.removeClass(this.focusClass);
12971 this.hasFocus = false;
12972 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12975 var v = this.getValue();
12976 if(String(v) !== String(this.startValue)){
12977 this.fireEvent('change', this, v, this.startValue);
12979 this.fireEvent("blur", this);
12982 onChange : function(e)
12984 var v = this.getValue();
12985 if(String(v) !== String(this.startValue)){
12986 this.fireEvent('change', this, v, this.startValue);
12992 * Resets the current field value to the originally loaded value and clears any validation messages
12994 reset : function(){
12995 this.setValue(this.originalValue);
12999 * Returns the name of the field
13000 * @return {Mixed} name The name field
13002 getName: function(){
13006 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13007 * @return {Mixed} value The field value
13009 getValue : function(){
13011 var v = this.inputEl().getValue();
13016 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13017 * @return {Mixed} value The field value
13019 getRawValue : function(){
13020 var v = this.inputEl().getValue();
13026 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13027 * @param {Mixed} value The value to set
13029 setRawValue : function(v){
13030 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13033 selectText : function(start, end){
13034 var v = this.getRawValue();
13036 start = start === undefined ? 0 : start;
13037 end = end === undefined ? v.length : end;
13038 var d = this.inputEl().dom;
13039 if(d.setSelectionRange){
13040 d.setSelectionRange(start, end);
13041 }else if(d.createTextRange){
13042 var range = d.createTextRange();
13043 range.moveStart("character", start);
13044 range.moveEnd("character", v.length-end);
13051 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13052 * @param {Mixed} value The value to set
13054 setValue : function(v){
13057 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13063 processValue : function(value){
13064 if(this.stripCharsRe){
13065 var newValue = value.replace(this.stripCharsRe, '');
13066 if(newValue !== value){
13067 this.setRawValue(newValue);
13074 preFocus : function(){
13076 if(this.selectOnFocus){
13077 this.inputEl().dom.select();
13080 filterKeys : function(e){
13081 var k = e.getKey();
13082 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13085 var c = e.getCharCode(), cc = String.fromCharCode(c);
13086 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13089 if(!this.maskRe.test(cc)){
13094 * Clear any invalid styles/messages for this field
13096 clearInvalid : function(){
13098 if(!this.el || this.preventMark){ // not rendered
13103 this.el.removeClass([this.invalidClass, 'is-invalid']);
13105 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13107 var feedback = this.el.select('.form-control-feedback', true).first();
13110 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13115 if(this.indicator){
13116 this.indicator.removeClass('visible');
13117 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13120 this.fireEvent('valid', this);
13124 * Mark this field as valid
13126 markValid : function()
13128 if(!this.el || this.preventMark){ // not rendered...
13132 this.el.removeClass([this.invalidClass, this.validClass]);
13133 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13135 var feedback = this.el.select('.form-control-feedback', true).first();
13138 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13141 if(this.indicator){
13142 this.indicator.removeClass('visible');
13143 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13151 if(this.allowBlank && !this.getRawValue().length){
13154 if (Roo.bootstrap.version == 3) {
13155 this.el.addClass(this.validClass);
13157 this.inputEl().addClass('is-valid');
13160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13162 var feedback = this.el.select('.form-control-feedback', true).first();
13165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13166 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13171 this.fireEvent('valid', this);
13175 * Mark this field as invalid
13176 * @param {String} msg The validation message
13178 markInvalid : function(msg)
13180 if(!this.el || this.preventMark){ // not rendered
13184 this.el.removeClass([this.invalidClass, this.validClass]);
13185 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13187 var feedback = this.el.select('.form-control-feedback', true).first();
13190 this.el.select('.form-control-feedback', true).first().removeClass(
13191 [this.invalidFeedbackClass, this.validFeedbackClass]);
13198 if(this.allowBlank && !this.getRawValue().length){
13202 if(this.indicator){
13203 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13204 this.indicator.addClass('visible');
13206 if (Roo.bootstrap.version == 3) {
13207 this.el.addClass(this.invalidClass);
13209 this.inputEl().addClass('is-invalid');
13214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13216 var feedback = this.el.select('.form-control-feedback', true).first();
13219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13221 if(this.getValue().length || this.forceFeedback){
13222 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13229 this.fireEvent('invalid', this, msg);
13232 SafariOnKeyDown : function(event)
13234 // this is a workaround for a password hang bug on chrome/ webkit.
13235 if (this.inputEl().dom.type != 'password') {
13239 var isSelectAll = false;
13241 if(this.inputEl().dom.selectionEnd > 0){
13242 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13244 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13245 event.preventDefault();
13250 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13252 event.preventDefault();
13253 // this is very hacky as keydown always get's upper case.
13255 var cc = String.fromCharCode(event.getCharCode());
13256 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13260 adjustWidth : function(tag, w){
13261 tag = tag.toLowerCase();
13262 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13263 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13264 if(tag == 'input'){
13267 if(tag == 'textarea'){
13270 }else if(Roo.isOpera){
13271 if(tag == 'input'){
13274 if(tag == 'textarea'){
13282 setFieldLabel : function(v)
13284 if(!this.rendered){
13288 if(this.indicatorEl()){
13289 var ar = this.el.select('label > span',true);
13291 if (ar.elements.length) {
13292 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13293 this.fieldLabel = v;
13297 var br = this.el.select('label',true);
13299 if(br.elements.length) {
13300 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13301 this.fieldLabel = v;
13305 Roo.log('Cannot Found any of label > span || label in input');
13309 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13310 this.fieldLabel = v;
13325 * @class Roo.bootstrap.form.TextArea
13326 * @extends Roo.bootstrap.form.Input
13327 * Bootstrap TextArea class
13328 * @cfg {Number} cols Specifies the visible width of a text area
13329 * @cfg {Number} rows Specifies the visible number of lines in a text area
13330 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13331 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13332 * @cfg {string} html text
13335 * Create a new TextArea
13336 * @param {Object} config The config object
13339 Roo.bootstrap.form.TextArea = function(config){
13340 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13344 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13354 getAutoCreate : function(){
13356 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13362 if(this.inputType != 'hidden'){
13363 cfg.cls = 'form-group' //input-group
13371 value : this.value || '',
13372 html: this.html || '',
13373 cls : 'form-control',
13374 placeholder : this.placeholder || ''
13378 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13379 input.maxLength = this.maxLength;
13383 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13387 input.cols = this.cols;
13390 if (this.readOnly) {
13391 input.readonly = true;
13395 input.name = this.name;
13399 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13403 ['xs','sm','md','lg'].map(function(size){
13404 if (settings[size]) {
13405 cfg.cls += ' col-' + size + '-' + settings[size];
13409 var inputblock = input;
13411 if(this.hasFeedback && !this.allowBlank){
13415 cls: 'glyphicon form-control-feedback'
13419 cls : 'has-feedback',
13428 if (this.before || this.after) {
13431 cls : 'input-group',
13435 inputblock.cn.push({
13437 cls : 'input-group-addon',
13442 inputblock.cn.push(input);
13444 if(this.hasFeedback && !this.allowBlank){
13445 inputblock.cls += ' has-feedback';
13446 inputblock.cn.push(feedback);
13450 inputblock.cn.push({
13452 cls : 'input-group-addon',
13459 if (align ==='left' && this.fieldLabel.length) {
13464 cls : 'control-label',
13465 html : this.fieldLabel
13476 if(this.labelWidth > 12){
13477 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13480 if(this.labelWidth < 13 && this.labelmd == 0){
13481 this.labelmd = this.labelWidth;
13484 if(this.labellg > 0){
13485 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13486 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13489 if(this.labelmd > 0){
13490 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13491 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13494 if(this.labelsm > 0){
13495 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13496 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13499 if(this.labelxs > 0){
13500 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13501 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13504 } else if ( this.fieldLabel.length) {
13509 //cls : 'input-group-addon',
13510 html : this.fieldLabel
13528 if (this.disabled) {
13529 input.disabled=true;
13536 * return the real textarea element.
13538 inputEl: function ()
13540 return this.el.select('textarea.form-control',true).first();
13544 * Clear any invalid styles/messages for this field
13546 clearInvalid : function()
13549 if(!this.el || this.preventMark){ // not rendered
13553 var label = this.el.select('label', true).first();
13554 var icon = this.el.select('i.fa-star', true).first();
13559 this.el.removeClass( this.validClass);
13560 this.inputEl().removeClass('is-invalid');
13562 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13564 var feedback = this.el.select('.form-control-feedback', true).first();
13567 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13572 this.fireEvent('valid', this);
13576 * Mark this field as valid
13578 markValid : function()
13580 if(!this.el || this.preventMark){ // not rendered
13584 this.el.removeClass([this.invalidClass, this.validClass]);
13585 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13587 var feedback = this.el.select('.form-control-feedback', true).first();
13590 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13593 if(this.disabled || this.allowBlank){
13597 var label = this.el.select('label', true).first();
13598 var icon = this.el.select('i.fa-star', true).first();
13603 if (Roo.bootstrap.version == 3) {
13604 this.el.addClass(this.validClass);
13606 this.inputEl().addClass('is-valid');
13610 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13612 var feedback = this.el.select('.form-control-feedback', true).first();
13615 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13616 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13621 this.fireEvent('valid', this);
13625 * Mark this field as invalid
13626 * @param {String} msg The validation message
13628 markInvalid : function(msg)
13630 if(!this.el || this.preventMark){ // not rendered
13634 this.el.removeClass([this.invalidClass, this.validClass]);
13635 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13637 var feedback = this.el.select('.form-control-feedback', true).first();
13640 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13643 if(this.disabled || this.allowBlank){
13647 var label = this.el.select('label', true).first();
13648 var icon = this.el.select('i.fa-star', true).first();
13650 if(!this.getValue().length && label && !icon){
13651 this.el.createChild({
13653 cls : 'text-danger fa fa-lg fa-star',
13654 tooltip : 'This field is required',
13655 style : 'margin-right:5px;'
13659 if (Roo.bootstrap.version == 3) {
13660 this.el.addClass(this.invalidClass);
13662 this.inputEl().addClass('is-invalid');
13665 // fixme ... this may be depricated need to test..
13666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13668 var feedback = this.el.select('.form-control-feedback', true).first();
13671 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13673 if(this.getValue().length || this.forceFeedback){
13674 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13681 this.fireEvent('invalid', this, msg);
13689 * trigger field - base class for combo..
13694 * @class Roo.bootstrap.form.TriggerField
13695 * @extends Roo.bootstrap.form.Input
13696 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13697 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13698 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13699 * for which you can provide a custom implementation. For example:
13701 var trigger = new Roo.bootstrap.form.TriggerField();
13702 trigger.onTriggerClick = myTriggerFn;
13703 trigger.applyTo('my-field');
13706 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13707 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13708 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13709 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13710 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13713 * Create a new TriggerField.
13714 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13715 * to the base TextField)
13717 Roo.bootstrap.form.TriggerField = function(config){
13718 this.mimicing = false;
13719 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13722 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13724 * @cfg {String} triggerClass A CSS class to apply to the trigger
13727 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13732 * @cfg {Boolean} removable (true|false) special filter default false
13736 /** @cfg {Boolean} grow @hide */
13737 /** @cfg {Number} growMin @hide */
13738 /** @cfg {Number} growMax @hide */
13744 autoSize: Roo.emptyFn,
13748 deferHeight : true,
13751 actionMode : 'wrap',
13756 getAutoCreate : function(){
13758 var align = this.labelAlign || this.parentLabelAlign();
13763 cls: 'form-group' //input-group
13770 type : this.inputType,
13771 cls : 'form-control',
13772 autocomplete: 'new-password',
13773 placeholder : this.placeholder || ''
13777 input.name = this.name;
13780 input.cls += ' input-' + this.size;
13783 if (this.disabled) {
13784 input.disabled=true;
13787 var inputblock = input;
13789 if(this.hasFeedback && !this.allowBlank){
13793 cls: 'glyphicon form-control-feedback'
13796 if(this.removable && !this.editable ){
13798 cls : 'has-feedback',
13804 cls : 'roo-combo-removable-btn close'
13811 cls : 'has-feedback',
13820 if(this.removable && !this.editable ){
13822 cls : 'roo-removable',
13828 cls : 'roo-combo-removable-btn close'
13835 if (this.before || this.after) {
13838 cls : 'input-group',
13842 inputblock.cn.push({
13844 cls : 'input-group-addon input-group-prepend input-group-text',
13849 inputblock.cn.push(input);
13851 if(this.hasFeedback && !this.allowBlank){
13852 inputblock.cls += ' has-feedback';
13853 inputblock.cn.push(feedback);
13857 inputblock.cn.push({
13859 cls : 'input-group-addon input-group-append input-group-text',
13868 var ibwrap = inputblock;
13873 cls: 'roo-select2-choices',
13877 cls: 'roo-select2-search-field',
13889 cls: 'roo-select2-container input-group',
13894 cls: 'form-hidden-field'
13900 if(!this.multiple && this.showToggleBtn){
13906 if (this.caret != false) {
13909 cls: 'fa fa-' + this.caret
13916 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13918 Roo.bootstrap.version == 3 ? caret : '',
13921 cls: 'combobox-clear',
13935 combobox.cls += ' roo-select2-container-multi';
13939 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13940 tooltip : 'This field is required'
13942 if (Roo.bootstrap.version == 4) {
13945 style : 'display:none'
13950 if (align ==='left' && this.fieldLabel.length) {
13952 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13959 cls : 'control-label',
13960 html : this.fieldLabel
13972 var labelCfg = cfg.cn[1];
13973 var contentCfg = cfg.cn[2];
13975 if(this.indicatorpos == 'right'){
13980 cls : 'control-label',
13984 html : this.fieldLabel
13998 labelCfg = cfg.cn[0];
13999 contentCfg = cfg.cn[1];
14002 if(this.labelWidth > 12){
14003 labelCfg.style = "width: " + this.labelWidth + 'px';
14006 if(this.labelWidth < 13 && this.labelmd == 0){
14007 this.labelmd = this.labelWidth;
14010 if(this.labellg > 0){
14011 labelCfg.cls += ' col-lg-' + this.labellg;
14012 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14015 if(this.labelmd > 0){
14016 labelCfg.cls += ' col-md-' + this.labelmd;
14017 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14020 if(this.labelsm > 0){
14021 labelCfg.cls += ' col-sm-' + this.labelsm;
14022 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14025 if(this.labelxs > 0){
14026 labelCfg.cls += ' col-xs-' + this.labelxs;
14027 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14030 } else if ( this.fieldLabel.length) {
14031 // Roo.log(" label");
14036 //cls : 'input-group-addon',
14037 html : this.fieldLabel
14045 if(this.indicatorpos == 'right'){
14053 html : this.fieldLabel
14067 // Roo.log(" no label && no align");
14074 ['xs','sm','md','lg'].map(function(size){
14075 if (settings[size]) {
14076 cfg.cls += ' col-' + size + '-' + settings[size];
14087 onResize : function(w, h){
14088 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14089 // if(typeof w == 'number'){
14090 // var x = w - this.trigger.getWidth();
14091 // this.inputEl().setWidth(this.adjustWidth('input', x));
14092 // this.trigger.setStyle('left', x+'px');
14097 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14100 getResizeEl : function(){
14101 return this.inputEl();
14105 getPositionEl : function(){
14106 return this.inputEl();
14110 alignErrorIcon : function(){
14111 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14115 initEvents : function(){
14119 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14120 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14121 if(!this.multiple && this.showToggleBtn){
14122 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14123 if(this.hideTrigger){
14124 this.trigger.setDisplayed(false);
14126 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14130 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14133 if(this.removable && !this.editable && !this.tickable){
14134 var close = this.closeTriggerEl();
14137 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14138 close.on('click', this.removeBtnClick, this, close);
14142 //this.trigger.addClassOnOver('x-form-trigger-over');
14143 //this.trigger.addClassOnClick('x-form-trigger-click');
14146 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14150 closeTriggerEl : function()
14152 var close = this.el.select('.roo-combo-removable-btn', true).first();
14153 return close ? close : false;
14156 removeBtnClick : function(e, h, el)
14158 e.preventDefault();
14160 if(this.fireEvent("remove", this) !== false){
14162 this.fireEvent("afterremove", this)
14166 createList : function()
14168 this.list = Roo.get(document.body).createChild({
14169 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14170 cls: 'typeahead typeahead-long dropdown-menu shadow',
14171 style: 'display:none'
14174 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14179 initTrigger : function(){
14184 onDestroy : function(){
14186 this.trigger.removeAllListeners();
14187 // this.trigger.remove();
14190 // this.wrap.remove();
14192 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14196 onFocus : function(){
14197 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14199 if(!this.mimicing){
14200 this.wrap.addClass('x-trigger-wrap-focus');
14201 this.mimicing = true;
14202 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14203 if(this.monitorTab){
14204 this.el.on("keydown", this.checkTab, this);
14211 checkTab : function(e){
14212 if(e.getKey() == e.TAB){
14213 this.triggerBlur();
14218 onBlur : function(){
14223 mimicBlur : function(e, t){
14225 if(!this.wrap.contains(t) && this.validateBlur()){
14226 this.triggerBlur();
14232 triggerBlur : function(){
14233 this.mimicing = false;
14234 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14235 if(this.monitorTab){
14236 this.el.un("keydown", this.checkTab, this);
14238 //this.wrap.removeClass('x-trigger-wrap-focus');
14239 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14243 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14244 validateBlur : function(e, t){
14249 onDisable : function(){
14250 this.inputEl().dom.disabled = true;
14251 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14253 // this.wrap.addClass('x-item-disabled');
14258 onEnable : function(){
14259 this.inputEl().dom.disabled = false;
14260 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14262 // this.el.removeClass('x-item-disabled');
14267 onShow : function(){
14268 var ae = this.getActionEl();
14271 ae.dom.style.display = '';
14272 ae.dom.style.visibility = 'visible';
14278 onHide : function(){
14279 var ae = this.getActionEl();
14280 ae.dom.style.display = 'none';
14284 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14285 * by an implementing function.
14287 * @param {EventObject} e
14289 onTriggerClick : Roo.emptyFn
14297 * @class Roo.bootstrap.form.CardUploader
14298 * @extends Roo.bootstrap.Button
14299 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14300 * @cfg {Number} errorTimeout default 3000
14301 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14302 * @cfg {Array} html The button text.
14306 * Create a new CardUploader
14307 * @param {Object} config The config object
14310 Roo.bootstrap.form.CardUploader = function(config){
14314 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14317 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14325 * When a image is clicked on - and needs to display a slideshow or similar..
14326 * @param {Roo.bootstrap.Card} this
14327 * @param {Object} The image information data
14333 * When a the download link is clicked
14334 * @param {Roo.bootstrap.Card} this
14335 * @param {Object} The image information data contains
14342 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14345 errorTimeout : 3000,
14349 fileCollection : false,
14352 getAutoCreate : function()
14356 cls :'form-group' ,
14361 //cls : 'input-group-addon',
14362 html : this.fieldLabel
14370 value : this.value,
14371 cls : 'd-none form-control'
14376 multiple : 'multiple',
14378 cls : 'd-none roo-card-upload-selector'
14382 cls : 'roo-card-uploader-button-container w-100 mb-2'
14385 cls : 'card-columns roo-card-uploader-container'
14395 getChildContainer : function() /// what children are added to.
14397 return this.containerEl;
14400 getButtonContainer : function() /// what children are added to.
14402 return this.el.select(".roo-card-uploader-button-container").first();
14405 initEvents : function()
14408 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14412 xns: Roo.bootstrap,
14415 container_method : 'getButtonContainer' ,
14416 html : this.html, // fix changable?
14419 'click' : function(btn, e) {
14428 this.urlAPI = (window.createObjectURL && window) ||
14429 (window.URL && URL.revokeObjectURL && URL) ||
14430 (window.webkitURL && webkitURL);
14435 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14437 this.selectorEl.on('change', this.onFileSelected, this);
14440 this.images.forEach(function(img) {
14443 this.images = false;
14445 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14451 onClick : function(e)
14453 e.preventDefault();
14455 this.selectorEl.dom.click();
14459 onFileSelected : function(e)
14461 e.preventDefault();
14463 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14467 Roo.each(this.selectorEl.dom.files, function(file){
14468 this.addFile(file);
14477 addFile : function(file)
14480 if(typeof(file) === 'string'){
14481 throw "Add file by name?"; // should not happen
14485 if(!file || !this.urlAPI){
14495 var url = _this.urlAPI.createObjectURL( file);
14498 id : Roo.bootstrap.form.CardUploader.ID--,
14499 is_uploaded : false,
14503 mimetype : file.type,
14511 * addCard - add an Attachment to the uploader
14512 * @param data - the data about the image to upload
14516 title : "Title of file",
14517 is_uploaded : false,
14518 src : "http://.....",
14519 srcfile : { the File upload object },
14520 mimetype : file.type,
14523 .. any other data...
14529 addCard : function (data)
14531 // hidden input element?
14532 // if the file is not an image...
14533 //then we need to use something other that and header_image
14538 xns : Roo.bootstrap,
14539 xtype : 'CardFooter',
14542 xns : Roo.bootstrap,
14548 xns : Roo.bootstrap,
14550 html : String.format("<small>{0}</small>", data.title),
14551 cls : 'col-10 text-left',
14556 click : function() {
14558 t.fireEvent( "download", t, data );
14564 xns : Roo.bootstrap,
14566 style: 'max-height: 28px; ',
14572 click : function() {
14573 t.removeCard(data.id)
14585 var cn = this.addxtype(
14588 xns : Roo.bootstrap,
14591 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14592 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14593 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14598 initEvents : function() {
14599 Roo.bootstrap.Card.prototype.initEvents.call(this);
14601 this.imgEl = this.el.select('.card-img-top').first();
14603 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14604 this.imgEl.set({ 'pointer' : 'cursor' });
14607 this.getCardFooter().addClass('p-1');
14614 // dont' really need ot update items.
14615 // this.items.push(cn);
14616 this.fileCollection.add(cn);
14618 if (!data.srcfile) {
14619 this.updateInput();
14624 var reader = new FileReader();
14625 reader.addEventListener("load", function() {
14626 data.srcdata = reader.result;
14629 reader.readAsDataURL(data.srcfile);
14634 removeCard : function(id)
14637 var card = this.fileCollection.get(id);
14638 card.data.is_deleted = 1;
14639 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14640 //this.fileCollection.remove(card);
14641 //this.items = this.items.filter(function(e) { return e != card });
14642 // dont' really need ot update items.
14643 card.el.dom.parentNode.removeChild(card.el.dom);
14644 this.updateInput();
14650 this.fileCollection.each(function(card) {
14651 if (card.el.dom && card.el.dom.parentNode) {
14652 card.el.dom.parentNode.removeChild(card.el.dom);
14655 this.fileCollection.clear();
14656 this.updateInput();
14659 updateInput : function()
14662 this.fileCollection.each(function(e) {
14666 this.inputEl().dom.value = JSON.stringify(data);
14676 Roo.bootstrap.form.CardUploader.ID = -1;/*
14678 * Ext JS Library 1.1.1
14679 * Copyright(c) 2006-2007, Ext JS, LLC.
14681 * Originally Released Under LGPL - original licence link has changed is not relivant.
14684 * <script type="text/javascript">
14689 * @class Roo.data.SortTypes
14691 * Defines the default sorting (casting?) comparison functions used when sorting data.
14693 Roo.data.SortTypes = {
14695 * Default sort that does nothing
14696 * @param {Mixed} s The value being converted
14697 * @return {Mixed} The comparison value
14699 none : function(s){
14704 * The regular expression used to strip tags
14708 stripTagsRE : /<\/?[^>]+>/gi,
14711 * Strips all HTML tags to sort on text only
14712 * @param {Mixed} s The value being converted
14713 * @return {String} The comparison value
14715 asText : function(s){
14716 return String(s).replace(this.stripTagsRE, "");
14720 * Strips all HTML tags to sort on text only - Case insensitive
14721 * @param {Mixed} s The value being converted
14722 * @return {String} The comparison value
14724 asUCText : function(s){
14725 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14729 * Case insensitive string
14730 * @param {Mixed} s The value being converted
14731 * @return {String} The comparison value
14733 asUCString : function(s) {
14734 return String(s).toUpperCase();
14739 * @param {Mixed} s The value being converted
14740 * @return {Number} The comparison value
14742 asDate : function(s) {
14746 if(s instanceof Date){
14747 return s.getTime();
14749 return Date.parse(String(s));
14754 * @param {Mixed} s The value being converted
14755 * @return {Float} The comparison value
14757 asFloat : function(s) {
14758 var val = parseFloat(String(s).replace(/,/g, ""));
14767 * @param {Mixed} s The value being converted
14768 * @return {Number} The comparison value
14770 asInt : function(s) {
14771 var val = parseInt(String(s).replace(/,/g, ""));
14779 * Ext JS Library 1.1.1
14780 * Copyright(c) 2006-2007, Ext JS, LLC.
14782 * Originally Released Under LGPL - original licence link has changed is not relivant.
14785 * <script type="text/javascript">
14789 * @class Roo.data.Record
14790 * Instances of this class encapsulate both record <em>definition</em> information, and record
14791 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14792 * to access Records cached in an {@link Roo.data.Store} object.<br>
14794 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14795 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14798 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14800 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14801 * {@link #create}. The parameters are the same.
14802 * @param {Array} data An associative Array of data values keyed by the field name.
14803 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14804 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14805 * not specified an integer id is generated.
14807 Roo.data.Record = function(data, id){
14808 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14813 * Generate a constructor for a specific record layout.
14814 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14815 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14816 * Each field definition object may contain the following properties: <ul>
14817 * <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,
14818 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14819 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14820 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14821 * is being used, then this is a string containing the javascript expression to reference the data relative to
14822 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14823 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14824 * this may be omitted.</p></li>
14825 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14826 * <ul><li>auto (Default, implies no conversion)</li>
14831 * <li>date</li></ul></p></li>
14832 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14833 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14834 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14835 * by the Reader into an object that will be stored in the Record. It is passed the
14836 * following parameters:<ul>
14837 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14839 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14841 * <br>usage:<br><pre><code>
14842 var TopicRecord = Roo.data.Record.create(
14843 {name: 'title', mapping: 'topic_title'},
14844 {name: 'author', mapping: 'username'},
14845 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14846 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14847 {name: 'lastPoster', mapping: 'user2'},
14848 {name: 'excerpt', mapping: 'post_text'}
14851 var myNewRecord = new TopicRecord({
14852 title: 'Do my job please',
14855 lastPost: new Date(),
14856 lastPoster: 'Animal',
14857 excerpt: 'No way dude!'
14859 myStore.add(myNewRecord);
14864 Roo.data.Record.create = function(o){
14865 var f = function(){
14866 f.superclass.constructor.apply(this, arguments);
14868 Roo.extend(f, Roo.data.Record);
14869 var p = f.prototype;
14870 p.fields = new Roo.util.MixedCollection(false, function(field){
14873 for(var i = 0, len = o.length; i < len; i++){
14874 p.fields.add(new Roo.data.Field(o[i]));
14876 f.getField = function(name){
14877 return p.fields.get(name);
14882 Roo.data.Record.AUTO_ID = 1000;
14883 Roo.data.Record.EDIT = 'edit';
14884 Roo.data.Record.REJECT = 'reject';
14885 Roo.data.Record.COMMIT = 'commit';
14887 Roo.data.Record.prototype = {
14889 * Readonly flag - true if this record has been modified.
14898 join : function(store){
14899 this.store = store;
14903 * Set the named field to the specified value.
14904 * @param {String} name The name of the field to set.
14905 * @param {Object} value The value to set the field to.
14907 set : function(name, value){
14908 if(this.data[name] == value){
14912 if(!this.modified){
14913 this.modified = {};
14915 if(typeof this.modified[name] == 'undefined'){
14916 this.modified[name] = this.data[name];
14918 this.data[name] = value;
14919 if(!this.editing && this.store){
14920 this.store.afterEdit(this);
14925 * Get the value of the named field.
14926 * @param {String} name The name of the field to get the value of.
14927 * @return {Object} The value of the field.
14929 get : function(name){
14930 return this.data[name];
14934 beginEdit : function(){
14935 this.editing = true;
14936 this.modified = {};
14940 cancelEdit : function(){
14941 this.editing = false;
14942 delete this.modified;
14946 endEdit : function(){
14947 this.editing = false;
14948 if(this.dirty && this.store){
14949 this.store.afterEdit(this);
14954 * Usually called by the {@link Roo.data.Store} which owns the Record.
14955 * Rejects all changes made to the Record since either creation, or the last commit operation.
14956 * Modified fields are reverted to their original values.
14958 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14959 * of reject operations.
14961 reject : function(){
14962 var m = this.modified;
14964 if(typeof m[n] != "function"){
14965 this.data[n] = m[n];
14968 this.dirty = false;
14969 delete this.modified;
14970 this.editing = false;
14972 this.store.afterReject(this);
14977 * Usually called by the {@link Roo.data.Store} which owns the Record.
14978 * Commits all changes made to the Record since either creation, or the last commit operation.
14980 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14981 * of commit operations.
14983 commit : function(){
14984 this.dirty = false;
14985 delete this.modified;
14986 this.editing = false;
14988 this.store.afterCommit(this);
14993 hasError : function(){
14994 return this.error != null;
14998 clearError : function(){
15003 * Creates a copy of this record.
15004 * @param {String} id (optional) A new record id if you don't want to use this record's id
15007 copy : function(newId) {
15008 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15012 * Ext JS Library 1.1.1
15013 * Copyright(c) 2006-2007, Ext JS, LLC.
15015 * Originally Released Under LGPL - original licence link has changed is not relivant.
15018 * <script type="text/javascript">
15024 * @class Roo.data.Store
15025 * @extends Roo.util.Observable
15026 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15027 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15029 * 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
15030 * has no knowledge of the format of the data returned by the Proxy.<br>
15032 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15033 * instances from the data object. These records are cached and made available through accessor functions.
15035 * Creates a new Store.
15036 * @param {Object} config A config object containing the objects needed for the Store to access data,
15037 * and read the data into Records.
15039 Roo.data.Store = function(config){
15040 this.data = new Roo.util.MixedCollection(false);
15041 this.data.getKey = function(o){
15044 this.baseParams = {};
15046 this.paramNames = {
15051 "multisort" : "_multisort"
15054 if(config && config.data){
15055 this.inlineData = config.data;
15056 delete config.data;
15059 Roo.apply(this, config);
15061 if(this.reader){ // reader passed
15062 this.reader = Roo.factory(this.reader, Roo.data);
15063 this.reader.xmodule = this.xmodule || false;
15064 if(!this.recordType){
15065 this.recordType = this.reader.recordType;
15067 if(this.reader.onMetaChange){
15068 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15072 if(this.recordType){
15073 this.fields = this.recordType.prototype.fields;
15075 this.modified = [];
15079 * @event datachanged
15080 * Fires when the data cache has changed, and a widget which is using this Store
15081 * as a Record cache should refresh its view.
15082 * @param {Store} this
15084 datachanged : true,
15086 * @event metachange
15087 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15088 * @param {Store} this
15089 * @param {Object} meta The JSON metadata
15094 * Fires when Records have been added to the Store
15095 * @param {Store} this
15096 * @param {Roo.data.Record[]} records The array of Records added
15097 * @param {Number} index The index at which the record(s) were added
15102 * Fires when a Record has been removed from the Store
15103 * @param {Store} this
15104 * @param {Roo.data.Record} record The Record that was removed
15105 * @param {Number} index The index at which the record was removed
15110 * Fires when a Record has been updated
15111 * @param {Store} this
15112 * @param {Roo.data.Record} record The Record that was updated
15113 * @param {String} operation The update operation being performed. Value may be one of:
15115 Roo.data.Record.EDIT
15116 Roo.data.Record.REJECT
15117 Roo.data.Record.COMMIT
15123 * Fires when the data cache has been cleared.
15124 * @param {Store} this
15128 * @event beforeload
15129 * Fires before a request is made for a new data object. If the beforeload handler returns false
15130 * the load action will be canceled.
15131 * @param {Store} this
15132 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15136 * @event beforeloadadd
15137 * Fires after a new set of Records has been loaded.
15138 * @param {Store} this
15139 * @param {Roo.data.Record[]} records The Records that were loaded
15140 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15142 beforeloadadd : true,
15145 * Fires after a new set of Records has been loaded, before they are added to the store.
15146 * @param {Store} this
15147 * @param {Roo.data.Record[]} records The Records that were loaded
15148 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15149 * @params {Object} return from reader
15153 * @event loadexception
15154 * Fires if an exception occurs in the Proxy during loading.
15155 * Called with the signature of the Proxy's "loadexception" event.
15156 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15159 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15160 * @param {Object} load options
15161 * @param {Object} jsonData from your request (normally this contains the Exception)
15163 loadexception : true
15167 this.proxy = Roo.factory(this.proxy, Roo.data);
15168 this.proxy.xmodule = this.xmodule || false;
15169 this.relayEvents(this.proxy, ["loadexception"]);
15171 this.sortToggle = {};
15172 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15174 Roo.data.Store.superclass.constructor.call(this);
15176 if(this.inlineData){
15177 this.loadData(this.inlineData);
15178 delete this.inlineData;
15182 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15184 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15185 * without a remote query - used by combo/forms at present.
15189 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15192 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15195 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15196 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15199 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15200 * on any HTTP request
15203 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15206 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15210 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15211 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15213 remoteSort : false,
15216 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15217 * loaded or when a record is removed. (defaults to false).
15219 pruneModifiedRecords : false,
15222 lastOptions : null,
15225 * Add Records to the Store and fires the add event.
15226 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15228 add : function(records){
15229 records = [].concat(records);
15230 for(var i = 0, len = records.length; i < len; i++){
15231 records[i].join(this);
15233 var index = this.data.length;
15234 this.data.addAll(records);
15235 this.fireEvent("add", this, records, index);
15239 * Remove a Record from the Store and fires the remove event.
15240 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15242 remove : function(record){
15243 var index = this.data.indexOf(record);
15244 this.data.removeAt(index);
15246 if(this.pruneModifiedRecords){
15247 this.modified.remove(record);
15249 this.fireEvent("remove", this, record, index);
15253 * Remove all Records from the Store and fires the clear event.
15255 removeAll : function(){
15257 if(this.pruneModifiedRecords){
15258 this.modified = [];
15260 this.fireEvent("clear", this);
15264 * Inserts Records to the Store at the given index and fires the add event.
15265 * @param {Number} index The start index at which to insert the passed Records.
15266 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15268 insert : function(index, records){
15269 records = [].concat(records);
15270 for(var i = 0, len = records.length; i < len; i++){
15271 this.data.insert(index, records[i]);
15272 records[i].join(this);
15274 this.fireEvent("add", this, records, index);
15278 * Get the index within the cache of the passed Record.
15279 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15280 * @return {Number} The index of the passed Record. Returns -1 if not found.
15282 indexOf : function(record){
15283 return this.data.indexOf(record);
15287 * Get the index within the cache of the Record with the passed id.
15288 * @param {String} id The id of the Record to find.
15289 * @return {Number} The index of the Record. Returns -1 if not found.
15291 indexOfId : function(id){
15292 return this.data.indexOfKey(id);
15296 * Get the Record with the specified id.
15297 * @param {String} id The id of the Record to find.
15298 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15300 getById : function(id){
15301 return this.data.key(id);
15305 * Get the Record at the specified index.
15306 * @param {Number} index The index of the Record to find.
15307 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15309 getAt : function(index){
15310 return this.data.itemAt(index);
15314 * Returns a range of Records between specified indices.
15315 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15316 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15317 * @return {Roo.data.Record[]} An array of Records
15319 getRange : function(start, end){
15320 return this.data.getRange(start, end);
15324 storeOptions : function(o){
15325 o = Roo.apply({}, o);
15328 this.lastOptions = o;
15332 * Loads the Record cache from the configured Proxy using the configured Reader.
15334 * If using remote paging, then the first load call must specify the <em>start</em>
15335 * and <em>limit</em> properties in the options.params property to establish the initial
15336 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15338 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15339 * and this call will return before the new data has been loaded. Perform any post-processing
15340 * in a callback function, or in a "load" event handler.</strong>
15342 * @param {Object} options An object containing properties which control loading options:<ul>
15343 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15344 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15345 * passed the following arguments:<ul>
15346 * <li>r : Roo.data.Record[]</li>
15347 * <li>options: Options object from the load call</li>
15348 * <li>success: Boolean success indicator</li></ul></li>
15349 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15350 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15353 load : function(options){
15354 options = options || {};
15355 if(this.fireEvent("beforeload", this, options) !== false){
15356 this.storeOptions(options);
15357 var p = Roo.apply(options.params || {}, this.baseParams);
15358 // if meta was not loaded from remote source.. try requesting it.
15359 if (!this.reader.metaFromRemote) {
15360 p._requestMeta = 1;
15362 if(this.sortInfo && this.remoteSort){
15363 var pn = this.paramNames;
15364 p[pn["sort"]] = this.sortInfo.field;
15365 p[pn["dir"]] = this.sortInfo.direction;
15367 if (this.multiSort) {
15368 var pn = this.paramNames;
15369 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15372 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15377 * Reloads the Record cache from the configured Proxy using the configured Reader and
15378 * the options from the last load operation performed.
15379 * @param {Object} options (optional) An object containing properties which may override the options
15380 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15381 * the most recently used options are reused).
15383 reload : function(options){
15384 this.load(Roo.applyIf(options||{}, this.lastOptions));
15388 // Called as a callback by the Reader during a load operation.
15389 loadRecords : function(o, options, success){
15390 if(!o || success === false){
15391 if(success !== false){
15392 this.fireEvent("load", this, [], options, o);
15394 if(options.callback){
15395 options.callback.call(options.scope || this, [], options, false);
15399 // if data returned failure - throw an exception.
15400 if (o.success === false) {
15401 // show a message if no listener is registered.
15402 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15403 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15405 // loadmask wil be hooked into this..
15406 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15409 var r = o.records, t = o.totalRecords || r.length;
15411 this.fireEvent("beforeloadadd", this, r, options, o);
15413 if(!options || options.add !== true){
15414 if(this.pruneModifiedRecords){
15415 this.modified = [];
15417 for(var i = 0, len = r.length; i < len; i++){
15421 this.data = this.snapshot;
15422 delete this.snapshot;
15425 this.data.addAll(r);
15426 this.totalLength = t;
15428 this.fireEvent("datachanged", this);
15430 this.totalLength = Math.max(t, this.data.length+r.length);
15434 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15436 var e = new Roo.data.Record({});
15438 e.set(this.parent.displayField, this.parent.emptyTitle);
15439 e.set(this.parent.valueField, '');
15444 this.fireEvent("load", this, r, options, o);
15445 if(options.callback){
15446 options.callback.call(options.scope || this, r, options, true);
15452 * Loads data from a passed data block. A Reader which understands the format of the data
15453 * must have been configured in the constructor.
15454 * @param {Object} data The data block from which to read the Records. The format of the data expected
15455 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15456 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15458 loadData : function(o, append){
15459 var r = this.reader.readRecords(o);
15460 this.loadRecords(r, {add: append}, true);
15464 * using 'cn' the nested child reader read the child array into it's child stores.
15465 * @param {Object} rec The record with a 'children array
15467 loadDataFromChildren : function(rec)
15469 this.loadData(this.reader.toLoadData(rec));
15474 * Gets the number of cached records.
15476 * <em>If using paging, this may not be the total size of the dataset. If the data object
15477 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15478 * the data set size</em>
15480 getCount : function(){
15481 return this.data.length || 0;
15485 * Gets the total number of records in the dataset as returned by the server.
15487 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15488 * the dataset size</em>
15490 getTotalCount : function(){
15491 return this.totalLength || 0;
15495 * Returns the sort state of the Store as an object with two properties:
15497 field {String} The name of the field by which the Records are sorted
15498 direction {String} The sort order, "ASC" or "DESC"
15501 getSortState : function(){
15502 return this.sortInfo;
15506 applySort : function(){
15507 if(this.sortInfo && !this.remoteSort){
15508 var s = this.sortInfo, f = s.field;
15509 var st = this.fields.get(f).sortType;
15510 var fn = function(r1, r2){
15511 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15512 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15514 this.data.sort(s.direction, fn);
15515 if(this.snapshot && this.snapshot != this.data){
15516 this.snapshot.sort(s.direction, fn);
15522 * Sets the default sort column and order to be used by the next load operation.
15523 * @param {String} fieldName The name of the field to sort by.
15524 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15526 setDefaultSort : function(field, dir){
15527 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15531 * Sort the Records.
15532 * If remote sorting is used, the sort is performed on the server, and the cache is
15533 * reloaded. If local sorting is used, the cache is sorted internally.
15534 * @param {String} fieldName The name of the field to sort by.
15535 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15537 sort : function(fieldName, dir){
15538 var f = this.fields.get(fieldName);
15540 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15542 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15543 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15548 this.sortToggle[f.name] = dir;
15549 this.sortInfo = {field: f.name, direction: dir};
15550 if(!this.remoteSort){
15552 this.fireEvent("datachanged", this);
15554 this.load(this.lastOptions);
15559 * Calls the specified function for each of the Records in the cache.
15560 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15561 * Returning <em>false</em> aborts and exits the iteration.
15562 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15564 each : function(fn, scope){
15565 this.data.each(fn, scope);
15569 * Gets all records modified since the last commit. Modified records are persisted across load operations
15570 * (e.g., during paging).
15571 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15573 getModifiedRecords : function(){
15574 return this.modified;
15578 createFilterFn : function(property, value, anyMatch){
15579 if(!value.exec){ // not a regex
15580 value = String(value);
15581 if(value.length == 0){
15584 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15586 return function(r){
15587 return value.test(r.data[property]);
15592 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15593 * @param {String} property A field on your records
15594 * @param {Number} start The record index to start at (defaults to 0)
15595 * @param {Number} end The last record index to include (defaults to length - 1)
15596 * @return {Number} The sum
15598 sum : function(property, start, end){
15599 var rs = this.data.items, v = 0;
15600 start = start || 0;
15601 end = (end || end === 0) ? end : rs.length-1;
15603 for(var i = start; i <= end; i++){
15604 v += (rs[i].data[property] || 0);
15610 * Filter the records by a specified property.
15611 * @param {String} field A field on your records
15612 * @param {String/RegExp} value Either a string that the field
15613 * should start with or a RegExp to test against the field
15614 * @param {Boolean} anyMatch True to match any part not just the beginning
15616 filter : function(property, value, anyMatch){
15617 var fn = this.createFilterFn(property, value, anyMatch);
15618 return fn ? this.filterBy(fn) : this.clearFilter();
15622 * Filter by a function. The specified function will be called with each
15623 * record in this data source. If the function returns true the record is included,
15624 * otherwise it is filtered.
15625 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15626 * @param {Object} scope (optional) The scope of the function (defaults to this)
15628 filterBy : function(fn, scope){
15629 this.snapshot = this.snapshot || this.data;
15630 this.data = this.queryBy(fn, scope||this);
15631 this.fireEvent("datachanged", this);
15635 * Query the records by a specified property.
15636 * @param {String} field A field on your records
15637 * @param {String/RegExp} value Either a string that the field
15638 * should start with or a RegExp to test against the field
15639 * @param {Boolean} anyMatch True to match any part not just the beginning
15640 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15642 query : function(property, value, anyMatch){
15643 var fn = this.createFilterFn(property, value, anyMatch);
15644 return fn ? this.queryBy(fn) : this.data.clone();
15648 * Query by a function. The specified function will be called with each
15649 * record in this data source. If the function returns true the record is included
15651 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15652 * @param {Object} scope (optional) The scope of the function (defaults to this)
15653 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15655 queryBy : function(fn, scope){
15656 var data = this.snapshot || this.data;
15657 return data.filterBy(fn, scope||this);
15661 * Collects unique values for a particular dataIndex from this store.
15662 * @param {String} dataIndex The property to collect
15663 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15664 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15665 * @return {Array} An array of the unique values
15667 collect : function(dataIndex, allowNull, bypassFilter){
15668 var d = (bypassFilter === true && this.snapshot) ?
15669 this.snapshot.items : this.data.items;
15670 var v, sv, r = [], l = {};
15671 for(var i = 0, len = d.length; i < len; i++){
15672 v = d[i].data[dataIndex];
15674 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15683 * Revert to a view of the Record cache with no filtering applied.
15684 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15686 clearFilter : function(suppressEvent){
15687 if(this.snapshot && this.snapshot != this.data){
15688 this.data = this.snapshot;
15689 delete this.snapshot;
15690 if(suppressEvent !== true){
15691 this.fireEvent("datachanged", this);
15697 afterEdit : function(record){
15698 if(this.modified.indexOf(record) == -1){
15699 this.modified.push(record);
15701 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15705 afterReject : function(record){
15706 this.modified.remove(record);
15707 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15711 afterCommit : function(record){
15712 this.modified.remove(record);
15713 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15717 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15718 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15720 commitChanges : function(){
15721 var m = this.modified.slice(0);
15722 this.modified = [];
15723 for(var i = 0, len = m.length; i < len; i++){
15729 * Cancel outstanding changes on all changed records.
15731 rejectChanges : function(){
15732 var m = this.modified.slice(0);
15733 this.modified = [];
15734 for(var i = 0, len = m.length; i < len; i++){
15739 onMetaChange : function(meta, rtype, o){
15740 this.recordType = rtype;
15741 this.fields = rtype.prototype.fields;
15742 delete this.snapshot;
15743 this.sortInfo = meta.sortInfo || this.sortInfo;
15744 this.modified = [];
15745 this.fireEvent('metachange', this, this.reader.meta);
15748 moveIndex : function(data, type)
15750 var index = this.indexOf(data);
15752 var newIndex = index + type;
15756 this.insert(newIndex, data);
15761 * Ext JS Library 1.1.1
15762 * Copyright(c) 2006-2007, Ext JS, LLC.
15764 * Originally Released Under LGPL - original licence link has changed is not relivant.
15767 * <script type="text/javascript">
15771 * @class Roo.data.SimpleStore
15772 * @extends Roo.data.Store
15773 * Small helper class to make creating Stores from Array data easier.
15774 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15775 * @cfg {Array} fields An array of field definition objects, or field name strings.
15776 * @cfg {Object} an existing reader (eg. copied from another store)
15777 * @cfg {Array} data The multi-dimensional array of data
15778 * @cfg {Roo.data.DataProxy} proxy [not-required]
15779 * @cfg {Roo.data.Reader} reader [not-required]
15781 * @param {Object} config
15783 Roo.data.SimpleStore = function(config)
15785 Roo.data.SimpleStore.superclass.constructor.call(this, {
15787 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15790 Roo.data.Record.create(config.fields)
15792 proxy : new Roo.data.MemoryProxy(config.data)
15796 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15798 * Ext JS Library 1.1.1
15799 * Copyright(c) 2006-2007, Ext JS, LLC.
15801 * Originally Released Under LGPL - original licence link has changed is not relivant.
15804 * <script type="text/javascript">
15809 * @extends Roo.data.Store
15810 * @class Roo.data.JsonStore
15811 * Small helper class to make creating Stores for JSON data easier. <br/>
15813 var store = new Roo.data.JsonStore({
15814 url: 'get-images.php',
15816 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15819 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15820 * JsonReader and HttpProxy (unless inline data is provided).</b>
15821 * @cfg {Array} fields An array of field definition objects, or field name strings.
15823 * @param {Object} config
15825 Roo.data.JsonStore = function(c){
15826 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15827 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15828 reader: new Roo.data.JsonReader(c, c.fields)
15831 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15833 * Ext JS Library 1.1.1
15834 * Copyright(c) 2006-2007, Ext JS, LLC.
15836 * Originally Released Under LGPL - original licence link has changed is not relivant.
15839 * <script type="text/javascript">
15843 Roo.data.Field = function(config){
15844 if(typeof config == "string"){
15845 config = {name: config};
15847 Roo.apply(this, config);
15850 this.type = "auto";
15853 var st = Roo.data.SortTypes;
15854 // named sortTypes are supported, here we look them up
15855 if(typeof this.sortType == "string"){
15856 this.sortType = st[this.sortType];
15859 // set default sortType for strings and dates
15860 if(!this.sortType){
15863 this.sortType = st.asUCString;
15866 this.sortType = st.asDate;
15869 this.sortType = st.none;
15874 var stripRe = /[\$,%]/g;
15876 // prebuilt conversion function for this field, instead of
15877 // switching every time we're reading a value
15879 var cv, dateFormat = this.dateFormat;
15884 cv = function(v){ return v; };
15887 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15891 return v !== undefined && v !== null && v !== '' ?
15892 parseInt(String(v).replace(stripRe, ""), 10) : '';
15897 return v !== undefined && v !== null && v !== '' ?
15898 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15903 cv = function(v){ return v === true || v === "true" || v == 1; };
15910 if(v instanceof Date){
15914 if(dateFormat == "timestamp"){
15915 return new Date(v*1000);
15917 return Date.parseDate(v, dateFormat);
15919 var parsed = Date.parse(v);
15920 return parsed ? new Date(parsed) : null;
15929 Roo.data.Field.prototype = {
15937 * Ext JS Library 1.1.1
15938 * Copyright(c) 2006-2007, Ext JS, LLC.
15940 * Originally Released Under LGPL - original licence link has changed is not relivant.
15943 * <script type="text/javascript">
15946 // Base class for reading structured data from a data source. This class is intended to be
15947 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15950 * @class Roo.data.DataReader
15952 * Base class for reading structured data from a data source. This class is intended to be
15953 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15956 Roo.data.DataReader = function(meta, recordType){
15960 this.recordType = recordType instanceof Array ?
15961 Roo.data.Record.create(recordType) : recordType;
15964 Roo.data.DataReader.prototype = {
15967 readerType : 'Data',
15969 * Create an empty record
15970 * @param {Object} data (optional) - overlay some values
15971 * @return {Roo.data.Record} record created.
15973 newRow : function(d) {
15975 this.recordType.prototype.fields.each(function(c) {
15977 case 'int' : da[c.name] = 0; break;
15978 case 'date' : da[c.name] = new Date(); break;
15979 case 'float' : da[c.name] = 0.0; break;
15980 case 'boolean' : da[c.name] = false; break;
15981 default : da[c.name] = ""; break;
15985 return new this.recordType(Roo.apply(da, d));
15991 * Ext JS Library 1.1.1
15992 * Copyright(c) 2006-2007, Ext JS, LLC.
15994 * Originally Released Under LGPL - original licence link has changed is not relivant.
15997 * <script type="text/javascript">
16001 * @class Roo.data.DataProxy
16002 * @extends Roo.util.Observable
16004 * This class is an abstract base class for implementations which provide retrieval of
16005 * unformatted data objects.<br>
16007 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16008 * (of the appropriate type which knows how to parse the data object) to provide a block of
16009 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16011 * Custom implementations must implement the load method as described in
16012 * {@link Roo.data.HttpProxy#load}.
16014 Roo.data.DataProxy = function(){
16017 * @event beforeload
16018 * Fires before a network request is made to retrieve a data object.
16019 * @param {Object} This DataProxy object.
16020 * @param {Object} params The params parameter to the load function.
16025 * Fires before the load method's callback is called.
16026 * @param {Object} This DataProxy object.
16027 * @param {Object} o The data object.
16028 * @param {Object} arg The callback argument object passed to the load function.
16032 * @event loadexception
16033 * Fires if an Exception occurs during data retrieval.
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.
16037 * @param {Object} e The Exception.
16039 loadexception : true
16041 Roo.data.DataProxy.superclass.constructor.call(this);
16044 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16047 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16051 * Ext JS Library 1.1.1
16052 * Copyright(c) 2006-2007, Ext JS, LLC.
16054 * Originally Released Under LGPL - original licence link has changed is not relivant.
16057 * <script type="text/javascript">
16060 * @class Roo.data.MemoryProxy
16061 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16062 * to the Reader when its load method is called.
16064 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16066 Roo.data.MemoryProxy = function(data){
16070 Roo.data.MemoryProxy.superclass.constructor.call(this);
16074 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16077 * Load data from the requested source (in this case an in-memory
16078 * data object passed to the constructor), read the data object into
16079 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16080 * process that block using the passed callback.
16081 * @param {Object} params This parameter is not used by the MemoryProxy class.
16082 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16083 * object into a block of Roo.data.Records.
16084 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16085 * The function must be passed <ul>
16086 * <li>The Record block object</li>
16087 * <li>The "arg" argument from the load function</li>
16088 * <li>A boolean success indicator</li>
16090 * @param {Object} scope The scope in which to call the callback
16091 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16093 load : function(params, reader, callback, scope, arg){
16094 params = params || {};
16097 result = reader.readRecords(params.data ? params.data :this.data);
16099 this.fireEvent("loadexception", this, arg, null, e);
16100 callback.call(scope, null, arg, false);
16103 callback.call(scope, result, arg, true);
16107 update : function(params, records){
16112 * Ext JS Library 1.1.1
16113 * Copyright(c) 2006-2007, Ext JS, LLC.
16115 * Originally Released Under LGPL - original licence link has changed is not relivant.
16118 * <script type="text/javascript">
16121 * @class Roo.data.HttpProxy
16122 * @extends Roo.data.DataProxy
16123 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16124 * configured to reference a certain URL.<br><br>
16126 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16127 * from which the running page was served.<br><br>
16129 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16131 * Be aware that to enable the browser to parse an XML document, the server must set
16132 * the Content-Type header in the HTTP response to "text/xml".
16134 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16135 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16136 * will be used to make the request.
16138 Roo.data.HttpProxy = function(conn){
16139 Roo.data.HttpProxy.superclass.constructor.call(this);
16140 // is conn a conn config or a real conn?
16142 this.useAjax = !conn || !conn.events;
16146 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16147 // thse are take from connection...
16150 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16153 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16154 * extra parameters to each request made by this object. (defaults to undefined)
16157 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16158 * to each request made by this object. (defaults to undefined)
16161 * @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)
16164 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16167 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16173 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16177 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16178 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16179 * a finer-grained basis than the DataProxy events.
16181 getConnection : function(){
16182 return this.useAjax ? Roo.Ajax : this.conn;
16186 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16187 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16188 * process that block using the passed callback.
16189 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16190 * for the request to the remote server.
16191 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16192 * object into a block of Roo.data.Records.
16193 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16194 * The function must be passed <ul>
16195 * <li>The Record block object</li>
16196 * <li>The "arg" argument from the load function</li>
16197 * <li>A boolean success indicator</li>
16199 * @param {Object} scope The scope in which to call the callback
16200 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16202 load : function(params, reader, callback, scope, arg){
16203 if(this.fireEvent("beforeload", this, params) !== false){
16205 params : params || {},
16207 callback : callback,
16212 callback : this.loadResponse,
16216 Roo.applyIf(o, this.conn);
16217 if(this.activeRequest){
16218 Roo.Ajax.abort(this.activeRequest);
16220 this.activeRequest = Roo.Ajax.request(o);
16222 this.conn.request(o);
16225 callback.call(scope||this, null, arg, false);
16230 loadResponse : function(o, success, response){
16231 delete this.activeRequest;
16233 this.fireEvent("loadexception", this, o, response);
16234 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16239 result = o.reader.read(response);
16241 this.fireEvent("loadexception", this, o, response, e);
16242 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16246 this.fireEvent("load", this, o, o.request.arg);
16247 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16251 update : function(dataSet){
16256 updateResponse : function(dataSet){
16261 * Ext JS Library 1.1.1
16262 * Copyright(c) 2006-2007, Ext JS, LLC.
16264 * Originally Released Under LGPL - original licence link has changed is not relivant.
16267 * <script type="text/javascript">
16271 * @class Roo.data.ScriptTagProxy
16272 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16273 * other than the originating domain of the running page.<br><br>
16275 * <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
16276 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16278 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16279 * source code that is used as the source inside a <script> tag.<br><br>
16281 * In order for the browser to process the returned data, the server must wrap the data object
16282 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16283 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16284 * depending on whether the callback name was passed:
16287 boolean scriptTag = false;
16288 String cb = request.getParameter("callback");
16291 response.setContentType("text/javascript");
16293 response.setContentType("application/x-json");
16295 Writer out = response.getWriter();
16297 out.write(cb + "(");
16299 out.print(dataBlock.toJsonString());
16306 * @param {Object} config A configuration object.
16308 Roo.data.ScriptTagProxy = function(config){
16309 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16310 Roo.apply(this, config);
16311 this.head = document.getElementsByTagName("head")[0];
16314 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16316 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16318 * @cfg {String} url The URL from which to request the data object.
16321 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16325 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16326 * the server the name of the callback function set up by the load call to process the returned data object.
16327 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16328 * javascript output which calls this named function passing the data object as its only parameter.
16330 callbackParam : "callback",
16332 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16333 * name to the request.
16338 * Load data from the configured URL, read the data object into
16339 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16340 * process that block using the passed callback.
16341 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16342 * for the request to the remote server.
16343 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16344 * object into a block of Roo.data.Records.
16345 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16346 * The function must be passed <ul>
16347 * <li>The Record block object</li>
16348 * <li>The "arg" argument from the load function</li>
16349 * <li>A boolean success indicator</li>
16351 * @param {Object} scope The scope in which to call the callback
16352 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16354 load : function(params, reader, callback, scope, arg){
16355 if(this.fireEvent("beforeload", this, params) !== false){
16357 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16359 var url = this.url;
16360 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16362 url += "&_dc=" + (new Date().getTime());
16364 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16367 cb : "stcCallback"+transId,
16368 scriptId : "stcScript"+transId,
16372 callback : callback,
16378 window[trans.cb] = function(o){
16379 conn.handleResponse(o, trans);
16382 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16384 if(this.autoAbort !== false){
16388 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16390 var script = document.createElement("script");
16391 script.setAttribute("src", url);
16392 script.setAttribute("type", "text/javascript");
16393 script.setAttribute("id", trans.scriptId);
16394 this.head.appendChild(script);
16396 this.trans = trans;
16398 callback.call(scope||this, null, arg, false);
16403 isLoading : function(){
16404 return this.trans ? true : false;
16408 * Abort the current server request.
16410 abort : function(){
16411 if(this.isLoading()){
16412 this.destroyTrans(this.trans);
16417 destroyTrans : function(trans, isLoaded){
16418 this.head.removeChild(document.getElementById(trans.scriptId));
16419 clearTimeout(trans.timeoutId);
16421 window[trans.cb] = undefined;
16423 delete window[trans.cb];
16426 // if hasn't been loaded, wait for load to remove it to prevent script error
16427 window[trans.cb] = function(){
16428 window[trans.cb] = undefined;
16430 delete window[trans.cb];
16437 handleResponse : function(o, trans){
16438 this.trans = false;
16439 this.destroyTrans(trans, true);
16442 result = trans.reader.readRecords(o);
16444 this.fireEvent("loadexception", this, o, trans.arg, e);
16445 trans.callback.call(trans.scope||window, null, trans.arg, false);
16448 this.fireEvent("load", this, o, trans.arg);
16449 trans.callback.call(trans.scope||window, result, trans.arg, true);
16453 handleFailure : function(trans){
16454 this.trans = false;
16455 this.destroyTrans(trans, false);
16456 this.fireEvent("loadexception", this, null, trans.arg);
16457 trans.callback.call(trans.scope||window, null, trans.arg, false);
16461 * Ext JS Library 1.1.1
16462 * Copyright(c) 2006-2007, Ext JS, LLC.
16464 * Originally Released Under LGPL - original licence link has changed is not relivant.
16467 * <script type="text/javascript">
16471 * @class Roo.data.JsonReader
16472 * @extends Roo.data.DataReader
16473 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16474 * based on mappings in a provided Roo.data.Record constructor.
16476 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16477 * in the reply previously.
16482 var RecordDef = Roo.data.Record.create([
16483 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16484 {name: 'occupation'} // This field will use "occupation" as the mapping.
16486 var myReader = new Roo.data.JsonReader({
16487 totalProperty: "results", // The property which contains the total dataset size (optional)
16488 root: "rows", // The property which contains an Array of row objects
16489 id: "id" // The property within each row object that provides an ID for the record (optional)
16493 * This would consume a JSON file like this:
16495 { 'results': 2, 'rows': [
16496 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16497 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16500 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16501 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16502 * paged from the remote server.
16503 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16504 * @cfg {String} root name of the property which contains the Array of row objects.
16505 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16506 * @cfg {Array} fields Array of field definition objects
16508 * Create a new JsonReader
16509 * @param {Object} meta Metadata configuration options
16510 * @param {Object} recordType Either an Array of field definition objects,
16511 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16513 Roo.data.JsonReader = function(meta, recordType){
16516 // set some defaults:
16517 Roo.applyIf(meta, {
16518 totalProperty: 'total',
16519 successProperty : 'success',
16524 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16526 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16528 readerType : 'Json',
16531 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16532 * Used by Store query builder to append _requestMeta to params.
16535 metaFromRemote : false,
16537 * This method is only used by a DataProxy which has retrieved data from a remote server.
16538 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16539 * @return {Object} data A data block which is used by an Roo.data.Store object as
16540 * a cache of Roo.data.Records.
16542 read : function(response){
16543 var json = response.responseText;
16545 var o = /* eval:var:o */ eval("("+json+")");
16547 throw {message: "JsonReader.read: Json object not found"};
16553 this.metaFromRemote = true;
16554 this.meta = o.metaData;
16555 this.recordType = Roo.data.Record.create(o.metaData.fields);
16556 this.onMetaChange(this.meta, this.recordType, o);
16558 return this.readRecords(o);
16561 // private function a store will implement
16562 onMetaChange : function(meta, recordType, o){
16569 simpleAccess: function(obj, subsc) {
16576 getJsonAccessor: function(){
16578 return function(expr) {
16580 return(re.test(expr))
16581 ? new Function("obj", "return obj." + expr)
16586 return Roo.emptyFn;
16591 * Create a data block containing Roo.data.Records from an XML document.
16592 * @param {Object} o An object which contains an Array of row objects in the property specified
16593 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16594 * which contains the total size of the dataset.
16595 * @return {Object} data A data block which is used by an Roo.data.Store object as
16596 * a cache of Roo.data.Records.
16598 readRecords : function(o){
16600 * After any data loads, the raw JSON data is available for further custom processing.
16604 var s = this.meta, Record = this.recordType,
16605 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16607 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16609 if(s.totalProperty) {
16610 this.getTotal = this.getJsonAccessor(s.totalProperty);
16612 if(s.successProperty) {
16613 this.getSuccess = this.getJsonAccessor(s.successProperty);
16615 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16617 var g = this.getJsonAccessor(s.id);
16618 this.getId = function(rec) {
16620 return (r === undefined || r === "") ? null : r;
16623 this.getId = function(){return null;};
16626 for(var jj = 0; jj < fl; jj++){
16628 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16629 this.ef[jj] = this.getJsonAccessor(map);
16633 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16634 if(s.totalProperty){
16635 var vt = parseInt(this.getTotal(o), 10);
16640 if(s.successProperty){
16641 var vs = this.getSuccess(o);
16642 if(vs === false || vs === 'false'){
16647 for(var i = 0; i < c; i++){
16650 var id = this.getId(n);
16651 for(var j = 0; j < fl; j++){
16653 var v = this.ef[j](n);
16655 Roo.log('missing convert for ' + f.name);
16659 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16661 var record = new Record(values, id);
16663 records[i] = record;
16669 totalRecords : totalRecords
16672 // used when loading children.. @see loadDataFromChildren
16673 toLoadData: function(rec)
16675 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16676 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16677 return { data : data, total : data.length };
16682 * Ext JS Library 1.1.1
16683 * Copyright(c) 2006-2007, Ext JS, LLC.
16685 * Originally Released Under LGPL - original licence link has changed is not relivant.
16688 * <script type="text/javascript">
16692 * @class Roo.data.ArrayReader
16693 * @extends Roo.data.DataReader
16694 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16695 * Each element of that Array represents a row of data fields. The
16696 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16697 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16701 var RecordDef = Roo.data.Record.create([
16702 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16703 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16705 var myReader = new Roo.data.ArrayReader({
16706 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16710 * This would consume an Array like this:
16712 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16716 * Create a new JsonReader
16717 * @param {Object} meta Metadata configuration options.
16718 * @param {Object|Array} recordType Either an Array of field definition objects
16720 * @cfg {Array} fields Array of field definition objects
16721 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16722 * as specified to {@link Roo.data.Record#create},
16723 * or an {@link Roo.data.Record} object
16726 * created using {@link Roo.data.Record#create}.
16728 Roo.data.ArrayReader = function(meta, recordType)
16730 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16733 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16736 * Create a data block containing Roo.data.Records from an XML document.
16737 * @param {Object} o An Array of row objects which represents the dataset.
16738 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16739 * a cache of Roo.data.Records.
16741 readRecords : function(o)
16743 var sid = this.meta ? this.meta.id : null;
16744 var recordType = this.recordType, fields = recordType.prototype.fields;
16747 for(var i = 0; i < root.length; i++){
16750 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16751 for(var j = 0, jlen = fields.length; j < jlen; j++){
16752 var f = fields.items[j];
16753 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16754 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16756 values[f.name] = v;
16758 var record = new recordType(values, id);
16760 records[records.length] = record;
16764 totalRecords : records.length
16767 // used when loading children.. @see loadDataFromChildren
16768 toLoadData: function(rec)
16770 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16771 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16782 * @class Roo.bootstrap.form.ComboBox
16783 * @extends Roo.bootstrap.form.TriggerField
16784 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16785 * @cfg {Boolean} append (true|false) default false
16786 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16787 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16788 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16789 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16790 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16791 * @cfg {Boolean} animate default true
16792 * @cfg {Boolean} emptyResultText only for touch device
16793 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16794 * @cfg {String} emptyTitle default ''
16795 * @cfg {Number} width fixed with? experimental
16797 * Create a new ComboBox.
16798 * @param {Object} config Configuration options
16800 Roo.bootstrap.form.ComboBox = function(config){
16801 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16805 * Fires when the dropdown list is expanded
16806 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16811 * Fires when the dropdown list is collapsed
16812 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16816 * @event beforeselect
16817 * Fires before a list item is selected. Return false to cancel the selection.
16818 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16819 * @param {Roo.data.Record} record The data record returned from the underlying store
16820 * @param {Number} index The index of the selected item in the dropdown list
16822 'beforeselect' : true,
16825 * Fires when a list item is selected
16826 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16827 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16828 * @param {Number} index The index of the selected item in the dropdown list
16832 * @event beforequery
16833 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16834 * The event object passed has these properties:
16835 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16836 * @param {String} query The query
16837 * @param {Boolean} forceAll true to force "all" query
16838 * @param {Boolean} cancel true to cancel the query
16839 * @param {Object} e The query event object
16841 'beforequery': true,
16844 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16845 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16850 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16851 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16852 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16857 * Fires when the remove value from the combobox array
16858 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862 * @event afterremove
16863 * Fires when the remove value from the combobox array
16864 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866 'afterremove' : true,
16868 * @event specialfilter
16869 * Fires when specialfilter
16870 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872 'specialfilter' : true,
16875 * Fires when tick the element
16876 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880 * @event touchviewdisplay
16881 * Fires when touch view require special display (default is using displayField)
16882 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883 * @param {Object} cfg set html .
16885 'touchviewdisplay' : true
16890 this.tickItems = [];
16892 this.selectedIndex = -1;
16893 if(this.mode == 'local'){
16894 if(config.queryDelay === undefined){
16895 this.queryDelay = 10;
16897 if(config.minChars === undefined){
16903 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16906 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16907 * rendering into an Roo.Editor, defaults to false)
16910 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16911 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16914 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16917 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16918 * the dropdown list (defaults to undefined, with no header element)
16922 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16926 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16928 listWidth: undefined,
16930 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16931 * mode = 'remote' or 'text' if mode = 'local')
16933 displayField: undefined,
16936 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16937 * mode = 'remote' or 'value' if mode = 'local').
16938 * Note: use of a valueField requires the user make a selection
16939 * in order for a value to be mapped.
16941 valueField: undefined,
16943 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16948 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16949 * field's data value (defaults to the underlying DOM element's name)
16951 hiddenName: undefined,
16953 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16957 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16959 selectedClass: 'active',
16962 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16966 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16967 * anchor positions (defaults to 'tl-bl')
16969 listAlign: 'tl-bl?',
16971 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16975 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16976 * query specified by the allQuery config option (defaults to 'query')
16978 triggerAction: 'query',
16980 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16981 * (defaults to 4, does not apply if editable = false)
16985 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16986 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16990 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16991 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16995 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16996 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17000 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17001 * when editable = true (defaults to false)
17003 selectOnFocus:false,
17005 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17007 queryParam: 'query',
17009 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17010 * when mode = 'remote' (defaults to 'Loading...')
17012 loadingText: 'Loading...',
17014 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17018 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17022 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17023 * traditional select (defaults to true)
17027 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17031 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17035 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17036 * listWidth has a higher value)
17040 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17041 * allow the user to set arbitrary text into the field (defaults to false)
17043 forceSelection:false,
17045 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17046 * if typeAhead = true (defaults to 250)
17048 typeAheadDelay : 250,
17050 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17051 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17053 valueNotFoundText : undefined,
17055 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17057 blockFocus : false,
17060 * @cfg {Boolean} disableClear Disable showing of clear button.
17062 disableClear : false,
17064 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17066 alwaysQuery : false,
17069 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17074 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17076 invalidClass : "has-warning",
17079 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17081 validClass : "has-success",
17084 * @cfg {Boolean} specialFilter (true|false) special filter default false
17086 specialFilter : false,
17089 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17091 mobileTouchView : true,
17094 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17096 useNativeIOS : false,
17099 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17101 mobile_restrict_height : false,
17103 ios_options : false,
17115 btnPosition : 'right',
17116 triggerList : true,
17117 showToggleBtn : true,
17119 emptyResultText: 'Empty',
17120 triggerText : 'Select',
17124 // element that contains real text value.. (when hidden is used..)
17126 getAutoCreate : function()
17131 * Render classic select for iso
17134 if(Roo.isIOS && this.useNativeIOS){
17135 cfg = this.getAutoCreateNativeIOS();
17143 if(Roo.isTouch && this.mobileTouchView){
17144 cfg = this.getAutoCreateTouchView();
17151 if(!this.tickable){
17152 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17157 * ComboBox with tickable selections
17160 var align = this.labelAlign || this.parentLabelAlign();
17163 cls : 'form-group roo-combobox-tickable' //input-group
17166 var btn_text_select = '';
17167 var btn_text_done = '';
17168 var btn_text_cancel = '';
17170 if (this.btn_text_show) {
17171 btn_text_select = 'Select';
17172 btn_text_done = 'Done';
17173 btn_text_cancel = 'Cancel';
17178 cls : 'tickable-buttons',
17183 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17184 //html : this.triggerText
17185 html: btn_text_select
17191 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17193 html: btn_text_done
17199 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17201 html: btn_text_cancel
17207 buttons.cn.unshift({
17209 cls: 'roo-select2-search-field-input'
17215 Roo.each(buttons.cn, function(c){
17217 c.cls += ' btn-' + _this.size;
17220 if (_this.disabled) {
17227 style : 'display: contents',
17232 cls: 'form-hidden-field'
17236 cls: 'roo-select2-choices',
17240 cls: 'roo-select2-search-field',
17251 cls: 'roo-select2-container input-group roo-select2-container-multi',
17257 // cls: 'typeahead typeahead-long dropdown-menu',
17258 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17263 if(this.hasFeedback && !this.allowBlank){
17267 cls: 'glyphicon form-control-feedback'
17270 combobox.cn.push(feedback);
17277 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17278 tooltip : 'This field is required'
17280 if (Roo.bootstrap.version == 4) {
17283 style : 'display:none'
17286 if (align ==='left' && this.fieldLabel.length) {
17288 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17295 cls : 'control-label col-form-label',
17296 html : this.fieldLabel
17308 var labelCfg = cfg.cn[1];
17309 var contentCfg = cfg.cn[2];
17312 if(this.indicatorpos == 'right'){
17318 cls : 'control-label col-form-label',
17322 html : this.fieldLabel
17338 labelCfg = cfg.cn[0];
17339 contentCfg = cfg.cn[1];
17343 if(this.labelWidth > 12){
17344 labelCfg.style = "width: " + this.labelWidth + 'px';
17346 if(this.width * 1 > 0){
17347 contentCfg.style = "width: " + this.width + 'px';
17349 if(this.labelWidth < 13 && this.labelmd == 0){
17350 this.labelmd = this.labelWidth;
17353 if(this.labellg > 0){
17354 labelCfg.cls += ' col-lg-' + this.labellg;
17355 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17358 if(this.labelmd > 0){
17359 labelCfg.cls += ' col-md-' + this.labelmd;
17360 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17363 if(this.labelsm > 0){
17364 labelCfg.cls += ' col-sm-' + this.labelsm;
17365 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17368 if(this.labelxs > 0){
17369 labelCfg.cls += ' col-xs-' + this.labelxs;
17370 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17374 } else if ( this.fieldLabel.length) {
17375 // Roo.log(" label");
17380 //cls : 'input-group-addon',
17381 html : this.fieldLabel
17386 if(this.indicatorpos == 'right'){
17390 //cls : 'input-group-addon',
17391 html : this.fieldLabel
17401 // Roo.log(" no label && no align");
17408 ['xs','sm','md','lg'].map(function(size){
17409 if (settings[size]) {
17410 cfg.cls += ' col-' + size + '-' + settings[size];
17418 _initEventsCalled : false,
17421 initEvents: function()
17423 if (this._initEventsCalled) { // as we call render... prevent looping...
17426 this._initEventsCalled = true;
17429 throw "can not find store for combo";
17432 this.indicator = this.indicatorEl();
17434 this.store = Roo.factory(this.store, Roo.data);
17435 this.store.parent = this;
17437 // if we are building from html. then this element is so complex, that we can not really
17438 // use the rendered HTML.
17439 // so we have to trash and replace the previous code.
17440 if (Roo.XComponent.build_from_html) {
17441 // remove this element....
17442 var e = this.el.dom, k=0;
17443 while (e ) { e = e.previousSibling; ++k;}
17448 this.rendered = false;
17450 this.render(this.parent().getChildContainer(true), k);
17453 if(Roo.isIOS && this.useNativeIOS){
17454 this.initIOSView();
17462 if(Roo.isTouch && this.mobileTouchView){
17463 this.initTouchView();
17468 this.initTickableEvents();
17472 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17474 if(this.hiddenName){
17476 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17478 this.hiddenField.dom.value =
17479 this.hiddenValue !== undefined ? this.hiddenValue :
17480 this.value !== undefined ? this.value : '';
17482 // prevent input submission
17483 this.el.dom.removeAttribute('name');
17484 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17489 // this.el.dom.setAttribute('autocomplete', 'off');
17492 var cls = 'x-combo-list';
17494 //this.list = new Roo.Layer({
17495 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17501 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17502 _this.list.setWidth(lw);
17505 this.list.on('mouseover', this.onViewOver, this);
17506 this.list.on('mousemove', this.onViewMove, this);
17507 this.list.on('scroll', this.onViewScroll, this);
17510 this.list.swallowEvent('mousewheel');
17511 this.assetHeight = 0;
17514 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17515 this.assetHeight += this.header.getHeight();
17518 this.innerList = this.list.createChild({cls:cls+'-inner'});
17519 this.innerList.on('mouseover', this.onViewOver, this);
17520 this.innerList.on('mousemove', this.onViewMove, this);
17521 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17523 if(this.allowBlank && !this.pageSize && !this.disableClear){
17524 this.footer = this.list.createChild({cls:cls+'-ft'});
17525 this.pageTb = new Roo.Toolbar(this.footer);
17529 this.footer = this.list.createChild({cls:cls+'-ft'});
17530 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17531 {pageSize: this.pageSize});
17535 if (this.pageTb && this.allowBlank && !this.disableClear) {
17537 this.pageTb.add(new Roo.Toolbar.Fill(), {
17538 cls: 'x-btn-icon x-btn-clear',
17540 handler: function()
17543 _this.clearValue();
17544 _this.onSelect(false, -1);
17549 this.assetHeight += this.footer.getHeight();
17554 this.tpl = Roo.bootstrap.version == 4 ?
17555 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17556 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17559 this.view = new Roo.View(this.list, this.tpl, {
17560 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17562 //this.view.wrapEl.setDisplayed(false);
17563 this.view.on('click', this.onViewClick, this);
17566 this.store.on('beforeload', this.onBeforeLoad, this);
17567 this.store.on('load', this.onLoad, this);
17568 this.store.on('loadexception', this.onLoadException, this);
17570 if(this.resizable){
17571 this.resizer = new Roo.Resizable(this.list, {
17572 pinned:true, handles:'se'
17574 this.resizer.on('resize', function(r, w, h){
17575 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17576 this.listWidth = w;
17577 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17578 this.restrictHeight();
17580 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17583 if(!this.editable){
17584 this.editable = true;
17585 this.setEditable(false);
17590 if (typeof(this.events.add.listeners) != 'undefined') {
17592 this.addicon = this.wrap.createChild(
17593 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17595 this.addicon.on('click', function(e) {
17596 this.fireEvent('add', this);
17599 if (typeof(this.events.edit.listeners) != 'undefined') {
17601 this.editicon = this.wrap.createChild(
17602 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17603 if (this.addicon) {
17604 this.editicon.setStyle('margin-left', '40px');
17606 this.editicon.on('click', function(e) {
17608 // we fire even if inothing is selected..
17609 this.fireEvent('edit', this, this.lastData );
17615 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17616 "up" : function(e){
17617 this.inKeyMode = true;
17621 "down" : function(e){
17622 if(!this.isExpanded()){
17623 this.onTriggerClick();
17625 this.inKeyMode = true;
17630 "enter" : function(e){
17631 // this.onViewClick();
17635 if(this.fireEvent("specialkey", this, e)){
17636 this.onViewClick(false);
17642 "esc" : function(e){
17646 "tab" : function(e){
17649 if(this.fireEvent("specialkey", this, e)){
17650 this.onViewClick(false);
17658 doRelay : function(foo, bar, hname){
17659 if(hname == 'down' || this.scope.isExpanded()){
17660 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17669 this.queryDelay = Math.max(this.queryDelay || 10,
17670 this.mode == 'local' ? 10 : 250);
17673 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17675 if(this.typeAhead){
17676 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17678 if(this.editable !== false){
17679 this.inputEl().on("keyup", this.onKeyUp, this);
17681 if(this.forceSelection){
17682 this.inputEl().on('blur', this.doForce, this);
17686 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17687 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17691 initTickableEvents: function()
17695 if(this.hiddenName){
17697 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17699 this.hiddenField.dom.value =
17700 this.hiddenValue !== undefined ? this.hiddenValue :
17701 this.value !== undefined ? this.value : '';
17703 // prevent input submission
17704 this.el.dom.removeAttribute('name');
17705 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17710 // this.list = this.el.select('ul.dropdown-menu',true).first();
17712 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17713 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17714 if(this.triggerList){
17715 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17718 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17719 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17721 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17722 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17724 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17725 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17727 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17728 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17729 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17732 this.cancelBtn.hide();
17737 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17738 _this.list.setWidth(lw);
17741 this.list.on('mouseover', this.onViewOver, this);
17742 this.list.on('mousemove', this.onViewMove, this);
17744 this.list.on('scroll', this.onViewScroll, this);
17747 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17748 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17751 this.view = new Roo.View(this.list, this.tpl, {
17756 selectedClass: this.selectedClass
17759 //this.view.wrapEl.setDisplayed(false);
17760 this.view.on('click', this.onViewClick, this);
17764 this.store.on('beforeload', this.onBeforeLoad, this);
17765 this.store.on('load', this.onLoad, this);
17766 this.store.on('loadexception', this.onLoadException, this);
17769 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17770 "up" : function(e){
17771 this.inKeyMode = true;
17775 "down" : function(e){
17776 this.inKeyMode = true;
17780 "enter" : function(e){
17781 if(this.fireEvent("specialkey", this, e)){
17782 this.onViewClick(false);
17788 "esc" : function(e){
17789 this.onTickableFooterButtonClick(e, false, false);
17792 "tab" : function(e){
17793 this.fireEvent("specialkey", this, e);
17795 this.onTickableFooterButtonClick(e, false, false);
17802 doRelay : function(e, fn, key){
17803 if(this.scope.isExpanded()){
17804 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17813 this.queryDelay = Math.max(this.queryDelay || 10,
17814 this.mode == 'local' ? 10 : 250);
17817 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17819 if(this.typeAhead){
17820 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17823 if(this.editable !== false){
17824 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17827 this.indicator = this.indicatorEl();
17829 if(this.indicator){
17830 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17831 this.indicator.hide();
17836 onDestroy : function(){
17838 this.view.setStore(null);
17839 this.view.el.removeAllListeners();
17840 this.view.el.remove();
17841 this.view.purgeListeners();
17844 this.list.dom.innerHTML = '';
17848 this.store.un('beforeload', this.onBeforeLoad, this);
17849 this.store.un('load', this.onLoad, this);
17850 this.store.un('loadexception', this.onLoadException, this);
17852 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17856 fireKey : function(e){
17857 if(e.isNavKeyPress() && !this.list.isVisible()){
17858 this.fireEvent("specialkey", this, e);
17863 onResize: function(w, h)
17867 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17869 // if(typeof w != 'number'){
17870 // // we do not handle it!?!?
17873 // var tw = this.trigger.getWidth();
17874 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17875 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17877 // this.inputEl().setWidth( this.adjustWidth('input', x));
17879 // //this.trigger.setStyle('left', x+'px');
17881 // if(this.list && this.listWidth === undefined){
17882 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17883 // this.list.setWidth(lw);
17884 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17892 * Allow or prevent the user from directly editing the field text. If false is passed,
17893 * the user will only be able to select from the items defined in the dropdown list. This method
17894 * is the runtime equivalent of setting the 'editable' config option at config time.
17895 * @param {Boolean} value True to allow the user to directly edit the field text
17897 setEditable : function(value){
17898 if(value == this.editable){
17901 this.editable = value;
17903 this.inputEl().dom.setAttribute('readOnly', true);
17904 this.inputEl().on('mousedown', this.onTriggerClick, this);
17905 this.inputEl().addClass('x-combo-noedit');
17907 this.inputEl().dom.removeAttribute('readOnly');
17908 this.inputEl().un('mousedown', this.onTriggerClick, this);
17909 this.inputEl().removeClass('x-combo-noedit');
17915 onBeforeLoad : function(combo,opts){
17916 if(!this.hasFocus){
17920 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17922 this.restrictHeight();
17923 this.selectedIndex = -1;
17927 onLoad : function(){
17929 this.hasQuery = false;
17931 if(!this.hasFocus){
17935 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17936 this.loading.hide();
17939 if(this.store.getCount() > 0){
17942 this.restrictHeight();
17943 if(this.lastQuery == this.allQuery){
17944 if(this.editable && !this.tickable){
17945 this.inputEl().dom.select();
17949 !this.selectByValue(this.value, true) &&
17952 !this.store.lastOptions ||
17953 typeof(this.store.lastOptions.add) == 'undefined' ||
17954 this.store.lastOptions.add != true
17957 this.select(0, true);
17960 if(this.autoFocus){
17963 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17964 this.taTask.delay(this.typeAheadDelay);
17968 this.onEmptyResults();
17974 onLoadException : function()
17976 this.hasQuery = false;
17978 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17979 this.loading.hide();
17982 if(this.tickable && this.editable){
17987 // only causes errors at present
17988 //Roo.log(this.store.reader.jsonData);
17989 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17991 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17997 onTypeAhead : function(){
17998 if(this.store.getCount() > 0){
17999 var r = this.store.getAt(0);
18000 var newValue = r.data[this.displayField];
18001 var len = newValue.length;
18002 var selStart = this.getRawValue().length;
18004 if(selStart != len){
18005 this.setRawValue(newValue);
18006 this.selectText(selStart, newValue.length);
18012 onSelect : function(record, index){
18014 if(this.fireEvent('beforeselect', this, record, index) !== false){
18016 this.setFromData(index > -1 ? record.data : false);
18019 this.fireEvent('select', this, record, index);
18024 * Returns the currently selected field value or empty string if no value is set.
18025 * @return {String} value The selected value
18027 getValue : function()
18029 if(Roo.isIOS && this.useNativeIOS){
18030 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18034 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18037 if(this.valueField){
18038 return typeof this.value != 'undefined' ? this.value : '';
18040 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18044 getRawValue : function()
18046 if(Roo.isIOS && this.useNativeIOS){
18047 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18050 var v = this.inputEl().getValue();
18056 * Clears any text/value currently set in the field
18058 clearValue : function(){
18060 if(this.hiddenField){
18061 this.hiddenField.dom.value = '';
18064 this.setRawValue('');
18065 this.lastSelectionText = '';
18066 this.lastData = false;
18068 var close = this.closeTriggerEl();
18079 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18080 * will be displayed in the field. If the value does not match the data value of an existing item,
18081 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18082 * Otherwise the field will be blank (although the value will still be set).
18083 * @param {String} value The value to match
18085 setValue : function(v)
18087 if(Roo.isIOS && this.useNativeIOS){
18088 this.setIOSValue(v);
18098 if(this.valueField){
18099 var r = this.findRecord(this.valueField, v);
18101 text = r.data[this.displayField];
18102 }else if(this.valueNotFoundText !== undefined){
18103 text = this.valueNotFoundText;
18106 this.lastSelectionText = text;
18107 if(this.hiddenField){
18108 this.hiddenField.dom.value = v;
18110 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18113 var close = this.closeTriggerEl();
18116 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18122 * @property {Object} the last set data for the element
18127 * Sets the value of the field based on a object which is related to the record format for the store.
18128 * @param {Object} value the value to set as. or false on reset?
18130 setFromData : function(o){
18137 var dv = ''; // display value
18138 var vv = ''; // value value..
18140 if (this.displayField) {
18141 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18143 // this is an error condition!!!
18144 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18147 if(this.valueField){
18148 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18151 var close = this.closeTriggerEl();
18154 if(dv.length || vv * 1 > 0){
18156 this.blockFocus=true;
18162 if(this.hiddenField){
18163 this.hiddenField.dom.value = vv;
18165 this.lastSelectionText = dv;
18166 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18170 // no hidden field.. - we store the value in 'value', but still display
18171 // display field!!!!
18172 this.lastSelectionText = dv;
18173 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18180 reset : function(){
18181 // overridden so that last data is reset..
18188 this.setValue(this.originalValue);
18189 //this.clearInvalid();
18190 this.lastData = false;
18192 this.view.clearSelections();
18198 findRecord : function(prop, value){
18200 if(this.store.getCount() > 0){
18201 this.store.each(function(r){
18202 if(r.data[prop] == value){
18212 getName: function()
18214 // returns hidden if it's set..
18215 if (!this.rendered) {return ''};
18216 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18220 onViewMove : function(e, t){
18221 this.inKeyMode = false;
18225 onViewOver : function(e, t){
18226 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18229 var item = this.view.findItemFromChild(t);
18232 var index = this.view.indexOf(item);
18233 this.select(index, false);
18238 onViewClick : function(view, doFocus, el, e)
18240 var index = this.view.getSelectedIndexes()[0];
18242 var r = this.store.getAt(index);
18246 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18253 Roo.each(this.tickItems, function(v,k){
18255 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18257 _this.tickItems.splice(k, 1);
18259 if(typeof(e) == 'undefined' && view == false){
18260 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18272 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18273 this.tickItems.push(r.data);
18276 if(typeof(e) == 'undefined' && view == false){
18277 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18284 this.onSelect(r, index);
18286 if(doFocus !== false && !this.blockFocus){
18287 this.inputEl().focus();
18292 restrictHeight : function(){
18293 //this.innerList.dom.style.height = '';
18294 //var inner = this.innerList.dom;
18295 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18296 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18297 //this.list.beginUpdate();
18298 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18299 this.list.alignTo(this.inputEl(), this.listAlign);
18300 this.list.alignTo(this.inputEl(), this.listAlign);
18301 //this.list.endUpdate();
18305 onEmptyResults : function(){
18307 if(this.tickable && this.editable){
18308 this.hasFocus = false;
18309 this.restrictHeight();
18317 * Returns true if the dropdown list is expanded, else false.
18319 isExpanded : function(){
18320 return this.list.isVisible();
18324 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18325 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18326 * @param {String} value The data value of the item to select
18327 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18328 * selected item if it is not currently in view (defaults to true)
18329 * @return {Boolean} True if the value matched an item in the list, else false
18331 selectByValue : function(v, scrollIntoView){
18332 if(v !== undefined && v !== null){
18333 var r = this.findRecord(this.valueField || this.displayField, v);
18335 this.select(this.store.indexOf(r), scrollIntoView);
18343 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18344 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18345 * @param {Number} index The zero-based index of the list item to select
18346 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18347 * selected item if it is not currently in view (defaults to true)
18349 select : function(index, scrollIntoView){
18350 this.selectedIndex = index;
18351 this.view.select(index);
18352 if(scrollIntoView !== false){
18353 var el = this.view.getNode(index);
18355 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18358 this.list.scrollChildIntoView(el, false);
18364 selectNext : function(){
18365 var ct = this.store.getCount();
18367 if(this.selectedIndex == -1){
18369 }else if(this.selectedIndex < ct-1){
18370 this.select(this.selectedIndex+1);
18376 selectPrev : function(){
18377 var ct = this.store.getCount();
18379 if(this.selectedIndex == -1){
18381 }else if(this.selectedIndex != 0){
18382 this.select(this.selectedIndex-1);
18388 onKeyUp : function(e){
18389 if(this.editable !== false && !e.isSpecialKey()){
18390 this.lastKey = e.getKey();
18391 this.dqTask.delay(this.queryDelay);
18396 validateBlur : function(){
18397 return !this.list || !this.list.isVisible();
18401 initQuery : function(){
18403 var v = this.getRawValue();
18405 if(this.tickable && this.editable){
18406 v = this.tickableInputEl().getValue();
18413 doForce : function(){
18414 if(this.inputEl().dom.value.length > 0){
18415 this.inputEl().dom.value =
18416 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18422 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18423 * query allowing the query action to be canceled if needed.
18424 * @param {String} query The SQL query to execute
18425 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18426 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18427 * saved in the current store (defaults to false)
18429 doQuery : function(q, forceAll){
18431 if(q === undefined || q === null){
18436 forceAll: forceAll,
18440 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18445 forceAll = qe.forceAll;
18446 if(forceAll === true || (q.length >= this.minChars)){
18448 this.hasQuery = true;
18450 if(this.lastQuery != q || this.alwaysQuery){
18451 this.lastQuery = q;
18452 if(this.mode == 'local'){
18453 this.selectedIndex = -1;
18455 this.store.clearFilter();
18458 if(this.specialFilter){
18459 this.fireEvent('specialfilter', this);
18464 this.store.filter(this.displayField, q);
18467 this.store.fireEvent("datachanged", this.store);
18474 this.store.baseParams[this.queryParam] = q;
18476 var options = {params : this.getParams(q)};
18479 options.add = true;
18480 options.params.start = this.page * this.pageSize;
18483 this.store.load(options);
18486 * this code will make the page width larger, at the beginning, the list not align correctly,
18487 * we should expand the list on onLoad
18488 * so command out it
18493 this.selectedIndex = -1;
18498 this.loadNext = false;
18502 getParams : function(q){
18504 //p[this.queryParam] = q;
18508 p.limit = this.pageSize;
18514 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18516 collapse : function(){
18517 if(!this.isExpanded()){
18523 this.hasFocus = false;
18527 this.cancelBtn.hide();
18528 this.trigger.show();
18531 this.tickableInputEl().dom.value = '';
18532 this.tickableInputEl().blur();
18537 Roo.get(document).un('mousedown', this.collapseIf, this);
18538 Roo.get(document).un('mousewheel', this.collapseIf, this);
18539 if (!this.editable) {
18540 Roo.get(document).un('keydown', this.listKeyPress, this);
18542 this.fireEvent('collapse', this);
18548 collapseIf : function(e){
18549 var in_combo = e.within(this.el);
18550 var in_list = e.within(this.list);
18551 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18553 if (in_combo || in_list || is_list) {
18554 //e.stopPropagation();
18559 this.onTickableFooterButtonClick(e, false, false);
18567 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18569 expand : function(){
18571 if(this.isExpanded() || !this.hasFocus){
18575 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18576 this.list.setWidth(lw);
18582 this.restrictHeight();
18586 this.tickItems = Roo.apply([], this.item);
18589 this.cancelBtn.show();
18590 this.trigger.hide();
18593 this.tickableInputEl().focus();
18598 Roo.get(document).on('mousedown', this.collapseIf, this);
18599 Roo.get(document).on('mousewheel', this.collapseIf, this);
18600 if (!this.editable) {
18601 Roo.get(document).on('keydown', this.listKeyPress, this);
18604 this.fireEvent('expand', this);
18608 // Implements the default empty TriggerField.onTriggerClick function
18609 onTriggerClick : function(e)
18611 Roo.log('trigger click');
18613 if(this.disabled || !this.triggerList){
18618 this.loadNext = false;
18620 if(this.isExpanded()){
18622 if (!this.blockFocus) {
18623 this.inputEl().focus();
18627 this.hasFocus = true;
18628 if(this.triggerAction == 'all') {
18629 this.doQuery(this.allQuery, true);
18631 this.doQuery(this.getRawValue());
18633 if (!this.blockFocus) {
18634 this.inputEl().focus();
18639 onTickableTriggerClick : function(e)
18646 this.loadNext = false;
18647 this.hasFocus = true;
18649 if(this.triggerAction == 'all') {
18650 this.doQuery(this.allQuery, true);
18652 this.doQuery(this.getRawValue());
18656 onSearchFieldClick : function(e)
18658 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18659 this.onTickableFooterButtonClick(e, false, false);
18663 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18668 this.loadNext = false;
18669 this.hasFocus = true;
18671 if(this.triggerAction == 'all') {
18672 this.doQuery(this.allQuery, true);
18674 this.doQuery(this.getRawValue());
18678 listKeyPress : function(e)
18680 //Roo.log('listkeypress');
18681 // scroll to first matching element based on key pres..
18682 if (e.isSpecialKey()) {
18685 var k = String.fromCharCode(e.getKey()).toUpperCase();
18688 var csel = this.view.getSelectedNodes();
18689 var cselitem = false;
18691 var ix = this.view.indexOf(csel[0]);
18692 cselitem = this.store.getAt(ix);
18693 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18699 this.store.each(function(v) {
18701 // start at existing selection.
18702 if (cselitem.id == v.id) {
18708 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18709 match = this.store.indexOf(v);
18715 if (match === false) {
18716 return true; // no more action?
18719 this.view.select(match);
18720 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18721 sn.scrollIntoView(sn.dom.parentNode, false);
18724 onViewScroll : function(e, t){
18726 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){
18730 this.hasQuery = true;
18732 this.loading = this.list.select('.loading', true).first();
18734 if(this.loading === null){
18735 this.list.createChild({
18737 cls: 'loading roo-select2-more-results roo-select2-active',
18738 html: 'Loading more results...'
18741 this.loading = this.list.select('.loading', true).first();
18743 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18745 this.loading.hide();
18748 this.loading.show();
18753 this.loadNext = true;
18755 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18760 addItem : function(o)
18762 var dv = ''; // display value
18764 if (this.displayField) {
18765 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18767 // this is an error condition!!!
18768 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18775 var choice = this.choices.createChild({
18777 cls: 'roo-select2-search-choice',
18786 cls: 'roo-select2-search-choice-close fa fa-times',
18791 }, this.searchField);
18793 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18795 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18803 this.inputEl().dom.value = '';
18808 onRemoveItem : function(e, _self, o)
18810 e.preventDefault();
18812 this.lastItem = Roo.apply([], this.item);
18814 var index = this.item.indexOf(o.data) * 1;
18817 Roo.log('not this item?!');
18821 this.item.splice(index, 1);
18826 this.fireEvent('remove', this, e);
18832 syncValue : function()
18834 if(!this.item.length){
18841 Roo.each(this.item, function(i){
18842 if(_this.valueField){
18843 value.push(i[_this.valueField]);
18850 this.value = value.join(',');
18852 if(this.hiddenField){
18853 this.hiddenField.dom.value = this.value;
18856 this.store.fireEvent("datachanged", this.store);
18861 clearItem : function()
18863 if(!this.multiple){
18869 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18877 if(this.tickable && !Roo.isTouch){
18878 this.view.refresh();
18882 inputEl: function ()
18884 if(Roo.isIOS && this.useNativeIOS){
18885 return this.el.select('select.roo-ios-select', true).first();
18888 if(Roo.isTouch && this.mobileTouchView){
18889 return this.el.select('input.form-control',true).first();
18893 return this.searchField;
18896 return this.el.select('input.form-control',true).first();
18899 onTickableFooterButtonClick : function(e, btn, el)
18901 e.preventDefault();
18903 this.lastItem = Roo.apply([], this.item);
18905 if(btn && btn.name == 'cancel'){
18906 this.tickItems = Roo.apply([], this.item);
18915 Roo.each(this.tickItems, function(o){
18923 validate : function()
18925 if(this.getVisibilityEl().hasClass('hidden')){
18929 var v = this.getRawValue();
18932 v = this.getValue();
18935 if(this.disabled || this.allowBlank || v.length){
18940 this.markInvalid();
18944 tickableInputEl : function()
18946 if(!this.tickable || !this.editable){
18947 return this.inputEl();
18950 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18954 getAutoCreateTouchView : function()
18959 cls: 'form-group' //input-group
18965 type : this.inputType,
18966 cls : 'form-control x-combo-noedit',
18967 autocomplete: 'new-password',
18968 placeholder : this.placeholder || '',
18973 input.name = this.name;
18977 input.cls += ' input-' + this.size;
18980 if (this.disabled) {
18981 input.disabled = true;
18985 cls : 'roo-combobox-wrap',
18992 inputblock.cls += ' input-group';
18994 inputblock.cn.unshift({
18996 cls : 'input-group-addon input-group-prepend input-group-text',
19001 if(this.removable && !this.multiple){
19002 inputblock.cls += ' roo-removable';
19004 inputblock.cn.push({
19007 cls : 'roo-combo-removable-btn close'
19011 if(this.hasFeedback && !this.allowBlank){
19013 inputblock.cls += ' has-feedback';
19015 inputblock.cn.push({
19017 cls: 'glyphicon form-control-feedback'
19024 inputblock.cls += (this.before) ? '' : ' input-group';
19026 inputblock.cn.push({
19028 cls : 'input-group-addon input-group-append input-group-text',
19034 var ibwrap = inputblock;
19039 cls: 'roo-select2-choices',
19043 cls: 'roo-select2-search-field',
19056 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19061 cls: 'form-hidden-field'
19067 if(!this.multiple && this.showToggleBtn){
19073 if (this.caret != false) {
19076 cls: 'fa fa-' + this.caret
19083 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19085 Roo.bootstrap.version == 3 ? caret : '',
19088 cls: 'combobox-clear',
19102 combobox.cls += ' roo-select2-container-multi';
19105 var required = this.allowBlank ? {
19107 style: 'display: none'
19110 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19111 tooltip : 'This field is required'
19114 var align = this.labelAlign || this.parentLabelAlign();
19116 if (align ==='left' && this.fieldLabel.length) {
19122 cls : 'control-label col-form-label',
19123 html : this.fieldLabel
19127 cls : 'roo-combobox-wrap ',
19134 var labelCfg = cfg.cn[1];
19135 var contentCfg = cfg.cn[2];
19138 if(this.indicatorpos == 'right'){
19143 cls : 'control-label col-form-label',
19147 html : this.fieldLabel
19153 cls : "roo-combobox-wrap ",
19161 labelCfg = cfg.cn[0];
19162 contentCfg = cfg.cn[1];
19167 if(this.labelWidth > 12){
19168 labelCfg.style = "width: " + this.labelWidth + 'px';
19171 if(this.labelWidth < 13 && this.labelmd == 0){
19172 this.labelmd = this.labelWidth;
19175 if(this.labellg > 0){
19176 labelCfg.cls += ' col-lg-' + this.labellg;
19177 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19180 if(this.labelmd > 0){
19181 labelCfg.cls += ' col-md-' + this.labelmd;
19182 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19185 if(this.labelsm > 0){
19186 labelCfg.cls += ' col-sm-' + this.labelsm;
19187 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19190 if(this.labelxs > 0){
19191 labelCfg.cls += ' col-xs-' + this.labelxs;
19192 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19196 } else if ( this.fieldLabel.length) {
19201 cls : 'control-label',
19202 html : this.fieldLabel
19213 if(this.indicatorpos == 'right'){
19217 cls : 'control-label',
19218 html : this.fieldLabel,
19236 var settings = this;
19238 ['xs','sm','md','lg'].map(function(size){
19239 if (settings[size]) {
19240 cfg.cls += ' col-' + size + '-' + settings[size];
19247 initTouchView : function()
19249 this.renderTouchView();
19251 this.touchViewEl.on('scroll', function(){
19252 this.el.dom.scrollTop = 0;
19255 this.originalValue = this.getValue();
19257 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19259 this.inputEl().on("click", this.showTouchView, this);
19260 if (this.triggerEl) {
19261 this.triggerEl.on("click", this.showTouchView, this);
19265 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19266 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19268 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19270 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19271 this.store.on('load', this.onTouchViewLoad, this);
19272 this.store.on('loadexception', this.onTouchViewLoadException, this);
19274 if(this.hiddenName){
19276 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19278 this.hiddenField.dom.value =
19279 this.hiddenValue !== undefined ? this.hiddenValue :
19280 this.value !== undefined ? this.value : '';
19282 this.el.dom.removeAttribute('name');
19283 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19287 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19288 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19291 if(this.removable && !this.multiple){
19292 var close = this.closeTriggerEl();
19294 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19295 close.on('click', this.removeBtnClick, this, close);
19299 * fix the bug in Safari iOS8
19301 this.inputEl().on("focus", function(e){
19302 document.activeElement.blur();
19305 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19312 renderTouchView : function()
19314 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19315 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19317 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19318 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19320 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19321 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19322 this.touchViewBodyEl.setStyle('overflow', 'auto');
19324 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19325 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19328 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332 showTouchView : function()
19338 this.touchViewHeaderEl.hide();
19340 if(this.modalTitle.length){
19341 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19342 this.touchViewHeaderEl.show();
19345 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19346 this.touchViewEl.show();
19348 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19350 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19351 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19353 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19355 if(this.modalTitle.length){
19356 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19359 this.touchViewBodyEl.setHeight(bodyHeight);
19363 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19365 this.touchViewEl.addClass(['in','show']);
19368 if(this._touchViewMask){
19369 Roo.get(document.body).addClass("x-body-masked");
19370 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19371 this._touchViewMask.setStyle('z-index', 10000);
19372 this._touchViewMask.addClass('show');
19375 this.doTouchViewQuery();
19379 hideTouchView : function()
19381 this.touchViewEl.removeClass(['in','show']);
19385 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19387 this.touchViewEl.setStyle('display', 'none');
19390 if(this._touchViewMask){
19391 this._touchViewMask.removeClass('show');
19392 Roo.get(document.body).removeClass("x-body-masked");
19396 setTouchViewValue : function()
19403 Roo.each(this.tickItems, function(o){
19408 this.hideTouchView();
19411 doTouchViewQuery : function()
19420 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19424 if(!this.alwaysQuery || this.mode == 'local'){
19425 this.onTouchViewLoad();
19432 onTouchViewBeforeLoad : function(combo,opts)
19438 onTouchViewLoad : function()
19440 if(this.store.getCount() < 1){
19441 this.onTouchViewEmptyResults();
19445 this.clearTouchView();
19447 var rawValue = this.getRawValue();
19449 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19451 this.tickItems = [];
19453 this.store.data.each(function(d, rowIndex){
19454 var row = this.touchViewListGroup.createChild(template);
19456 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19457 row.addClass(d.data.cls);
19460 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19463 html : d.data[this.displayField]
19466 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19467 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19470 row.removeClass('selected');
19471 if(!this.multiple && this.valueField &&
19472 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19476 row.addClass('selected');
19479 if(this.multiple && this.valueField &&
19480 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19484 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19485 this.tickItems.push(d.data);
19488 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19492 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19494 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19496 if(this.modalTitle.length){
19497 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19500 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19502 if(this.mobile_restrict_height && listHeight < bodyHeight){
19503 this.touchViewBodyEl.setHeight(listHeight);
19508 if(firstChecked && listHeight > bodyHeight){
19509 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19514 onTouchViewLoadException : function()
19516 this.hideTouchView();
19519 onTouchViewEmptyResults : function()
19521 this.clearTouchView();
19523 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19525 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19529 clearTouchView : function()
19531 this.touchViewListGroup.dom.innerHTML = '';
19534 onTouchViewClick : function(e, el, o)
19536 e.preventDefault();
19539 var rowIndex = o.rowIndex;
19541 var r = this.store.getAt(rowIndex);
19543 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19545 if(!this.multiple){
19546 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19547 c.dom.removeAttribute('checked');
19550 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19552 this.setFromData(r.data);
19554 var close = this.closeTriggerEl();
19560 this.hideTouchView();
19562 this.fireEvent('select', this, r, rowIndex);
19567 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19568 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19569 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19573 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19574 this.addItem(r.data);
19575 this.tickItems.push(r.data);
19579 getAutoCreateNativeIOS : function()
19582 cls: 'form-group' //input-group,
19587 cls : 'roo-ios-select'
19591 combobox.name = this.name;
19594 if (this.disabled) {
19595 combobox.disabled = true;
19598 var settings = this;
19600 ['xs','sm','md','lg'].map(function(size){
19601 if (settings[size]) {
19602 cfg.cls += ' col-' + size + '-' + settings[size];
19612 initIOSView : function()
19614 this.store.on('load', this.onIOSViewLoad, this);
19619 onIOSViewLoad : function()
19621 if(this.store.getCount() < 1){
19625 this.clearIOSView();
19627 if(this.allowBlank) {
19629 var default_text = '-- SELECT --';
19631 if(this.placeholder.length){
19632 default_text = this.placeholder;
19635 if(this.emptyTitle.length){
19636 default_text += ' - ' + this.emptyTitle + ' -';
19639 var opt = this.inputEl().createChild({
19642 html : default_text
19646 o[this.valueField] = 0;
19647 o[this.displayField] = default_text;
19649 this.ios_options.push({
19656 this.store.data.each(function(d, rowIndex){
19660 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19661 html = d.data[this.displayField];
19666 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19667 value = d.data[this.valueField];
19676 if(this.value == d.data[this.valueField]){
19677 option['selected'] = true;
19680 var opt = this.inputEl().createChild(option);
19682 this.ios_options.push({
19689 this.inputEl().on('change', function(){
19690 this.fireEvent('select', this);
19695 clearIOSView: function()
19697 this.inputEl().dom.innerHTML = '';
19699 this.ios_options = [];
19702 setIOSValue: function(v)
19706 if(!this.ios_options){
19710 Roo.each(this.ios_options, function(opts){
19712 opts.el.dom.removeAttribute('selected');
19714 if(opts.data[this.valueField] != v){
19718 opts.el.dom.setAttribute('selected', true);
19724 * @cfg {Boolean} grow
19728 * @cfg {Number} growMin
19732 * @cfg {Number} growMax
19741 Roo.apply(Roo.bootstrap.form.ComboBox, {
19745 cls: 'modal-header',
19767 cls: 'list-group-item',
19771 cls: 'roo-combobox-list-group-item-value'
19775 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19789 listItemCheckbox : {
19791 cls: 'list-group-item',
19795 cls: 'roo-combobox-list-group-item-value'
19799 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19815 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19820 cls: 'modal-footer',
19828 cls: 'col-xs-6 text-left',
19831 cls: 'btn btn-danger roo-touch-view-cancel',
19837 cls: 'col-xs-6 text-right',
19840 cls: 'btn btn-success roo-touch-view-ok',
19851 Roo.apply(Roo.bootstrap.form.ComboBox, {
19853 touchViewTemplate : {
19855 cls: 'modal fade roo-combobox-touch-view',
19859 cls: 'modal-dialog',
19860 style : 'position:fixed', // we have to fix position....
19864 cls: 'modal-content',
19866 Roo.bootstrap.form.ComboBox.header,
19867 Roo.bootstrap.form.ComboBox.body,
19868 Roo.bootstrap.form.ComboBox.footer
19877 * Ext JS Library 1.1.1
19878 * Copyright(c) 2006-2007, Ext JS, LLC.
19880 * Originally Released Under LGPL - original licence link has changed is not relivant.
19883 * <script type="text/javascript">
19888 * @extends Roo.util.Observable
19889 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19890 * This class also supports single and multi selection modes. <br>
19891 * Create a data model bound view:
19893 var store = new Roo.data.Store(...);
19895 var view = new Roo.View({
19897 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19899 singleSelect: true,
19900 selectedClass: "ydataview-selected",
19904 // listen for node click?
19905 view.on("click", function(vw, index, node, e){
19906 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19910 dataModel.load("foobar.xml");
19912 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19914 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19915 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19917 * Note: old style constructor is still suported (container, template, config)
19920 * Create a new View
19921 * @param {Object} config The config object
19924 Roo.View = function(config, depreciated_tpl, depreciated_config){
19926 this.parent = false;
19928 if (typeof(depreciated_tpl) == 'undefined') {
19929 // new way.. - universal constructor.
19930 Roo.apply(this, config);
19931 this.el = Roo.get(this.el);
19934 this.el = Roo.get(config);
19935 this.tpl = depreciated_tpl;
19936 Roo.apply(this, depreciated_config);
19938 this.wrapEl = this.el.wrap().wrap();
19939 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19942 if(typeof(this.tpl) == "string"){
19943 this.tpl = new Roo.Template(this.tpl);
19945 // support xtype ctors..
19946 this.tpl = new Roo.factory(this.tpl, Roo);
19950 this.tpl.compile();
19955 * @event beforeclick
19956 * Fires before a click is processed. Returns false to cancel the default action.
19957 * @param {Roo.View} this
19958 * @param {Number} index The index of the target node
19959 * @param {HTMLElement} node The target node
19960 * @param {Roo.EventObject} e The raw event object
19962 "beforeclick" : true,
19965 * Fires when a template node is clicked.
19966 * @param {Roo.View} this
19967 * @param {Number} index The index of the target node
19968 * @param {HTMLElement} node The target node
19969 * @param {Roo.EventObject} e The raw event object
19974 * Fires when a template node is double clicked.
19975 * @param {Roo.View} this
19976 * @param {Number} index The index of the target node
19977 * @param {HTMLElement} node The target node
19978 * @param {Roo.EventObject} e The raw event object
19982 * @event contextmenu
19983 * Fires when a template node is right clicked.
19984 * @param {Roo.View} this
19985 * @param {Number} index The index of the target node
19986 * @param {HTMLElement} node The target node
19987 * @param {Roo.EventObject} e The raw event object
19989 "contextmenu" : true,
19991 * @event selectionchange
19992 * Fires when the selected nodes change.
19993 * @param {Roo.View} this
19994 * @param {Array} selections Array of the selected nodes
19996 "selectionchange" : true,
19999 * @event beforeselect
20000 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20001 * @param {Roo.View} this
20002 * @param {HTMLElement} node The node to be selected
20003 * @param {Array} selections Array of currently selected nodes
20005 "beforeselect" : true,
20007 * @event preparedata
20008 * Fires on every row to render, to allow you to change the data.
20009 * @param {Roo.View} this
20010 * @param {Object} data to be rendered (change this)
20012 "preparedata" : true
20020 "click": this.onClick,
20021 "dblclick": this.onDblClick,
20022 "contextmenu": this.onContextMenu,
20026 this.selections = [];
20028 this.cmp = new Roo.CompositeElementLite([]);
20030 this.store = Roo.factory(this.store, Roo.data);
20031 this.setStore(this.store, true);
20034 if ( this.footer && this.footer.xtype) {
20036 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20038 this.footer.dataSource = this.store;
20039 this.footer.container = fctr;
20040 this.footer = Roo.factory(this.footer, Roo);
20041 fctr.insertFirst(this.el);
20043 // this is a bit insane - as the paging toolbar seems to detach the el..
20044 // dom.parentNode.parentNode.parentNode
20045 // they get detached?
20049 Roo.View.superclass.constructor.call(this);
20054 Roo.extend(Roo.View, Roo.util.Observable, {
20057 * @cfg {Roo.data.Store} store Data store to load data from.
20062 * @cfg {String|Roo.Element} el The container element.
20067 * @cfg {String|Roo.Template} tpl The template used by this View
20071 * @cfg {String} dataName the named area of the template to use as the data area
20072 * Works with domtemplates roo-name="name"
20076 * @cfg {String} selectedClass The css class to add to selected nodes
20078 selectedClass : "x-view-selected",
20080 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20085 * @cfg {String} text to display on mask (default Loading)
20089 * @cfg {Boolean} multiSelect Allow multiple selection
20091 multiSelect : false,
20093 * @cfg {Boolean} singleSelect Allow single selection
20095 singleSelect: false,
20098 * @cfg {Boolean} toggleSelect - selecting
20100 toggleSelect : false,
20103 * @cfg {Boolean} tickable - selecting
20108 * Returns the element this view is bound to.
20109 * @return {Roo.Element}
20111 getEl : function(){
20112 return this.wrapEl;
20118 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20120 refresh : function(){
20121 //Roo.log('refresh');
20124 // if we are using something like 'domtemplate', then
20125 // the what gets used is:
20126 // t.applySubtemplate(NAME, data, wrapping data..)
20127 // the outer template then get' applied with
20128 // the store 'extra data'
20129 // and the body get's added to the
20130 // roo-name="data" node?
20131 // <span class='roo-tpl-{name}'></span> ?????
20135 this.clearSelections();
20136 this.el.update("");
20138 var records = this.store.getRange();
20139 if(records.length < 1) {
20141 // is this valid?? = should it render a template??
20143 this.el.update(this.emptyText);
20147 if (this.dataName) {
20148 this.el.update(t.apply(this.store.meta)); //????
20149 el = this.el.child('.roo-tpl-' + this.dataName);
20152 for(var i = 0, len = records.length; i < len; i++){
20153 var data = this.prepareData(records[i].data, i, records[i]);
20154 this.fireEvent("preparedata", this, data, i, records[i]);
20156 var d = Roo.apply({}, data);
20159 Roo.apply(d, {'roo-id' : Roo.id()});
20163 Roo.each(this.parent.item, function(item){
20164 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20167 Roo.apply(d, {'roo-data-checked' : 'checked'});
20171 html[html.length] = Roo.util.Format.trim(
20173 t.applySubtemplate(this.dataName, d, this.store.meta) :
20180 el.update(html.join(""));
20181 this.nodes = el.dom.childNodes;
20182 this.updateIndexes(0);
20187 * Function to override to reformat the data that is sent to
20188 * the template for each node.
20189 * DEPRICATED - use the preparedata event handler.
20190 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20191 * a JSON object for an UpdateManager bound view).
20193 prepareData : function(data, index, record)
20195 this.fireEvent("preparedata", this, data, index, record);
20199 onUpdate : function(ds, record){
20200 // Roo.log('on update');
20201 this.clearSelections();
20202 var index = this.store.indexOf(record);
20203 var n = this.nodes[index];
20204 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20205 n.parentNode.removeChild(n);
20206 this.updateIndexes(index, index);
20212 onAdd : function(ds, records, index)
20214 //Roo.log(['on Add', ds, records, index] );
20215 this.clearSelections();
20216 if(this.nodes.length == 0){
20220 var n = this.nodes[index];
20221 for(var i = 0, len = records.length; i < len; i++){
20222 var d = this.prepareData(records[i].data, i, records[i]);
20224 this.tpl.insertBefore(n, d);
20227 this.tpl.append(this.el, d);
20230 this.updateIndexes(index);
20233 onRemove : function(ds, record, index){
20234 // Roo.log('onRemove');
20235 this.clearSelections();
20236 var el = this.dataName ?
20237 this.el.child('.roo-tpl-' + this.dataName) :
20240 el.dom.removeChild(this.nodes[index]);
20241 this.updateIndexes(index);
20245 * Refresh an individual node.
20246 * @param {Number} index
20248 refreshNode : function(index){
20249 this.onUpdate(this.store, this.store.getAt(index));
20252 updateIndexes : function(startIndex, endIndex){
20253 var ns = this.nodes;
20254 startIndex = startIndex || 0;
20255 endIndex = endIndex || ns.length - 1;
20256 for(var i = startIndex; i <= endIndex; i++){
20257 ns[i].nodeIndex = i;
20262 * Changes the data store this view uses and refresh the view.
20263 * @param {Store} store
20265 setStore : function(store, initial){
20266 if(!initial && this.store){
20267 this.store.un("datachanged", this.refresh);
20268 this.store.un("add", this.onAdd);
20269 this.store.un("remove", this.onRemove);
20270 this.store.un("update", this.onUpdate);
20271 this.store.un("clear", this.refresh);
20272 this.store.un("beforeload", this.onBeforeLoad);
20273 this.store.un("load", this.onLoad);
20274 this.store.un("loadexception", this.onLoad);
20278 store.on("datachanged", this.refresh, this);
20279 store.on("add", this.onAdd, this);
20280 store.on("remove", this.onRemove, this);
20281 store.on("update", this.onUpdate, this);
20282 store.on("clear", this.refresh, this);
20283 store.on("beforeload", this.onBeforeLoad, this);
20284 store.on("load", this.onLoad, this);
20285 store.on("loadexception", this.onLoad, this);
20293 * onbeforeLoad - masks the loading area.
20296 onBeforeLoad : function(store,opts)
20298 //Roo.log('onBeforeLoad');
20300 this.el.update("");
20302 this.el.mask(this.mask ? this.mask : "Loading" );
20304 onLoad : function ()
20311 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20312 * @param {HTMLElement} node
20313 * @return {HTMLElement} The template node
20315 findItemFromChild : function(node){
20316 var el = this.dataName ?
20317 this.el.child('.roo-tpl-' + this.dataName,true) :
20320 if(!node || node.parentNode == el){
20323 var p = node.parentNode;
20324 while(p && p != el){
20325 if(p.parentNode == el){
20334 onClick : function(e){
20335 var item = this.findItemFromChild(e.getTarget());
20337 var index = this.indexOf(item);
20338 if(this.onItemClick(item, index, e) !== false){
20339 this.fireEvent("click", this, index, item, e);
20342 this.clearSelections();
20347 onContextMenu : function(e){
20348 var item = this.findItemFromChild(e.getTarget());
20350 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20355 onDblClick : function(e){
20356 var item = this.findItemFromChild(e.getTarget());
20358 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20362 onItemClick : function(item, index, e)
20364 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20367 if (this.toggleSelect) {
20368 var m = this.isSelected(item) ? 'unselect' : 'select';
20371 _t[m](item, true, false);
20374 if(this.multiSelect || this.singleSelect){
20375 if(this.multiSelect && e.shiftKey && this.lastSelection){
20376 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20378 this.select(item, this.multiSelect && e.ctrlKey);
20379 this.lastSelection = item;
20382 if(!this.tickable){
20383 e.preventDefault();
20391 * Get the number of selected nodes.
20394 getSelectionCount : function(){
20395 return this.selections.length;
20399 * Get the currently selected nodes.
20400 * @return {Array} An array of HTMLElements
20402 getSelectedNodes : function(){
20403 return this.selections;
20407 * Get the indexes of the selected nodes.
20410 getSelectedIndexes : function(){
20411 var indexes = [], s = this.selections;
20412 for(var i = 0, len = s.length; i < len; i++){
20413 indexes.push(s[i].nodeIndex);
20419 * Clear all selections
20420 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20422 clearSelections : function(suppressEvent){
20423 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20424 this.cmp.elements = this.selections;
20425 this.cmp.removeClass(this.selectedClass);
20426 this.selections = [];
20427 if(!suppressEvent){
20428 this.fireEvent("selectionchange", this, this.selections);
20434 * Returns true if the passed node is selected
20435 * @param {HTMLElement/Number} node The node or node index
20436 * @return {Boolean}
20438 isSelected : function(node){
20439 var s = this.selections;
20443 node = this.getNode(node);
20444 return s.indexOf(node) !== -1;
20449 * @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
20450 * @param {Boolean} keepExisting (optional) true to keep existing selections
20451 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20453 select : function(nodeInfo, keepExisting, suppressEvent){
20454 if(nodeInfo instanceof Array){
20456 this.clearSelections(true);
20458 for(var i = 0, len = nodeInfo.length; i < len; i++){
20459 this.select(nodeInfo[i], true, true);
20463 var node = this.getNode(nodeInfo);
20464 if(!node || this.isSelected(node)){
20465 return; // already selected.
20468 this.clearSelections(true);
20471 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20472 Roo.fly(node).addClass(this.selectedClass);
20473 this.selections.push(node);
20474 if(!suppressEvent){
20475 this.fireEvent("selectionchange", this, this.selections);
20483 * @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
20484 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20485 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20487 unselect : function(nodeInfo, keepExisting, suppressEvent)
20489 if(nodeInfo instanceof Array){
20490 Roo.each(this.selections, function(s) {
20491 this.unselect(s, nodeInfo);
20495 var node = this.getNode(nodeInfo);
20496 if(!node || !this.isSelected(node)){
20497 //Roo.log("not selected");
20498 return; // not selected.
20502 Roo.each(this.selections, function(s) {
20504 Roo.fly(node).removeClass(this.selectedClass);
20511 this.selections= ns;
20512 this.fireEvent("selectionchange", this, this.selections);
20516 * Gets a template node.
20517 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20518 * @return {HTMLElement} The node or null if it wasn't found
20520 getNode : function(nodeInfo){
20521 if(typeof nodeInfo == "string"){
20522 return document.getElementById(nodeInfo);
20523 }else if(typeof nodeInfo == "number"){
20524 return this.nodes[nodeInfo];
20530 * Gets a range template nodes.
20531 * @param {Number} startIndex
20532 * @param {Number} endIndex
20533 * @return {Array} An array of nodes
20535 getNodes : function(start, end){
20536 var ns = this.nodes;
20537 start = start || 0;
20538 end = typeof end == "undefined" ? ns.length - 1 : end;
20541 for(var i = start; i <= end; i++){
20545 for(var i = start; i >= end; i--){
20553 * Finds the index of the passed node
20554 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20555 * @return {Number} The index of the node or -1
20557 indexOf : function(node){
20558 node = this.getNode(node);
20559 if(typeof node.nodeIndex == "number"){
20560 return node.nodeIndex;
20562 var ns = this.nodes;
20563 for(var i = 0, len = ns.length; i < len; i++){
20574 * based on jquery fullcalendar
20578 Roo.bootstrap = Roo.bootstrap || {};
20580 * @class Roo.bootstrap.Calendar
20581 * @extends Roo.bootstrap.Component
20582 * Bootstrap Calendar class
20583 * @cfg {Boolean} loadMask (true|false) default false
20584 * @cfg {Object} header generate the user specific header of the calendar, default false
20587 * Create a new Container
20588 * @param {Object} config The config object
20593 Roo.bootstrap.Calendar = function(config){
20594 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20598 * Fires when a date is selected
20599 * @param {DatePicker} this
20600 * @param {Date} date The selected date
20604 * @event monthchange
20605 * Fires when the displayed month changes
20606 * @param {DatePicker} this
20607 * @param {Date} date The selected month
20609 'monthchange': true,
20611 * @event evententer
20612 * Fires when mouse over an event
20613 * @param {Calendar} this
20614 * @param {event} Event
20616 'evententer': true,
20618 * @event eventleave
20619 * Fires when the mouse leaves an
20620 * @param {Calendar} this
20623 'eventleave': true,
20625 * @event eventclick
20626 * Fires when the mouse click an
20627 * @param {Calendar} this
20636 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20639 * @cfg {Roo.data.Store} store
20640 * The data source for the calendar
20644 * @cfg {Number} startDay
20645 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20653 getAutoCreate : function(){
20656 var fc_button = function(name, corner, style, content ) {
20657 return Roo.apply({},{
20659 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20661 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20664 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20675 style : 'width:100%',
20682 cls : 'fc-header-left',
20684 fc_button('prev', 'left', 'arrow', '‹' ),
20685 fc_button('next', 'right', 'arrow', '›' ),
20686 { tag: 'span', cls: 'fc-header-space' },
20687 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20695 cls : 'fc-header-center',
20699 cls: 'fc-header-title',
20702 html : 'month / year'
20710 cls : 'fc-header-right',
20712 /* fc_button('month', 'left', '', 'month' ),
20713 fc_button('week', '', '', 'week' ),
20714 fc_button('day', 'right', '', 'day' )
20726 header = this.header;
20729 var cal_heads = function() {
20731 // fixme - handle this.
20733 for (var i =0; i < Date.dayNames.length; i++) {
20734 var d = Date.dayNames[i];
20737 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20738 html : d.substring(0,3)
20742 ret[0].cls += ' fc-first';
20743 ret[6].cls += ' fc-last';
20746 var cal_cell = function(n) {
20749 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20754 cls: 'fc-day-number',
20758 cls: 'fc-day-content',
20762 style: 'position: relative;' // height: 17px;
20774 var cal_rows = function() {
20777 for (var r = 0; r < 6; r++) {
20784 for (var i =0; i < Date.dayNames.length; i++) {
20785 var d = Date.dayNames[i];
20786 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20789 row.cn[0].cls+=' fc-first';
20790 row.cn[0].cn[0].style = 'min-height:90px';
20791 row.cn[6].cls+=' fc-last';
20795 ret[0].cls += ' fc-first';
20796 ret[4].cls += ' fc-prev-last';
20797 ret[5].cls += ' fc-last';
20804 cls: 'fc-border-separate',
20805 style : 'width:100%',
20813 cls : 'fc-first fc-last',
20831 cls : 'fc-content',
20832 style : "position: relative;",
20835 cls : 'fc-view fc-view-month fc-grid',
20836 style : 'position: relative',
20837 unselectable : 'on',
20840 cls : 'fc-event-container',
20841 style : 'position:absolute;z-index:8;top:0;left:0;'
20859 initEvents : function()
20862 throw "can not find store for calendar";
20868 style: "text-align:center",
20872 style: "background-color:white;width:50%;margin:250 auto",
20876 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20887 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20889 var size = this.el.select('.fc-content', true).first().getSize();
20890 this.maskEl.setSize(size.width, size.height);
20891 this.maskEl.enableDisplayMode("block");
20892 if(!this.loadMask){
20893 this.maskEl.hide();
20896 this.store = Roo.factory(this.store, Roo.data);
20897 this.store.on('load', this.onLoad, this);
20898 this.store.on('beforeload', this.onBeforeLoad, this);
20902 this.cells = this.el.select('.fc-day',true);
20903 //Roo.log(this.cells);
20904 this.textNodes = this.el.query('.fc-day-number');
20905 this.cells.addClassOnOver('fc-state-hover');
20907 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20908 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20909 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20910 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20912 this.on('monthchange', this.onMonthChange, this);
20914 this.update(new Date().clearTime());
20917 resize : function() {
20918 var sz = this.el.getSize();
20920 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20921 this.el.select('.fc-day-content div',true).setHeight(34);
20926 showPrevMonth : function(e){
20927 this.update(this.activeDate.add("mo", -1));
20929 showToday : function(e){
20930 this.update(new Date().clearTime());
20933 showNextMonth : function(e){
20934 this.update(this.activeDate.add("mo", 1));
20938 showPrevYear : function(){
20939 this.update(this.activeDate.add("y", -1));
20943 showNextYear : function(){
20944 this.update(this.activeDate.add("y", 1));
20949 update : function(date)
20951 var vd = this.activeDate;
20952 this.activeDate = date;
20953 // if(vd && this.el){
20954 // var t = date.getTime();
20955 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20956 // Roo.log('using add remove');
20958 // this.fireEvent('monthchange', this, date);
20960 // this.cells.removeClass("fc-state-highlight");
20961 // this.cells.each(function(c){
20962 // if(c.dateValue == t){
20963 // c.addClass("fc-state-highlight");
20964 // setTimeout(function(){
20965 // try{c.dom.firstChild.focus();}catch(e){}
20975 var days = date.getDaysInMonth();
20977 var firstOfMonth = date.getFirstDateOfMonth();
20978 var startingPos = firstOfMonth.getDay()-this.startDay;
20980 if(startingPos < this.startDay){
20984 var pm = date.add(Date.MONTH, -1);
20985 var prevStart = pm.getDaysInMonth()-startingPos;
20987 this.cells = this.el.select('.fc-day',true);
20988 this.textNodes = this.el.query('.fc-day-number');
20989 this.cells.addClassOnOver('fc-state-hover');
20991 var cells = this.cells.elements;
20992 var textEls = this.textNodes;
20994 Roo.each(cells, function(cell){
20995 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20998 days += startingPos;
21000 // convert everything to numbers so it's fast
21001 var day = 86400000;
21002 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21005 //Roo.log(prevStart);
21007 var today = new Date().clearTime().getTime();
21008 var sel = date.clearTime().getTime();
21009 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21010 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21011 var ddMatch = this.disabledDatesRE;
21012 var ddText = this.disabledDatesText;
21013 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21014 var ddaysText = this.disabledDaysText;
21015 var format = this.format;
21017 var setCellClass = function(cal, cell){
21021 //Roo.log('set Cell Class');
21023 var t = d.getTime();
21027 cell.dateValue = t;
21029 cell.className += " fc-today";
21030 cell.className += " fc-state-highlight";
21031 cell.title = cal.todayText;
21034 // disable highlight in other month..
21035 //cell.className += " fc-state-highlight";
21040 cell.className = " fc-state-disabled";
21041 cell.title = cal.minText;
21045 cell.className = " fc-state-disabled";
21046 cell.title = cal.maxText;
21050 if(ddays.indexOf(d.getDay()) != -1){
21051 cell.title = ddaysText;
21052 cell.className = " fc-state-disabled";
21055 if(ddMatch && format){
21056 var fvalue = d.dateFormat(format);
21057 if(ddMatch.test(fvalue)){
21058 cell.title = ddText.replace("%0", fvalue);
21059 cell.className = " fc-state-disabled";
21063 if (!cell.initialClassName) {
21064 cell.initialClassName = cell.dom.className;
21067 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21072 for(; i < startingPos; i++) {
21073 textEls[i].innerHTML = (++prevStart);
21074 d.setDate(d.getDate()+1);
21076 cells[i].className = "fc-past fc-other-month";
21077 setCellClass(this, cells[i]);
21082 for(; i < days; i++){
21083 intDay = i - startingPos + 1;
21084 textEls[i].innerHTML = (intDay);
21085 d.setDate(d.getDate()+1);
21087 cells[i].className = ''; // "x-date-active";
21088 setCellClass(this, cells[i]);
21092 for(; i < 42; i++) {
21093 textEls[i].innerHTML = (++extraDays);
21094 d.setDate(d.getDate()+1);
21096 cells[i].className = "fc-future fc-other-month";
21097 setCellClass(this, cells[i]);
21100 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21102 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21104 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21105 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21107 if(totalRows != 6){
21108 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21109 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21112 this.fireEvent('monthchange', this, date);
21116 if(!this.internalRender){
21117 var main = this.el.dom.firstChild;
21118 var w = main.offsetWidth;
21119 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21120 Roo.fly(main).setWidth(w);
21121 this.internalRender = true;
21122 // opera does not respect the auto grow header center column
21123 // then, after it gets a width opera refuses to recalculate
21124 // without a second pass
21125 if(Roo.isOpera && !this.secondPass){
21126 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21127 this.secondPass = true;
21128 this.update.defer(10, this, [date]);
21135 findCell : function(dt) {
21136 dt = dt.clearTime().getTime();
21138 this.cells.each(function(c){
21139 //Roo.log("check " +c.dateValue + '?=' + dt);
21140 if(c.dateValue == dt){
21150 findCells : function(ev) {
21151 var s = ev.start.clone().clearTime().getTime();
21153 var e= ev.end.clone().clearTime().getTime();
21156 this.cells.each(function(c){
21157 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21159 if(c.dateValue > e){
21162 if(c.dateValue < s){
21171 // findBestRow: function(cells)
21175 // for (var i =0 ; i < cells.length;i++) {
21176 // ret = Math.max(cells[i].rows || 0,ret);
21183 addItem : function(ev)
21185 // look for vertical location slot in
21186 var cells = this.findCells(ev);
21188 // ev.row = this.findBestRow(cells);
21190 // work out the location.
21194 for(var i =0; i < cells.length; i++) {
21196 cells[i].row = cells[0].row;
21199 cells[i].row = cells[i].row + 1;
21209 if (crow.start.getY() == cells[i].getY()) {
21211 crow.end = cells[i];
21228 cells[0].events.push(ev);
21230 this.calevents.push(ev);
21233 clearEvents: function() {
21235 if(!this.calevents){
21239 Roo.each(this.cells.elements, function(c){
21245 Roo.each(this.calevents, function(e) {
21246 Roo.each(e.els, function(el) {
21247 el.un('mouseenter' ,this.onEventEnter, this);
21248 el.un('mouseleave' ,this.onEventLeave, this);
21253 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21259 renderEvents: function()
21263 this.cells.each(function(c) {
21272 if(c.row != c.events.length){
21273 r = 4 - (4 - (c.row - c.events.length));
21276 c.events = ev.slice(0, r);
21277 c.more = ev.slice(r);
21279 if(c.more.length && c.more.length == 1){
21280 c.events.push(c.more.pop());
21283 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21287 this.cells.each(function(c) {
21289 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21292 for (var e = 0; e < c.events.length; e++){
21293 var ev = c.events[e];
21294 var rows = ev.rows;
21296 for(var i = 0; i < rows.length; i++) {
21298 // how many rows should it span..
21301 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21302 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21304 unselectable : "on",
21307 cls: 'fc-event-inner',
21311 // cls: 'fc-event-time',
21312 // html : cells.length > 1 ? '' : ev.time
21316 cls: 'fc-event-title',
21317 html : String.format('{0}', ev.title)
21324 cls: 'ui-resizable-handle ui-resizable-e',
21325 html : '  '
21332 cfg.cls += ' fc-event-start';
21334 if ((i+1) == rows.length) {
21335 cfg.cls += ' fc-event-end';
21338 var ctr = _this.el.select('.fc-event-container',true).first();
21339 var cg = ctr.createChild(cfg);
21341 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21342 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21344 var r = (c.more.length) ? 1 : 0;
21345 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21346 cg.setWidth(ebox.right - sbox.x -2);
21348 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21349 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21350 cg.on('click', _this.onEventClick, _this, ev);
21361 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21362 style : 'position: absolute',
21363 unselectable : "on",
21366 cls: 'fc-event-inner',
21370 cls: 'fc-event-title',
21378 cls: 'ui-resizable-handle ui-resizable-e',
21379 html : '  '
21385 var ctr = _this.el.select('.fc-event-container',true).first();
21386 var cg = ctr.createChild(cfg);
21388 var sbox = c.select('.fc-day-content',true).first().getBox();
21389 var ebox = c.select('.fc-day-content',true).first().getBox();
21391 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21392 cg.setWidth(ebox.right - sbox.x -2);
21394 cg.on('click', _this.onMoreEventClick, _this, c.more);
21404 onEventEnter: function (e, el,event,d) {
21405 this.fireEvent('evententer', this, el, event);
21408 onEventLeave: function (e, el,event,d) {
21409 this.fireEvent('eventleave', this, el, event);
21412 onEventClick: function (e, el,event,d) {
21413 this.fireEvent('eventclick', this, el, event);
21416 onMonthChange: function () {
21420 onMoreEventClick: function(e, el, more)
21424 this.calpopover.placement = 'right';
21425 this.calpopover.setTitle('More');
21427 this.calpopover.setContent('');
21429 var ctr = this.calpopover.el.select('.popover-content', true).first();
21431 Roo.each(more, function(m){
21433 cls : 'fc-event-hori fc-event-draggable',
21436 var cg = ctr.createChild(cfg);
21438 cg.on('click', _this.onEventClick, _this, m);
21441 this.calpopover.show(el);
21446 onLoad: function ()
21448 this.calevents = [];
21451 if(this.store.getCount() > 0){
21452 this.store.data.each(function(d){
21455 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21456 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21457 time : d.data.start_time,
21458 title : d.data.title,
21459 description : d.data.description,
21460 venue : d.data.venue
21465 this.renderEvents();
21467 if(this.calevents.length && this.loadMask){
21468 this.maskEl.hide();
21472 onBeforeLoad: function()
21474 this.clearEvents();
21476 this.maskEl.show();
21490 * @class Roo.bootstrap.Popover
21491 * @extends Roo.bootstrap.Component
21494 * @children Roo.bootstrap.Component
21495 * Bootstrap Popover class
21496 * @cfg {String} html contents of the popover (or false to use children..)
21497 * @cfg {String} title of popover (or false to hide)
21498 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21499 * @cfg {String} trigger click || hover (or false to trigger manually)
21500 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21501 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21502 * - if false and it has a 'parent' then it will be automatically added to that element
21503 * - if string - Roo.get will be called
21504 * @cfg {Number} delay - delay before showing
21507 * Create a new Popover
21508 * @param {Object} config The config object
21511 Roo.bootstrap.Popover = function(config){
21512 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21518 * After the popover show
21520 * @param {Roo.bootstrap.Popover} this
21525 * After the popover hide
21527 * @param {Roo.bootstrap.Popover} this
21533 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21538 placement : 'right',
21539 trigger : 'hover', // hover
21545 can_build_overlaid : false,
21547 maskEl : false, // the mask element
21550 alignEl : false, // when show is called with an element - this get's stored.
21552 getChildContainer : function()
21554 return this.contentEl;
21557 getPopoverHeader : function()
21559 this.title = true; // flag not to hide it..
21560 this.headerEl.addClass('p-0');
21561 return this.headerEl
21565 getAutoCreate : function(){
21568 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21569 style: 'display:block',
21575 cls : 'popover-inner ',
21579 cls: 'popover-title popover-header',
21580 html : this.title === false ? '' : this.title
21583 cls : 'popover-content popover-body ' + (this.cls || ''),
21584 html : this.html || ''
21595 * @param {string} the title
21597 setTitle: function(str)
21601 this.headerEl.dom.innerHTML = str;
21606 * @param {string} the body content
21608 setContent: function(str)
21611 if (this.contentEl) {
21612 this.contentEl.dom.innerHTML = str;
21616 // as it get's added to the bottom of the page.
21617 onRender : function(ct, position)
21619 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21624 var cfg = Roo.apply({}, this.getAutoCreate());
21628 cfg.cls += ' ' + this.cls;
21631 cfg.style = this.style;
21633 //Roo.log("adding to ");
21634 this.el = Roo.get(document.body).createChild(cfg, position);
21635 // Roo.log(this.el);
21638 this.contentEl = this.el.select('.popover-content',true).first();
21639 this.headerEl = this.el.select('.popover-title',true).first();
21642 if(typeof(this.items) != 'undefined'){
21643 var items = this.items;
21646 for(var i =0;i < items.length;i++) {
21647 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21651 this.items = nitems;
21653 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21654 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21661 resizeMask : function()
21663 this.maskEl.setSize(
21664 Roo.lib.Dom.getViewWidth(true),
21665 Roo.lib.Dom.getViewHeight(true)
21669 initEvents : function()
21673 Roo.bootstrap.Popover.register(this);
21676 this.arrowEl = this.el.select('.arrow',true).first();
21677 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21678 this.el.enableDisplayMode('block');
21682 if (this.over === false && !this.parent()) {
21685 if (this.triggers === false) {
21690 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21691 var triggers = this.trigger ? this.trigger.split(' ') : [];
21692 Roo.each(triggers, function(trigger) {
21694 if (trigger == 'click') {
21695 on_el.on('click', this.toggle, this);
21696 } else if (trigger != 'manual') {
21697 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21698 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21700 on_el.on(eventIn ,this.enter, this);
21701 on_el.on(eventOut, this.leave, this);
21711 toggle : function () {
21712 this.hoverState == 'in' ? this.leave() : this.enter();
21715 enter : function () {
21717 clearTimeout(this.timeout);
21719 this.hoverState = 'in';
21721 if (!this.delay || !this.delay.show) {
21726 this.timeout = setTimeout(function () {
21727 if (_t.hoverState == 'in') {
21730 }, this.delay.show)
21733 leave : function() {
21734 clearTimeout(this.timeout);
21736 this.hoverState = 'out';
21738 if (!this.delay || !this.delay.hide) {
21743 this.timeout = setTimeout(function () {
21744 if (_t.hoverState == 'out') {
21747 }, this.delay.hide)
21751 * update the position of the dialog
21752 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21757 doAlign : function()
21760 if (this.alignEl) {
21761 this.updatePosition(this.placement, true);
21764 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21765 var es = this.el.getSize();
21766 var x = Roo.lib.Dom.getViewWidth()/2;
21767 var y = Roo.lib.Dom.getViewHeight()/2;
21768 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21780 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21781 * @param {string} (left|right|top|bottom) position
21783 show : function (on_el, placement)
21785 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21786 on_el = on_el || false; // default to false
21789 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21790 on_el = this.parent().el;
21791 } else if (this.over) {
21792 on_el = Roo.get(this.over);
21797 this.alignEl = Roo.get( on_el );
21800 this.render(document.body);
21806 if (this.title === false) {
21807 this.headerEl.hide();
21812 this.el.dom.style.display = 'block';
21816 //var arrow = this.el.select('.arrow',true).first();
21817 //arrow.set(align[2],
21819 this.el.addClass('in');
21823 this.hoverState = 'in';
21826 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21827 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21828 this.maskEl.dom.style.display = 'block';
21829 this.maskEl.addClass('show');
21831 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21833 this.fireEvent('show', this);
21837 * fire this manually after loading a grid in the table for example
21838 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21839 * @param {Boolean} try and move it if we cant get right position.
21841 updatePosition : function(placement, try_move)
21843 // allow for calling with no parameters
21844 placement = placement ? placement : this.placement;
21845 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21847 this.el.removeClass([
21848 'fade','top','bottom', 'left', 'right','in',
21849 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21851 this.el.addClass(placement + ' bs-popover-' + placement);
21853 if (!this.alignEl ) {
21857 switch (placement) {
21859 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21860 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21861 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21862 //normal display... or moved up/down.
21863 this.el.setXY(offset);
21864 var xy = this.alignEl.getAnchorXY('tr', false);
21866 this.arrowEl.setXY(xy);
21869 // continue through...
21870 return this.updatePosition('left', false);
21874 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21875 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21876 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21877 //normal display... or moved up/down.
21878 this.el.setXY(offset);
21879 var xy = this.alignEl.getAnchorXY('tl', false);
21880 xy[0]-=10;xy[1]+=5; // << fix me
21881 this.arrowEl.setXY(xy);
21885 return this.updatePosition('right', false);
21888 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21889 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21890 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21891 //normal display... or moved up/down.
21892 this.el.setXY(offset);
21893 var xy = this.alignEl.getAnchorXY('t', false);
21894 xy[1]-=10; // << fix me
21895 this.arrowEl.setXY(xy);
21899 return this.updatePosition('bottom', false);
21902 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21903 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21904 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21905 //normal display... or moved up/down.
21906 this.el.setXY(offset);
21907 var xy = this.alignEl.getAnchorXY('b', false);
21908 xy[1]+=2; // << fix me
21909 this.arrowEl.setXY(xy);
21913 return this.updatePosition('top', false);
21924 this.el.setXY([0,0]);
21925 this.el.removeClass('in');
21927 this.hoverState = null;
21928 this.maskEl.hide(); // always..
21929 this.fireEvent('hide', this);
21935 Roo.apply(Roo.bootstrap.Popover, {
21938 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21939 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21940 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21941 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21946 clickHander : false,
21950 onMouseDown : function(e)
21952 if (this.popups.length && !e.getTarget(".roo-popover")) {
21953 /// what is nothing is showing..
21962 register : function(popup)
21964 if (!Roo.bootstrap.Popover.clickHandler) {
21965 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21967 // hide other popups.
21968 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21969 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21970 this.hideAll(); //<< why?
21971 //this.popups.push(popup);
21973 hideAll : function()
21975 this.popups.forEach(function(p) {
21979 onShow : function() {
21980 Roo.bootstrap.Popover.popups.push(this);
21982 onHide : function() {
21983 Roo.bootstrap.Popover.popups.remove(this);
21988 * @class Roo.bootstrap.PopoverNav
21989 * @extends Roo.bootstrap.nav.Simplebar
21990 * @parent Roo.bootstrap.Popover
21991 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21993 * Bootstrap Popover header navigation class
21994 * FIXME? should this go under nav?
21998 * Create a new Popover Header Navigation
21999 * @param {Object} config The config object
22002 Roo.bootstrap.PopoverNav = function(config){
22003 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22006 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22009 container_method : 'getPopoverHeader'
22027 * @class Roo.bootstrap.Progress
22028 * @extends Roo.bootstrap.Component
22029 * @children Roo.bootstrap.ProgressBar
22030 * Bootstrap Progress class
22031 * @cfg {Boolean} striped striped of the progress bar
22032 * @cfg {Boolean} active animated of the progress bar
22036 * Create a new Progress
22037 * @param {Object} config The config object
22040 Roo.bootstrap.Progress = function(config){
22041 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22044 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22049 getAutoCreate : function(){
22057 cfg.cls += ' progress-striped';
22061 cfg.cls += ' active';
22080 * @class Roo.bootstrap.ProgressBar
22081 * @extends Roo.bootstrap.Component
22082 * Bootstrap ProgressBar class
22083 * @cfg {Number} aria_valuenow aria-value now
22084 * @cfg {Number} aria_valuemin aria-value min
22085 * @cfg {Number} aria_valuemax aria-value max
22086 * @cfg {String} label label for the progress bar
22087 * @cfg {String} panel (success | info | warning | danger )
22088 * @cfg {String} role role of the progress bar
22089 * @cfg {String} sr_only text
22093 * Create a new ProgressBar
22094 * @param {Object} config The config object
22097 Roo.bootstrap.ProgressBar = function(config){
22098 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22101 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22105 aria_valuemax : 100,
22111 getAutoCreate : function()
22116 cls: 'progress-bar',
22117 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22129 cfg.role = this.role;
22132 if(this.aria_valuenow){
22133 cfg['aria-valuenow'] = this.aria_valuenow;
22136 if(this.aria_valuemin){
22137 cfg['aria-valuemin'] = this.aria_valuemin;
22140 if(this.aria_valuemax){
22141 cfg['aria-valuemax'] = this.aria_valuemax;
22144 if(this.label && !this.sr_only){
22145 cfg.html = this.label;
22149 cfg.cls += ' progress-bar-' + this.panel;
22155 update : function(aria_valuenow)
22157 this.aria_valuenow = aria_valuenow;
22159 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22167 * @class Roo.bootstrap.TabGroup
22168 * @extends Roo.bootstrap.Column
22169 * @children Roo.bootstrap.TabPanel
22170 * Bootstrap Column class
22171 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22172 * @cfg {Boolean} carousel true to make the group behave like a carousel
22173 * @cfg {Boolean} bullets show bullets for the panels
22174 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22175 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22176 * @cfg {Boolean} showarrow (true|false) show arrow default true
22179 * Create a new TabGroup
22180 * @param {Object} config The config object
22183 Roo.bootstrap.TabGroup = function(config){
22184 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22186 this.navId = Roo.id();
22189 Roo.bootstrap.TabGroup.register(this);
22193 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22196 transition : false,
22201 slideOnTouch : false,
22204 getAutoCreate : function()
22206 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22208 cfg.cls += ' tab-content';
22210 if (this.carousel) {
22211 cfg.cls += ' carousel slide';
22214 cls : 'carousel-inner',
22218 if(this.bullets && !Roo.isTouch){
22221 cls : 'carousel-bullets',
22225 if(this.bullets_cls){
22226 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22233 cfg.cn[0].cn.push(bullets);
22236 if(this.showarrow){
22237 cfg.cn[0].cn.push({
22239 class : 'carousel-arrow',
22243 class : 'carousel-prev',
22247 class : 'fa fa-chevron-left'
22253 class : 'carousel-next',
22257 class : 'fa fa-chevron-right'
22270 initEvents: function()
22272 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22273 // this.el.on("touchstart", this.onTouchStart, this);
22276 if(this.autoslide){
22279 this.slideFn = window.setInterval(function() {
22280 _this.showPanelNext();
22284 if(this.showarrow){
22285 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22286 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22292 // onTouchStart : function(e, el, o)
22294 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22298 // this.showPanelNext();
22302 getChildContainer : function()
22304 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22308 * register a Navigation item
22309 * @param {Roo.bootstrap.nav.Item} the navitem to add
22311 register : function(item)
22313 this.tabs.push( item);
22314 item.navId = this.navId; // not really needed..
22319 getActivePanel : function()
22322 Roo.each(this.tabs, function(t) {
22332 getPanelByName : function(n)
22335 Roo.each(this.tabs, function(t) {
22336 if (t.tabId == n) {
22344 indexOfPanel : function(p)
22347 Roo.each(this.tabs, function(t,i) {
22348 if (t.tabId == p.tabId) {
22357 * show a specific panel
22358 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22359 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22361 showPanel : function (pan)
22363 if(this.transition || typeof(pan) == 'undefined'){
22364 Roo.log("waiting for the transitionend");
22368 if (typeof(pan) == 'number') {
22369 pan = this.tabs[pan];
22372 if (typeof(pan) == 'string') {
22373 pan = this.getPanelByName(pan);
22376 var cur = this.getActivePanel();
22379 Roo.log('pan or acitve pan is undefined');
22383 if (pan.tabId == this.getActivePanel().tabId) {
22387 if (false === cur.fireEvent('beforedeactivate')) {
22391 if(this.bullets > 0 && !Roo.isTouch){
22392 this.setActiveBullet(this.indexOfPanel(pan));
22395 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22397 //class="carousel-item carousel-item-next carousel-item-left"
22399 this.transition = true;
22400 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22401 var lr = dir == 'next' ? 'left' : 'right';
22402 pan.el.addClass(dir); // or prev
22403 pan.el.addClass('carousel-item-' + dir); // or prev
22404 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22405 cur.el.addClass(lr); // or right
22406 pan.el.addClass(lr);
22407 cur.el.addClass('carousel-item-' +lr); // or right
22408 pan.el.addClass('carousel-item-' +lr);
22412 cur.el.on('transitionend', function() {
22413 Roo.log("trans end?");
22415 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22416 pan.setActive(true);
22418 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22419 cur.setActive(false);
22421 _this.transition = false;
22423 }, this, { single: true } );
22428 cur.setActive(false);
22429 pan.setActive(true);
22434 showPanelNext : function()
22436 var i = this.indexOfPanel(this.getActivePanel());
22438 if (i >= this.tabs.length - 1 && !this.autoslide) {
22442 if (i >= this.tabs.length - 1 && this.autoslide) {
22446 this.showPanel(this.tabs[i+1]);
22449 showPanelPrev : function()
22451 var i = this.indexOfPanel(this.getActivePanel());
22453 if (i < 1 && !this.autoslide) {
22457 if (i < 1 && this.autoslide) {
22458 i = this.tabs.length;
22461 this.showPanel(this.tabs[i-1]);
22465 addBullet: function()
22467 if(!this.bullets || Roo.isTouch){
22470 var ctr = this.el.select('.carousel-bullets',true).first();
22471 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22472 var bullet = ctr.createChild({
22473 cls : 'bullet bullet-' + i
22474 },ctr.dom.lastChild);
22479 bullet.on('click', (function(e, el, o, ii, t){
22481 e.preventDefault();
22483 this.showPanel(ii);
22485 if(this.autoslide && this.slideFn){
22486 clearInterval(this.slideFn);
22487 this.slideFn = window.setInterval(function() {
22488 _this.showPanelNext();
22492 }).createDelegate(this, [i, bullet], true));
22497 setActiveBullet : function(i)
22503 Roo.each(this.el.select('.bullet', true).elements, function(el){
22504 el.removeClass('selected');
22507 var bullet = this.el.select('.bullet-' + i, true).first();
22513 bullet.addClass('selected');
22524 Roo.apply(Roo.bootstrap.TabGroup, {
22528 * register a Navigation Group
22529 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22531 register : function(navgrp)
22533 this.groups[navgrp.navId] = navgrp;
22537 * fetch a Navigation Group based on the navigation ID
22538 * if one does not exist , it will get created.
22539 * @param {string} the navgroup to add
22540 * @returns {Roo.bootstrap.nav.Group} the navgroup
22542 get: function(navId) {
22543 if (typeof(this.groups[navId]) == 'undefined') {
22544 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22546 return this.groups[navId] ;
22561 * @class Roo.bootstrap.TabPanel
22562 * @extends Roo.bootstrap.Component
22563 * @children Roo.bootstrap.Component
22564 * Bootstrap TabPanel class
22565 * @cfg {Boolean} active panel active
22566 * @cfg {String} html panel content
22567 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22568 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22569 * @cfg {String} href click to link..
22570 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22574 * Create a new TabPanel
22575 * @param {Object} config The config object
22578 Roo.bootstrap.TabPanel = function(config){
22579 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22583 * Fires when the active status changes
22584 * @param {Roo.bootstrap.TabPanel} this
22585 * @param {Boolean} state the new state
22590 * @event beforedeactivate
22591 * Fires before a tab is de-activated - can be used to do validation on a form.
22592 * @param {Roo.bootstrap.TabPanel} this
22593 * @return {Boolean} false if there is an error
22596 'beforedeactivate': true
22599 this.tabId = this.tabId || Roo.id();
22603 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22610 touchSlide : false,
22611 getAutoCreate : function(){
22616 // item is needed for carousel - not sure if it has any effect otherwise
22617 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22618 html: this.html || ''
22622 cfg.cls += ' active';
22626 cfg.tabId = this.tabId;
22634 initEvents: function()
22636 var p = this.parent();
22638 this.navId = this.navId || p.navId;
22640 if (typeof(this.navId) != 'undefined') {
22641 // not really needed.. but just in case.. parent should be a NavGroup.
22642 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22646 var i = tg.tabs.length - 1;
22648 if(this.active && tg.bullets > 0 && i < tg.bullets){
22649 tg.setActiveBullet(i);
22653 this.el.on('click', this.onClick, this);
22655 if(Roo.isTouch && this.touchSlide){
22656 this.el.on("touchstart", this.onTouchStart, this);
22657 this.el.on("touchmove", this.onTouchMove, this);
22658 this.el.on("touchend", this.onTouchEnd, this);
22663 onRender : function(ct, position)
22665 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22668 setActive : function(state)
22670 Roo.log("panel - set active " + this.tabId + "=" + state);
22672 this.active = state;
22674 this.el.removeClass('active');
22676 } else if (!this.el.hasClass('active')) {
22677 this.el.addClass('active');
22680 this.fireEvent('changed', this, state);
22683 onClick : function(e)
22685 e.preventDefault();
22687 if(!this.href.length){
22691 window.location.href = this.href;
22700 onTouchStart : function(e)
22702 this.swiping = false;
22704 this.startX = e.browserEvent.touches[0].clientX;
22705 this.startY = e.browserEvent.touches[0].clientY;
22708 onTouchMove : function(e)
22710 this.swiping = true;
22712 this.endX = e.browserEvent.touches[0].clientX;
22713 this.endY = e.browserEvent.touches[0].clientY;
22716 onTouchEnd : function(e)
22723 var tabGroup = this.parent();
22725 if(this.endX > this.startX){ // swiping right
22726 tabGroup.showPanelPrev();
22730 if(this.startX > this.endX){ // swiping left
22731 tabGroup.showPanelNext();
22750 * @class Roo.bootstrap.form.DateField
22751 * @extends Roo.bootstrap.form.Input
22752 * Bootstrap DateField class
22753 * @cfg {Number} weekStart default 0
22754 * @cfg {String} viewMode default empty, (months|years)
22755 * @cfg {String} minViewMode default empty, (months|years)
22756 * @cfg {Number} startDate default -Infinity
22757 * @cfg {Number} endDate default Infinity
22758 * @cfg {Boolean} todayHighlight default false
22759 * @cfg {Boolean} todayBtn default false
22760 * @cfg {Boolean} calendarWeeks default false
22761 * @cfg {Object} daysOfWeekDisabled default empty
22762 * @cfg {Boolean} singleMode default false (true | false)
22764 * @cfg {Boolean} keyboardNavigation default true
22765 * @cfg {String} language default en
22768 * Create a new DateField
22769 * @param {Object} config The config object
22772 Roo.bootstrap.form.DateField = function(config){
22773 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22777 * Fires when this field show.
22778 * @param {Roo.bootstrap.form.DateField} this
22779 * @param {Mixed} date The date value
22784 * Fires when this field hide.
22785 * @param {Roo.bootstrap.form.DateField} this
22786 * @param {Mixed} date The date value
22791 * Fires when select a date.
22792 * @param {Roo.bootstrap.form.DateField} this
22793 * @param {Mixed} date The date value
22797 * @event beforeselect
22798 * Fires when before select a date.
22799 * @param {Roo.bootstrap.form.DateField} this
22800 * @param {Mixed} date The date value
22802 beforeselect : true
22806 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22809 * @cfg {String} format
22810 * The default date format string which can be overriden for localization support. The format must be
22811 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22815 * @cfg {String} altFormats
22816 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22817 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22819 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22827 todayHighlight : false,
22833 keyboardNavigation: true,
22835 calendarWeeks: false,
22837 startDate: -Infinity,
22841 daysOfWeekDisabled: [],
22845 singleMode : false,
22847 UTCDate: function()
22849 return new Date(Date.UTC.apply(Date, arguments));
22852 UTCToday: function()
22854 var today = new Date();
22855 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22858 getDate: function() {
22859 var d = this.getUTCDate();
22860 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22863 getUTCDate: function() {
22867 setDate: function(d) {
22868 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22871 setUTCDate: function(d) {
22873 this.setValue(this.formatDate(this.date));
22876 onRender: function(ct, position)
22879 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22881 this.language = this.language || 'en';
22882 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22883 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22885 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22886 this.format = this.format || 'm/d/y';
22887 this.isInline = false;
22888 this.isInput = true;
22889 this.component = this.el.select('.add-on', true).first() || false;
22890 this.component = (this.component && this.component.length === 0) ? false : this.component;
22891 this.hasInput = this.component && this.inputEl().length;
22893 if (typeof(this.minViewMode === 'string')) {
22894 switch (this.minViewMode) {
22896 this.minViewMode = 1;
22899 this.minViewMode = 2;
22902 this.minViewMode = 0;
22907 if (typeof(this.viewMode === 'string')) {
22908 switch (this.viewMode) {
22921 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22923 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22925 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22927 this.picker().on('mousedown', this.onMousedown, this);
22928 this.picker().on('click', this.onClick, this);
22930 this.picker().addClass('datepicker-dropdown');
22932 this.startViewMode = this.viewMode;
22934 if(this.singleMode){
22935 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22936 v.setVisibilityMode(Roo.Element.DISPLAY);
22940 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22941 v.setStyle('width', '189px');
22945 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22946 if(!this.calendarWeeks){
22951 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22952 v.attr('colspan', function(i, val){
22953 return parseInt(val) + 1;
22958 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22960 this.setStartDate(this.startDate);
22961 this.setEndDate(this.endDate);
22963 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22970 if(this.isInline) {
22975 picker : function()
22977 return this.pickerEl;
22978 // return this.el.select('.datepicker', true).first();
22981 fillDow: function()
22983 var dowCnt = this.weekStart;
22992 if(this.calendarWeeks){
23000 while (dowCnt < this.weekStart + 7) {
23004 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23008 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23011 fillMonths: function()
23014 var months = this.picker().select('>.datepicker-months td', true).first();
23016 months.dom.innerHTML = '';
23022 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23025 months.createChild(month);
23032 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;
23034 if (this.date < this.startDate) {
23035 this.viewDate = new Date(this.startDate);
23036 } else if (this.date > this.endDate) {
23037 this.viewDate = new Date(this.endDate);
23039 this.viewDate = new Date(this.date);
23047 var d = new Date(this.viewDate),
23048 year = d.getUTCFullYear(),
23049 month = d.getUTCMonth(),
23050 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23051 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23052 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23053 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23054 currentDate = this.date && this.date.valueOf(),
23055 today = this.UTCToday();
23057 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23059 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23061 // this.picker.select('>tfoot th.today').
23062 // .text(dates[this.language].today)
23063 // .toggle(this.todayBtn !== false);
23065 this.updateNavArrows();
23068 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23070 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23072 prevMonth.setUTCDate(day);
23074 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23076 var nextMonth = new Date(prevMonth);
23078 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23080 nextMonth = nextMonth.valueOf();
23082 var fillMonths = false;
23084 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23086 while(prevMonth.valueOf() <= nextMonth) {
23089 if (prevMonth.getUTCDay() === this.weekStart) {
23091 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23099 if(this.calendarWeeks){
23100 // ISO 8601: First week contains first thursday.
23101 // ISO also states week starts on Monday, but we can be more abstract here.
23103 // Start of current week: based on weekstart/current date
23104 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23105 // Thursday of this week
23106 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23107 // First Thursday of year, year from thursday
23108 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23109 // Calendar week: ms between thursdays, div ms per day, div 7 days
23110 calWeek = (th - yth) / 864e5 / 7 + 1;
23112 fillMonths.cn.push({
23120 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23122 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23125 if (this.todayHighlight &&
23126 prevMonth.getUTCFullYear() == today.getFullYear() &&
23127 prevMonth.getUTCMonth() == today.getMonth() &&
23128 prevMonth.getUTCDate() == today.getDate()) {
23129 clsName += ' today';
23132 if (currentDate && prevMonth.valueOf() === currentDate) {
23133 clsName += ' active';
23136 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23137 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23138 clsName += ' disabled';
23141 fillMonths.cn.push({
23143 cls: 'day ' + clsName,
23144 html: prevMonth.getDate()
23147 prevMonth.setDate(prevMonth.getDate()+1);
23150 var currentYear = this.date && this.date.getUTCFullYear();
23151 var currentMonth = this.date && this.date.getUTCMonth();
23153 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23155 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23156 v.removeClass('active');
23158 if(currentYear === year && k === currentMonth){
23159 v.addClass('active');
23162 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23163 v.addClass('disabled');
23169 year = parseInt(year/10, 10) * 10;
23171 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23173 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23176 for (var i = -1; i < 11; i++) {
23177 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23179 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23187 showMode: function(dir)
23190 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23193 Roo.each(this.picker().select('>div',true).elements, function(v){
23194 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23197 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23202 if(this.isInline) {
23206 this.picker().removeClass(['bottom', 'top']);
23208 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23210 * place to the top of element!
23214 this.picker().addClass('top');
23215 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23220 this.picker().addClass('bottom');
23222 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23225 parseDate : function(value)
23227 if(!value || value instanceof Date){
23230 var v = Date.parseDate(value, this.format);
23231 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23232 v = Date.parseDate(value, 'Y-m-d');
23234 if(!v && this.altFormats){
23235 if(!this.altFormatsArray){
23236 this.altFormatsArray = this.altFormats.split("|");
23238 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23239 v = Date.parseDate(value, this.altFormatsArray[i]);
23245 formatDate : function(date, fmt)
23247 return (!date || !(date instanceof Date)) ?
23248 date : date.dateFormat(fmt || this.format);
23251 onFocus : function()
23253 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23257 onBlur : function()
23259 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23261 var d = this.inputEl().getValue();
23268 showPopup : function()
23270 this.picker().show();
23274 this.fireEvent('showpopup', this, this.date);
23277 hidePopup : function()
23279 if(this.isInline) {
23282 this.picker().hide();
23283 this.viewMode = this.startViewMode;
23286 this.fireEvent('hidepopup', this, this.date);
23290 onMousedown: function(e)
23292 e.stopPropagation();
23293 e.preventDefault();
23298 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23302 setValue: function(v)
23304 if(this.fireEvent('beforeselect', this, v) !== false){
23305 var d = new Date(this.parseDate(v) ).clearTime();
23307 if(isNaN(d.getTime())){
23308 this.date = this.viewDate = '';
23309 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23313 v = this.formatDate(d);
23315 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23317 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23321 this.fireEvent('select', this, this.date);
23325 getValue: function()
23327 return this.formatDate(this.date);
23330 fireKey: function(e)
23332 if (!this.picker().isVisible()){
23333 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23339 var dateChanged = false,
23341 newDate, newViewDate;
23346 e.preventDefault();
23350 if (!this.keyboardNavigation) {
23353 dir = e.keyCode == 37 ? -1 : 1;
23356 newDate = this.moveYear(this.date, dir);
23357 newViewDate = this.moveYear(this.viewDate, dir);
23358 } else if (e.shiftKey){
23359 newDate = this.moveMonth(this.date, dir);
23360 newViewDate = this.moveMonth(this.viewDate, dir);
23362 newDate = new Date(this.date);
23363 newDate.setUTCDate(this.date.getUTCDate() + dir);
23364 newViewDate = new Date(this.viewDate);
23365 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23367 if (this.dateWithinRange(newDate)){
23368 this.date = newDate;
23369 this.viewDate = newViewDate;
23370 this.setValue(this.formatDate(this.date));
23372 e.preventDefault();
23373 dateChanged = true;
23378 if (!this.keyboardNavigation) {
23381 dir = e.keyCode == 38 ? -1 : 1;
23383 newDate = this.moveYear(this.date, dir);
23384 newViewDate = this.moveYear(this.viewDate, dir);
23385 } else if (e.shiftKey){
23386 newDate = this.moveMonth(this.date, dir);
23387 newViewDate = this.moveMonth(this.viewDate, dir);
23389 newDate = new Date(this.date);
23390 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23391 newViewDate = new Date(this.viewDate);
23392 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23394 if (this.dateWithinRange(newDate)){
23395 this.date = newDate;
23396 this.viewDate = newViewDate;
23397 this.setValue(this.formatDate(this.date));
23399 e.preventDefault();
23400 dateChanged = true;
23404 this.setValue(this.formatDate(this.date));
23406 e.preventDefault();
23409 this.setValue(this.formatDate(this.date));
23423 onClick: function(e)
23425 e.stopPropagation();
23426 e.preventDefault();
23428 var target = e.getTarget();
23430 if(target.nodeName.toLowerCase() === 'i'){
23431 target = Roo.get(target).dom.parentNode;
23434 var nodeName = target.nodeName;
23435 var className = target.className;
23436 var html = target.innerHTML;
23437 //Roo.log(nodeName);
23439 switch(nodeName.toLowerCase()) {
23441 switch(className) {
23447 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23448 switch(this.viewMode){
23450 this.viewDate = this.moveMonth(this.viewDate, dir);
23454 this.viewDate = this.moveYear(this.viewDate, dir);
23460 var date = new Date();
23461 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23463 this.setValue(this.formatDate(this.date));
23470 if (className.indexOf('disabled') < 0) {
23471 if (!this.viewDate) {
23472 this.viewDate = new Date();
23474 this.viewDate.setUTCDate(1);
23475 if (className.indexOf('month') > -1) {
23476 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23478 var year = parseInt(html, 10) || 0;
23479 this.viewDate.setUTCFullYear(year);
23483 if(this.singleMode){
23484 this.setValue(this.formatDate(this.viewDate));
23495 //Roo.log(className);
23496 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23497 var day = parseInt(html, 10) || 1;
23498 var year = (this.viewDate || new Date()).getUTCFullYear(),
23499 month = (this.viewDate || new Date()).getUTCMonth();
23501 if (className.indexOf('old') > -1) {
23508 } else if (className.indexOf('new') > -1) {
23516 //Roo.log([year,month,day]);
23517 this.date = this.UTCDate(year, month, day,0,0,0,0);
23518 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23520 //Roo.log(this.formatDate(this.date));
23521 this.setValue(this.formatDate(this.date));
23528 setStartDate: function(startDate)
23530 this.startDate = startDate || -Infinity;
23531 if (this.startDate !== -Infinity) {
23532 this.startDate = this.parseDate(this.startDate);
23535 this.updateNavArrows();
23538 setEndDate: function(endDate)
23540 this.endDate = endDate || Infinity;
23541 if (this.endDate !== Infinity) {
23542 this.endDate = this.parseDate(this.endDate);
23545 this.updateNavArrows();
23548 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23550 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23551 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23552 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23554 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23555 return parseInt(d, 10);
23558 this.updateNavArrows();
23561 updateNavArrows: function()
23563 if(this.singleMode){
23567 var d = new Date(this.viewDate),
23568 year = d.getUTCFullYear(),
23569 month = d.getUTCMonth();
23571 Roo.each(this.picker().select('.prev', true).elements, function(v){
23573 switch (this.viewMode) {
23576 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23582 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23589 Roo.each(this.picker().select('.next', true).elements, function(v){
23591 switch (this.viewMode) {
23594 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23600 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23608 moveMonth: function(date, dir)
23613 var new_date = new Date(date.valueOf()),
23614 day = new_date.getUTCDate(),
23615 month = new_date.getUTCMonth(),
23616 mag = Math.abs(dir),
23618 dir = dir > 0 ? 1 : -1;
23621 // If going back one month, make sure month is not current month
23622 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23624 return new_date.getUTCMonth() == month;
23626 // If going forward one month, make sure month is as expected
23627 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23629 return new_date.getUTCMonth() != new_month;
23631 new_month = month + dir;
23632 new_date.setUTCMonth(new_month);
23633 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23634 if (new_month < 0 || new_month > 11) {
23635 new_month = (new_month + 12) % 12;
23638 // For magnitudes >1, move one month at a time...
23639 for (var i=0; i<mag; i++) {
23640 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23641 new_date = this.moveMonth(new_date, dir);
23643 // ...then reset the day, keeping it in the new month
23644 new_month = new_date.getUTCMonth();
23645 new_date.setUTCDate(day);
23647 return new_month != new_date.getUTCMonth();
23650 // Common date-resetting loop -- if date is beyond end of month, make it
23653 new_date.setUTCDate(--day);
23654 new_date.setUTCMonth(new_month);
23659 moveYear: function(date, dir)
23661 return this.moveMonth(date, dir*12);
23664 dateWithinRange: function(date)
23666 return date >= this.startDate && date <= this.endDate;
23672 this.picker().remove();
23675 validateValue : function(value)
23677 if(this.getVisibilityEl().hasClass('hidden')){
23681 if(value.length < 1) {
23682 if(this.allowBlank){
23688 if(value.length < this.minLength){
23691 if(value.length > this.maxLength){
23695 var vt = Roo.form.VTypes;
23696 if(!vt[this.vtype](value, this)){
23700 if(typeof this.validator == "function"){
23701 var msg = this.validator(value);
23707 if(this.regex && !this.regex.test(value)){
23711 if(typeof(this.parseDate(value)) == 'undefined'){
23715 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23719 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23729 this.date = this.viewDate = '';
23731 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23736 Roo.apply(Roo.bootstrap.form.DateField, {
23747 html: '<i class="fa fa-arrow-left"/>'
23757 html: '<i class="fa fa-arrow-right"/>'
23799 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23800 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23801 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23802 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23803 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23816 navFnc: 'FullYear',
23821 navFnc: 'FullYear',
23826 Roo.apply(Roo.bootstrap.form.DateField, {
23830 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23834 cls: 'datepicker-days',
23838 cls: 'table-condensed',
23840 Roo.bootstrap.form.DateField.head,
23844 Roo.bootstrap.form.DateField.footer
23851 cls: 'datepicker-months',
23855 cls: 'table-condensed',
23857 Roo.bootstrap.form.DateField.head,
23858 Roo.bootstrap.form.DateField.content,
23859 Roo.bootstrap.form.DateField.footer
23866 cls: 'datepicker-years',
23870 cls: 'table-condensed',
23872 Roo.bootstrap.form.DateField.head,
23873 Roo.bootstrap.form.DateField.content,
23874 Roo.bootstrap.form.DateField.footer
23893 * @class Roo.bootstrap.form.TimeField
23894 * @extends Roo.bootstrap.form.Input
23895 * Bootstrap DateField class
23899 * Create a new TimeField
23900 * @param {Object} config The config object
23903 Roo.bootstrap.form.TimeField = function(config){
23904 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23908 * Fires when this field show.
23909 * @param {Roo.bootstrap.form.DateField} thisthis
23910 * @param {Mixed} date The date value
23915 * Fires when this field hide.
23916 * @param {Roo.bootstrap.form.DateField} this
23917 * @param {Mixed} date The date value
23922 * Fires when select a date.
23923 * @param {Roo.bootstrap.form.DateField} this
23924 * @param {Mixed} date The date value
23930 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23933 * @cfg {String} format
23934 * The default time format string which can be overriden for localization support. The format must be
23935 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23939 getAutoCreate : function()
23941 this.after = '<i class="fa far fa-clock"></i>';
23942 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23946 onRender: function(ct, position)
23949 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23951 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23953 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23955 this.pop = this.picker().select('>.datepicker-time',true).first();
23956 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23958 this.picker().on('mousedown', this.onMousedown, this);
23959 this.picker().on('click', this.onClick, this);
23961 this.picker().addClass('datepicker-dropdown');
23966 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23967 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23968 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23969 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23970 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23971 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23975 fireKey: function(e){
23976 if (!this.picker().isVisible()){
23977 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23983 e.preventDefault();
23991 this.onTogglePeriod();
23994 this.onIncrementMinutes();
23997 this.onDecrementMinutes();
24006 onClick: function(e) {
24007 e.stopPropagation();
24008 e.preventDefault();
24011 picker : function()
24013 return this.pickerEl;
24016 fillTime: function()
24018 var time = this.pop.select('tbody', true).first();
24020 time.dom.innerHTML = '';
24035 cls: 'hours-up fa fas fa-chevron-up'
24055 cls: 'minutes-up fa fas fa-chevron-up'
24076 cls: 'timepicker-hour',
24091 cls: 'timepicker-minute',
24106 cls: 'btn btn-primary period',
24128 cls: 'hours-down fa fas fa-chevron-down'
24148 cls: 'minutes-down fa fas fa-chevron-down'
24166 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24173 var hours = this.time.getHours();
24174 var minutes = this.time.getMinutes();
24187 hours = hours - 12;
24191 hours = '0' + hours;
24195 minutes = '0' + minutes;
24198 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24199 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24200 this.pop.select('button', true).first().dom.innerHTML = period;
24206 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24208 var cls = ['bottom'];
24210 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24217 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24221 //this.picker().setXY(20000,20000);
24222 this.picker().addClass(cls.join('-'));
24226 Roo.each(cls, function(c){
24231 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24232 //_this.picker().setTop(_this.inputEl().getHeight());
24236 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24238 //_this.picker().setTop(0 - _this.picker().getHeight());
24243 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24247 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24255 onFocus : function()
24257 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24261 onBlur : function()
24263 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24269 this.picker().show();
24274 this.fireEvent('show', this, this.date);
24279 this.picker().hide();
24282 this.fireEvent('hide', this, this.date);
24285 setTime : function()
24288 this.setValue(this.time.format(this.format));
24290 this.fireEvent('select', this, this.date);
24295 onMousedown: function(e){
24296 e.stopPropagation();
24297 e.preventDefault();
24300 onIncrementHours: function()
24302 Roo.log('onIncrementHours');
24303 this.time = this.time.add(Date.HOUR, 1);
24308 onDecrementHours: function()
24310 Roo.log('onDecrementHours');
24311 this.time = this.time.add(Date.HOUR, -1);
24315 onIncrementMinutes: function()
24317 Roo.log('onIncrementMinutes');
24318 this.time = this.time.add(Date.MINUTE, 1);
24322 onDecrementMinutes: function()
24324 Roo.log('onDecrementMinutes');
24325 this.time = this.time.add(Date.MINUTE, -1);
24329 onTogglePeriod: function()
24331 Roo.log('onTogglePeriod');
24332 this.time = this.time.add(Date.HOUR, 12);
24340 Roo.apply(Roo.bootstrap.form.TimeField, {
24344 cls: 'datepicker dropdown-menu',
24348 cls: 'datepicker-time',
24352 cls: 'table-condensed',
24381 cls: 'btn btn-info ok',
24409 * @class Roo.bootstrap.form.MonthField
24410 * @extends Roo.bootstrap.form.Input
24411 * Bootstrap MonthField class
24413 * @cfg {String} language default en
24416 * Create a new MonthField
24417 * @param {Object} config The config object
24420 Roo.bootstrap.form.MonthField = function(config){
24421 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24426 * Fires when this field show.
24427 * @param {Roo.bootstrap.form.MonthField} this
24428 * @param {Mixed} date The date value
24433 * Fires when this field hide.
24434 * @param {Roo.bootstrap.form.MonthField} this
24435 * @param {Mixed} date The date value
24440 * Fires when select a date.
24441 * @param {Roo.bootstrap.form.MonthField} this
24442 * @param {String} oldvalue The old value
24443 * @param {String} newvalue The new value
24449 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24451 onRender: function(ct, position)
24454 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24456 this.language = this.language || 'en';
24457 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24458 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24460 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24461 this.isInline = false;
24462 this.isInput = true;
24463 this.component = this.el.select('.add-on', true).first() || false;
24464 this.component = (this.component && this.component.length === 0) ? false : this.component;
24465 this.hasInput = this.component && this.inputEL().length;
24467 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24469 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24471 this.picker().on('mousedown', this.onMousedown, this);
24472 this.picker().on('click', this.onClick, this);
24474 this.picker().addClass('datepicker-dropdown');
24476 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24477 v.setStyle('width', '189px');
24484 if(this.isInline) {
24490 setValue: function(v, suppressEvent)
24492 var o = this.getValue();
24494 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24498 if(suppressEvent !== true){
24499 this.fireEvent('select', this, o, v);
24504 getValue: function()
24509 onClick: function(e)
24511 e.stopPropagation();
24512 e.preventDefault();
24514 var target = e.getTarget();
24516 if(target.nodeName.toLowerCase() === 'i'){
24517 target = Roo.get(target).dom.parentNode;
24520 var nodeName = target.nodeName;
24521 var className = target.className;
24522 var html = target.innerHTML;
24524 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24528 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24530 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24536 picker : function()
24538 return this.pickerEl;
24541 fillMonths: function()
24544 var months = this.picker().select('>.datepicker-months td', true).first();
24546 months.dom.innerHTML = '';
24552 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24555 months.createChild(month);
24564 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24565 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24568 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24569 e.removeClass('active');
24571 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24572 e.addClass('active');
24579 if(this.isInline) {
24583 this.picker().removeClass(['bottom', 'top']);
24585 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24587 * place to the top of element!
24591 this.picker().addClass('top');
24592 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24597 this.picker().addClass('bottom');
24599 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24602 onFocus : function()
24604 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24608 onBlur : function()
24610 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24612 var d = this.inputEl().getValue();
24621 this.picker().show();
24622 this.picker().select('>.datepicker-months', true).first().show();
24626 this.fireEvent('show', this, this.date);
24631 if(this.isInline) {
24634 this.picker().hide();
24635 this.fireEvent('hide', this, this.date);
24639 onMousedown: function(e)
24641 e.stopPropagation();
24642 e.preventDefault();
24647 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24651 fireKey: function(e)
24653 if (!this.picker().isVisible()){
24654 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24665 e.preventDefault();
24669 dir = e.keyCode == 37 ? -1 : 1;
24671 this.vIndex = this.vIndex + dir;
24673 if(this.vIndex < 0){
24677 if(this.vIndex > 11){
24681 if(isNaN(this.vIndex)){
24685 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24691 dir = e.keyCode == 38 ? -1 : 1;
24693 this.vIndex = this.vIndex + dir * 4;
24695 if(this.vIndex < 0){
24699 if(this.vIndex > 11){
24703 if(isNaN(this.vIndex)){
24707 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24712 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24713 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24717 e.preventDefault();
24720 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24721 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24737 this.picker().remove();
24742 Roo.apply(Roo.bootstrap.form.MonthField, {
24761 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24762 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24767 Roo.apply(Roo.bootstrap.form.MonthField, {
24771 cls: 'datepicker dropdown-menu roo-dynamic',
24775 cls: 'datepicker-months',
24779 cls: 'table-condensed',
24781 Roo.bootstrap.form.DateField.content
24801 * @class Roo.bootstrap.form.CheckBox
24802 * @extends Roo.bootstrap.form.Input
24803 * Bootstrap CheckBox class
24805 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24806 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24807 * @cfg {String} boxLabel The text that appears beside the checkbox
24808 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24809 * @cfg {Boolean} checked initnal the element
24810 * @cfg {Boolean} inline inline the element (default false)
24811 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24812 * @cfg {String} tooltip label tooltip
24815 * Create a new CheckBox
24816 * @param {Object} config The config object
24819 Roo.bootstrap.form.CheckBox = function(config){
24820 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24825 * Fires when the element is checked or unchecked.
24826 * @param {Roo.bootstrap.form.CheckBox} this This input
24827 * @param {Boolean} checked The new checked value
24832 * Fires when the element is click.
24833 * @param {Roo.bootstrap.form.CheckBox} this This input
24840 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24842 inputType: 'checkbox',
24851 // checkbox success does not make any sense really..
24856 getAutoCreate : function()
24858 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24864 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24867 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24873 type : this.inputType,
24874 value : this.inputValue,
24875 cls : 'roo-' + this.inputType, //'form-box',
24876 placeholder : this.placeholder || ''
24880 if(this.inputType != 'radio'){
24884 cls : 'roo-hidden-value',
24885 value : this.checked ? this.inputValue : this.valueOff
24890 if (this.weight) { // Validity check?
24891 cfg.cls += " " + this.inputType + "-" + this.weight;
24894 if (this.disabled) {
24895 input.disabled=true;
24899 input.checked = this.checked;
24904 input.name = this.name;
24906 if(this.inputType != 'radio'){
24907 hidden.name = this.name;
24908 input.name = '_hidden_' + this.name;
24913 input.cls += ' input-' + this.size;
24918 ['xs','sm','md','lg'].map(function(size){
24919 if (settings[size]) {
24920 cfg.cls += ' col-' + size + '-' + settings[size];
24924 var inputblock = input;
24926 if (this.before || this.after) {
24929 cls : 'input-group',
24934 inputblock.cn.push({
24936 cls : 'input-group-addon',
24941 inputblock.cn.push(input);
24943 if(this.inputType != 'radio'){
24944 inputblock.cn.push(hidden);
24948 inputblock.cn.push({
24950 cls : 'input-group-addon',
24956 var boxLabelCfg = false;
24962 //'for': id, // box label is handled by onclick - so no for...
24964 html: this.boxLabel
24967 boxLabelCfg.tooltip = this.tooltip;
24973 if (align ==='left' && this.fieldLabel.length) {
24974 // Roo.log("left and has label");
24979 cls : 'control-label',
24980 html : this.fieldLabel
24991 cfg.cn[1].cn.push(boxLabelCfg);
24994 if(this.labelWidth > 12){
24995 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24998 if(this.labelWidth < 13 && this.labelmd == 0){
24999 this.labelmd = this.labelWidth;
25002 if(this.labellg > 0){
25003 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25004 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25007 if(this.labelmd > 0){
25008 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25009 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25012 if(this.labelsm > 0){
25013 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25014 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25017 if(this.labelxs > 0){
25018 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25019 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25022 } else if ( this.fieldLabel.length) {
25023 // Roo.log(" label");
25027 tag: this.boxLabel ? 'span' : 'label',
25029 cls: 'control-label box-input-label',
25030 //cls : 'input-group-addon',
25031 html : this.fieldLabel
25038 cfg.cn.push(boxLabelCfg);
25043 // Roo.log(" no label && no align");
25044 cfg.cn = [ inputblock ] ;
25046 cfg.cn.push(boxLabelCfg);
25054 if(this.inputType != 'radio'){
25055 cfg.cn.push(hidden);
25063 * return the real input element.
25065 inputEl: function ()
25067 return this.el.select('input.roo-' + this.inputType,true).first();
25069 hiddenEl: function ()
25071 return this.el.select('input.roo-hidden-value',true).first();
25074 labelEl: function()
25076 return this.el.select('label.control-label',true).first();
25078 /* depricated... */
25082 return this.labelEl();
25085 boxLabelEl: function()
25087 return this.el.select('label.box-label',true).first();
25090 initEvents : function()
25092 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25094 this.inputEl().on('click', this.onClick, this);
25096 if (this.boxLabel) {
25097 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25100 this.startValue = this.getValue();
25103 Roo.bootstrap.form.CheckBox.register(this);
25107 onClick : function(e)
25109 if(this.fireEvent('click', this, e) !== false){
25110 this.setChecked(!this.checked);
25115 setChecked : function(state,suppressEvent)
25117 this.startValue = this.getValue();
25119 if(this.inputType == 'radio'){
25121 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25122 e.dom.checked = false;
25125 this.inputEl().dom.checked = true;
25127 this.inputEl().dom.value = this.inputValue;
25129 if(suppressEvent !== true){
25130 this.fireEvent('check', this, true);
25138 this.checked = state;
25140 this.inputEl().dom.checked = state;
25143 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25145 if(suppressEvent !== true){
25146 this.fireEvent('check', this, state);
25152 getValue : function()
25154 if(this.inputType == 'radio'){
25155 return this.getGroupValue();
25158 return this.hiddenEl().dom.value;
25162 getGroupValue : function()
25164 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25168 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25171 setValue : function(v,suppressEvent)
25173 if(this.inputType == 'radio'){
25174 this.setGroupValue(v, suppressEvent);
25178 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25183 setGroupValue : function(v, suppressEvent)
25185 this.startValue = this.getValue();
25187 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25188 e.dom.checked = false;
25190 if(e.dom.value == v){
25191 e.dom.checked = true;
25195 if(suppressEvent !== true){
25196 this.fireEvent('check', this, true);
25204 validate : function()
25206 if(this.getVisibilityEl().hasClass('hidden')){
25212 (this.inputType == 'radio' && this.validateRadio()) ||
25213 (this.inputType == 'checkbox' && this.validateCheckbox())
25219 this.markInvalid();
25223 validateRadio : function()
25225 if(this.getVisibilityEl().hasClass('hidden')){
25229 if(this.allowBlank){
25235 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25236 if(!e.dom.checked){
25248 validateCheckbox : function()
25251 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25252 //return (this.getValue() == this.inputValue) ? true : false;
25255 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25263 for(var i in group){
25264 if(group[i].el.isVisible(true)){
25272 for(var i in group){
25277 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25284 * Mark this field as valid
25286 markValid : function()
25290 this.fireEvent('valid', this);
25292 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25295 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25302 if(this.inputType == 'radio'){
25303 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25304 var fg = e.findParent('.form-group', false, true);
25305 if (Roo.bootstrap.version == 3) {
25306 fg.removeClass([_this.invalidClass, _this.validClass]);
25307 fg.addClass(_this.validClass);
25309 fg.removeClass(['is-valid', 'is-invalid']);
25310 fg.addClass('is-valid');
25318 var fg = this.el.findParent('.form-group', false, true);
25319 if (Roo.bootstrap.version == 3) {
25320 fg.removeClass([this.invalidClass, this.validClass]);
25321 fg.addClass(this.validClass);
25323 fg.removeClass(['is-valid', 'is-invalid']);
25324 fg.addClass('is-valid');
25329 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25335 for(var i in group){
25336 var fg = group[i].el.findParent('.form-group', false, true);
25337 if (Roo.bootstrap.version == 3) {
25338 fg.removeClass([this.invalidClass, this.validClass]);
25339 fg.addClass(this.validClass);
25341 fg.removeClass(['is-valid', 'is-invalid']);
25342 fg.addClass('is-valid');
25348 * Mark this field as invalid
25349 * @param {String} msg The validation message
25351 markInvalid : function(msg)
25353 if(this.allowBlank){
25359 this.fireEvent('invalid', this, msg);
25361 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25364 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25368 label.markInvalid();
25371 if(this.inputType == 'radio'){
25373 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25374 var fg = e.findParent('.form-group', false, true);
25375 if (Roo.bootstrap.version == 3) {
25376 fg.removeClass([_this.invalidClass, _this.validClass]);
25377 fg.addClass(_this.invalidClass);
25379 fg.removeClass(['is-invalid', 'is-valid']);
25380 fg.addClass('is-invalid');
25388 var fg = this.el.findParent('.form-group', false, true);
25389 if (Roo.bootstrap.version == 3) {
25390 fg.removeClass([_this.invalidClass, _this.validClass]);
25391 fg.addClass(_this.invalidClass);
25393 fg.removeClass(['is-invalid', 'is-valid']);
25394 fg.addClass('is-invalid');
25399 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25405 for(var i in group){
25406 var fg = group[i].el.findParent('.form-group', false, true);
25407 if (Roo.bootstrap.version == 3) {
25408 fg.removeClass([_this.invalidClass, _this.validClass]);
25409 fg.addClass(_this.invalidClass);
25411 fg.removeClass(['is-invalid', 'is-valid']);
25412 fg.addClass('is-invalid');
25418 clearInvalid : function()
25420 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25422 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25424 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25426 if (label && label.iconEl) {
25427 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25428 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25432 disable : function()
25434 if(this.inputType != 'radio'){
25435 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25442 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25443 _this.getActionEl().addClass(this.disabledClass);
25444 e.dom.disabled = true;
25448 this.disabled = true;
25449 this.fireEvent("disable", this);
25453 enable : function()
25455 if(this.inputType != 'radio'){
25456 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25463 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25464 _this.getActionEl().removeClass(this.disabledClass);
25465 e.dom.disabled = false;
25469 this.disabled = false;
25470 this.fireEvent("enable", this);
25474 setBoxLabel : function(v)
25479 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25485 Roo.apply(Roo.bootstrap.form.CheckBox, {
25490 * register a CheckBox Group
25491 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25493 register : function(checkbox)
25495 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25496 this.groups[checkbox.groupId] = {};
25499 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25503 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25507 * fetch a CheckBox Group based on the group ID
25508 * @param {string} the group ID
25509 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25511 get: function(groupId) {
25512 if (typeof(this.groups[groupId]) == 'undefined') {
25516 return this.groups[groupId] ;
25529 * @class Roo.bootstrap.form.Radio
25530 * @extends Roo.bootstrap.Component
25531 * Bootstrap Radio class
25532 * @cfg {String} boxLabel - the label associated
25533 * @cfg {String} value - the value of radio
25536 * Create a new Radio
25537 * @param {Object} config The config object
25539 Roo.bootstrap.form.Radio = function(config){
25540 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25544 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25550 getAutoCreate : function()
25554 cls : 'form-group radio',
25559 html : this.boxLabel
25567 initEvents : function()
25569 this.parent().register(this);
25571 this.el.on('click', this.onClick, this);
25575 onClick : function(e)
25577 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25578 this.setChecked(true);
25582 setChecked : function(state, suppressEvent)
25584 this.parent().setValue(this.value, suppressEvent);
25588 setBoxLabel : function(v)
25593 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25608 * @class Roo.bootstrap.form.SecurePass
25609 * @extends Roo.bootstrap.form.Input
25610 * Bootstrap SecurePass class
25614 * Create a new SecurePass
25615 * @param {Object} config The config object
25618 Roo.bootstrap.form.SecurePass = function (config) {
25619 // these go here, so the translation tool can replace them..
25621 PwdEmpty: "Please type a password, and then retype it to confirm.",
25622 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25623 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25624 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25625 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25626 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25627 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25628 TooWeak: "Your password is Too Weak."
25630 this.meterLabel = "Password strength:";
25631 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25632 this.meterClass = [
25633 "roo-password-meter-tooweak",
25634 "roo-password-meter-weak",
25635 "roo-password-meter-medium",
25636 "roo-password-meter-strong",
25637 "roo-password-meter-grey"
25642 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25645 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25647 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25649 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25650 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25651 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25652 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25653 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25654 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25655 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25665 * @cfg {String/Object} Label for the strength meter (defaults to
25666 * 'Password strength:')
25671 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25672 * ['Weak', 'Medium', 'Strong'])
25675 pwdStrengths: false,
25688 initEvents: function ()
25690 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25692 if (this.el.is('input[type=password]') && Roo.isSafari) {
25693 this.el.on('keydown', this.SafariOnKeyDown, this);
25696 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25699 onRender: function (ct, position)
25701 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25702 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25703 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25705 this.trigger.createChild({
25710 cls: 'roo-password-meter-grey col-xs-12',
25713 //width: this.meterWidth + 'px'
25717 cls: 'roo-password-meter-text'
25723 if (this.hideTrigger) {
25724 this.trigger.setDisplayed(false);
25726 this.setSize(this.width || '', this.height || '');
25729 onDestroy: function ()
25731 if (this.trigger) {
25732 this.trigger.removeAllListeners();
25733 this.trigger.remove();
25736 this.wrap.remove();
25738 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25741 checkStrength: function ()
25743 var pwd = this.inputEl().getValue();
25744 if (pwd == this._lastPwd) {
25749 if (this.ClientSideStrongPassword(pwd)) {
25751 } else if (this.ClientSideMediumPassword(pwd)) {
25753 } else if (this.ClientSideWeakPassword(pwd)) {
25759 Roo.log('strength1: ' + strength);
25761 //var pm = this.trigger.child('div/div/div').dom;
25762 var pm = this.trigger.child('div/div');
25763 pm.removeClass(this.meterClass);
25764 pm.addClass(this.meterClass[strength]);
25767 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25769 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25771 this._lastPwd = pwd;
25775 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25777 this._lastPwd = '';
25779 var pm = this.trigger.child('div/div');
25780 pm.removeClass(this.meterClass);
25781 pm.addClass('roo-password-meter-grey');
25784 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25787 this.inputEl().dom.type='password';
25790 validateValue: function (value)
25792 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25795 if (value.length == 0) {
25796 if (this.allowBlank) {
25797 this.clearInvalid();
25801 this.markInvalid(this.errors.PwdEmpty);
25802 this.errorMsg = this.errors.PwdEmpty;
25810 if (!value.match(/[\x21-\x7e]+/)) {
25811 this.markInvalid(this.errors.PwdBadChar);
25812 this.errorMsg = this.errors.PwdBadChar;
25815 if (value.length < 6) {
25816 this.markInvalid(this.errors.PwdShort);
25817 this.errorMsg = this.errors.PwdShort;
25820 if (value.length > 16) {
25821 this.markInvalid(this.errors.PwdLong);
25822 this.errorMsg = this.errors.PwdLong;
25826 if (this.ClientSideStrongPassword(value)) {
25828 } else if (this.ClientSideMediumPassword(value)) {
25830 } else if (this.ClientSideWeakPassword(value)) {
25837 if (strength < 2) {
25838 //this.markInvalid(this.errors.TooWeak);
25839 this.errorMsg = this.errors.TooWeak;
25844 console.log('strength2: ' + strength);
25846 //var pm = this.trigger.child('div/div/div').dom;
25848 var pm = this.trigger.child('div/div');
25849 pm.removeClass(this.meterClass);
25850 pm.addClass(this.meterClass[strength]);
25852 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25854 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25856 this.errorMsg = '';
25860 CharacterSetChecks: function (type)
25863 this.fResult = false;
25866 isctype: function (character, type)
25869 case this.kCapitalLetter:
25870 if (character >= 'A' && character <= 'Z') {
25875 case this.kSmallLetter:
25876 if (character >= 'a' && character <= 'z') {
25882 if (character >= '0' && character <= '9') {
25887 case this.kPunctuation:
25888 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25899 IsLongEnough: function (pwd, size)
25901 return !(pwd == null || isNaN(size) || pwd.length < size);
25904 SpansEnoughCharacterSets: function (word, nb)
25906 if (!this.IsLongEnough(word, nb))
25911 var characterSetChecks = new Array(
25912 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25913 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25916 for (var index = 0; index < word.length; ++index) {
25917 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25918 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25919 characterSetChecks[nCharSet].fResult = true;
25926 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25927 if (characterSetChecks[nCharSet].fResult) {
25932 if (nCharSets < nb) {
25938 ClientSideStrongPassword: function (pwd)
25940 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25943 ClientSideMediumPassword: function (pwd)
25945 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25948 ClientSideWeakPassword: function (pwd)
25950 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25953 })//<script type="text/javascript">
25956 * Based Ext JS Library 1.1.1
25957 * Copyright(c) 2006-2007, Ext JS, LLC.
25963 * @class Roo.HtmlEditorCore
25964 * @extends Roo.Component
25965 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25967 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25970 Roo.HtmlEditorCore = function(config){
25973 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25978 * @event initialize
25979 * Fires when the editor is fully initialized (including the iframe)
25980 * @param {Roo.HtmlEditorCore} this
25985 * Fires when the editor is first receives the focus. Any insertion must wait
25986 * until after this event.
25987 * @param {Roo.HtmlEditorCore} this
25991 * @event beforesync
25992 * Fires before the textarea is updated with content from the editor iframe. Return false
25993 * to cancel the sync.
25994 * @param {Roo.HtmlEditorCore} this
25995 * @param {String} html
25999 * @event beforepush
26000 * Fires before the iframe editor is updated with content from the textarea. Return false
26001 * to cancel the push.
26002 * @param {Roo.HtmlEditorCore} this
26003 * @param {String} html
26008 * Fires when the textarea is updated with content from the editor iframe.
26009 * @param {Roo.HtmlEditorCore} this
26010 * @param {String} html
26015 * Fires when the iframe editor is updated with content from the textarea.
26016 * @param {Roo.HtmlEditorCore} this
26017 * @param {String} html
26022 * @event editorevent
26023 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26024 * @param {Roo.HtmlEditorCore} this
26030 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26032 // defaults : white / black...
26033 this.applyBlacklists();
26040 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26044 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26050 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26055 * @cfg {Number} height (in pixels)
26059 * @cfg {Number} width (in pixels)
26064 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26067 stylesheets: false,
26070 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26072 allowComments: false,
26076 // private properties
26077 validationEvent : false,
26079 initialized : false,
26081 sourceEditMode : false,
26082 onFocus : Roo.emptyFn,
26084 hideMode:'offsets',
26088 // blacklist + whitelisted elements..
26095 * Protected method that will not generally be called directly. It
26096 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26097 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26099 getDocMarkup : function(){
26103 // inherit styels from page...??
26104 if (this.stylesheets === false) {
26106 Roo.get(document.head).select('style').each(function(node) {
26107 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26110 Roo.get(document.head).select('link').each(function(node) {
26111 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26114 } else if (!this.stylesheets.length) {
26116 st = '<style type="text/css">' +
26117 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26120 for (var i in this.stylesheets) {
26121 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26126 st += '<style type="text/css">' +
26127 'IMG { cursor: pointer } ' +
26130 var cls = 'roo-htmleditor-body';
26132 if(this.bodyCls.length){
26133 cls += ' ' + this.bodyCls;
26136 return '<html><head>' + st +
26137 //<style type="text/css">' +
26138 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26140 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26144 onRender : function(ct, position)
26147 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26148 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26151 this.el.dom.style.border = '0 none';
26152 this.el.dom.setAttribute('tabIndex', -1);
26153 this.el.addClass('x-hidden hide');
26157 if(Roo.isIE){ // fix IE 1px bogus margin
26158 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26162 this.frameId = Roo.id();
26166 var iframe = this.owner.wrap.createChild({
26168 cls: 'form-control', // bootstrap..
26170 name: this.frameId,
26171 frameBorder : 'no',
26172 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26177 this.iframe = iframe.dom;
26179 this.assignDocWin();
26181 this.doc.designMode = 'on';
26184 this.doc.write(this.getDocMarkup());
26188 var task = { // must defer to wait for browser to be ready
26190 //console.log("run task?" + this.doc.readyState);
26191 this.assignDocWin();
26192 if(this.doc.body || this.doc.readyState == 'complete'){
26194 this.doc.designMode="on";
26198 Roo.TaskMgr.stop(task);
26199 this.initEditor.defer(10, this);
26206 Roo.TaskMgr.start(task);
26211 onResize : function(w, h)
26213 Roo.log('resize: ' +w + ',' + h );
26214 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26218 if(typeof w == 'number'){
26220 this.iframe.style.width = w + 'px';
26222 if(typeof h == 'number'){
26224 this.iframe.style.height = h + 'px';
26226 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26233 * Toggles the editor between standard and source edit mode.
26234 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26236 toggleSourceEdit : function(sourceEditMode){
26238 this.sourceEditMode = sourceEditMode === true;
26240 if(this.sourceEditMode){
26242 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26245 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26246 //this.iframe.className = '';
26249 //this.setSize(this.owner.wrap.getSize());
26250 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26257 * Protected method that will not generally be called directly. If you need/want
26258 * custom HTML cleanup, this is the method you should override.
26259 * @param {String} html The HTML to be cleaned
26260 * return {String} The cleaned HTML
26262 cleanHtml : function(html){
26263 html = String(html);
26264 if(html.length > 5){
26265 if(Roo.isSafari){ // strip safari nonsense
26266 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26269 if(html == ' '){
26276 * HTML Editor -> Textarea
26277 * Protected method that will not generally be called directly. Syncs the contents
26278 * of the editor iframe with the textarea.
26280 syncValue : function(){
26281 if(this.initialized){
26282 var bd = (this.doc.body || this.doc.documentElement);
26283 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26284 var html = bd.innerHTML;
26286 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26287 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26289 html = '<div style="'+m[0]+'">' + html + '</div>';
26292 html = this.cleanHtml(html);
26293 // fix up the special chars.. normaly like back quotes in word...
26294 // however we do not want to do this with chinese..
26295 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26297 var cc = match.charCodeAt();
26299 // Get the character value, handling surrogate pairs
26300 if (match.length == 2) {
26301 // It's a surrogate pair, calculate the Unicode code point
26302 var high = match.charCodeAt(0) - 0xD800;
26303 var low = match.charCodeAt(1) - 0xDC00;
26304 cc = (high * 0x400) + low + 0x10000;
26306 (cc >= 0x4E00 && cc < 0xA000 ) ||
26307 (cc >= 0x3400 && cc < 0x4E00 ) ||
26308 (cc >= 0xf900 && cc < 0xfb00 )
26313 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26314 return "&#" + cc + ";";
26321 if(this.owner.fireEvent('beforesync', this, html) !== false){
26322 this.el.dom.value = html;
26323 this.owner.fireEvent('sync', this, html);
26329 * Protected method that will not generally be called directly. Pushes the value of the textarea
26330 * into the iframe editor.
26332 pushValue : function(){
26333 if(this.initialized){
26334 var v = this.el.dom.value.trim();
26336 // if(v.length < 1){
26340 if(this.owner.fireEvent('beforepush', this, v) !== false){
26341 var d = (this.doc.body || this.doc.documentElement);
26343 this.cleanUpPaste();
26344 this.el.dom.value = d.innerHTML;
26345 this.owner.fireEvent('push', this, v);
26351 deferFocus : function(){
26352 this.focus.defer(10, this);
26356 focus : function(){
26357 if(this.win && !this.sourceEditMode){
26364 assignDocWin: function()
26366 var iframe = this.iframe;
26369 this.doc = iframe.contentWindow.document;
26370 this.win = iframe.contentWindow;
26372 // if (!Roo.get(this.frameId)) {
26375 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26376 // this.win = Roo.get(this.frameId).dom.contentWindow;
26378 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26382 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26383 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26388 initEditor : function(){
26389 //console.log("INIT EDITOR");
26390 this.assignDocWin();
26394 this.doc.designMode="on";
26396 this.doc.write(this.getDocMarkup());
26399 var dbody = (this.doc.body || this.doc.documentElement);
26400 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26401 // this copies styles from the containing element into thsi one..
26402 // not sure why we need all of this..
26403 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26405 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26406 //ss['background-attachment'] = 'fixed'; // w3c
26407 dbody.bgProperties = 'fixed'; // ie
26408 //Roo.DomHelper.applyStyles(dbody, ss);
26409 Roo.EventManager.on(this.doc, {
26410 //'mousedown': this.onEditorEvent,
26411 'mouseup': this.onEditorEvent,
26412 'dblclick': this.onEditorEvent,
26413 'click': this.onEditorEvent,
26414 'keyup': this.onEditorEvent,
26419 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26421 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26422 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26424 this.initialized = true;
26426 this.owner.fireEvent('initialize', this);
26431 onDestroy : function(){
26437 //for (var i =0; i < this.toolbars.length;i++) {
26438 // // fixme - ask toolbars for heights?
26439 // this.toolbars[i].onDestroy();
26442 //this.wrap.dom.innerHTML = '';
26443 //this.wrap.remove();
26448 onFirstFocus : function(){
26450 this.assignDocWin();
26453 this.activated = true;
26456 if(Roo.isGecko){ // prevent silly gecko errors
26458 var s = this.win.getSelection();
26459 if(!s.focusNode || s.focusNode.nodeType != 3){
26460 var r = s.getRangeAt(0);
26461 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26466 this.execCmd('useCSS', true);
26467 this.execCmd('styleWithCSS', false);
26470 this.owner.fireEvent('activate', this);
26474 adjustFont: function(btn){
26475 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26476 //if(Roo.isSafari){ // safari
26479 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26480 if(Roo.isSafari){ // safari
26481 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26482 v = (v < 10) ? 10 : v;
26483 v = (v > 48) ? 48 : v;
26484 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26489 v = Math.max(1, v+adjust);
26491 this.execCmd('FontSize', v );
26494 onEditorEvent : function(e)
26496 this.owner.fireEvent('editorevent', this, e);
26497 // this.updateToolbar();
26498 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26501 insertTag : function(tg)
26503 // could be a bit smarter... -> wrap the current selected tRoo..
26504 if (tg.toLowerCase() == 'span' ||
26505 tg.toLowerCase() == 'code' ||
26506 tg.toLowerCase() == 'sup' ||
26507 tg.toLowerCase() == 'sub'
26510 range = this.createRange(this.getSelection());
26511 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26512 wrappingNode.appendChild(range.extractContents());
26513 range.insertNode(wrappingNode);
26520 this.execCmd("formatblock", tg);
26524 insertText : function(txt)
26528 var range = this.createRange();
26529 range.deleteContents();
26530 //alert(Sender.getAttribute('label'));
26532 range.insertNode(this.doc.createTextNode(txt));
26538 * Executes a Midas editor command on the editor document and performs necessary focus and
26539 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26540 * @param {String} cmd The Midas command
26541 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26543 relayCmd : function(cmd, value){
26545 this.execCmd(cmd, value);
26546 this.owner.fireEvent('editorevent', this);
26547 //this.updateToolbar();
26548 this.owner.deferFocus();
26552 * Executes a Midas editor command directly on the editor document.
26553 * For visual commands, you should use {@link #relayCmd} instead.
26554 * <b>This should only be called after the editor is initialized.</b>
26555 * @param {String} cmd The Midas command
26556 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26558 execCmd : function(cmd, value){
26559 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26566 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26568 * @param {String} text | dom node..
26570 insertAtCursor : function(text)
26573 if(!this.activated){
26579 var r = this.doc.selection.createRange();
26590 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26594 // from jquery ui (MIT licenced)
26596 var win = this.win;
26598 if (win.getSelection && win.getSelection().getRangeAt) {
26599 range = win.getSelection().getRangeAt(0);
26600 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26601 range.insertNode(node);
26602 } else if (win.document.selection && win.document.selection.createRange) {
26603 // no firefox support
26604 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26605 win.document.selection.createRange().pasteHTML(txt);
26607 // no firefox support
26608 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26609 this.execCmd('InsertHTML', txt);
26618 mozKeyPress : function(e){
26620 var c = e.getCharCode(), cmd;
26623 c = String.fromCharCode(c).toLowerCase();
26637 this.cleanUpPaste.defer(100, this);
26645 e.preventDefault();
26653 fixKeys : function(){ // load time branching for fastest keydown performance
26655 return function(e){
26656 var k = e.getKey(), r;
26659 r = this.doc.selection.createRange();
26662 r.pasteHTML('    ');
26669 r = this.doc.selection.createRange();
26671 var target = r.parentElement();
26672 if(!target || target.tagName.toLowerCase() != 'li'){
26674 r.pasteHTML('<br />');
26680 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26681 this.cleanUpPaste.defer(100, this);
26687 }else if(Roo.isOpera){
26688 return function(e){
26689 var k = e.getKey();
26693 this.execCmd('InsertHTML','    ');
26696 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26697 this.cleanUpPaste.defer(100, this);
26702 }else if(Roo.isSafari){
26703 return function(e){
26704 var k = e.getKey();
26708 this.execCmd('InsertText','\t');
26712 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26713 this.cleanUpPaste.defer(100, this);
26721 getAllAncestors: function()
26723 var p = this.getSelectedNode();
26726 a.push(p); // push blank onto stack..
26727 p = this.getParentElement();
26731 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26735 a.push(this.doc.body);
26739 lastSelNode : false,
26742 getSelection : function()
26744 this.assignDocWin();
26745 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26748 getSelectedNode: function()
26750 // this may only work on Gecko!!!
26752 // should we cache this!!!!
26757 var range = this.createRange(this.getSelection()).cloneRange();
26760 var parent = range.parentElement();
26762 var testRange = range.duplicate();
26763 testRange.moveToElementText(parent);
26764 if (testRange.inRange(range)) {
26767 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26770 parent = parent.parentElement;
26775 // is ancestor a text element.
26776 var ac = range.commonAncestorContainer;
26777 if (ac.nodeType == 3) {
26778 ac = ac.parentNode;
26781 var ar = ac.childNodes;
26784 var other_nodes = [];
26785 var has_other_nodes = false;
26786 for (var i=0;i<ar.length;i++) {
26787 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26790 // fullly contained node.
26792 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26797 // probably selected..
26798 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26799 other_nodes.push(ar[i]);
26803 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26808 has_other_nodes = true;
26810 if (!nodes.length && other_nodes.length) {
26811 nodes= other_nodes;
26813 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26819 createRange: function(sel)
26821 // this has strange effects when using with
26822 // top toolbar - not sure if it's a great idea.
26823 //this.editor.contentWindow.focus();
26824 if (typeof sel != "undefined") {
26826 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26828 return this.doc.createRange();
26831 return this.doc.createRange();
26834 getParentElement: function()
26837 this.assignDocWin();
26838 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26840 var range = this.createRange(sel);
26843 var p = range.commonAncestorContainer;
26844 while (p.nodeType == 3) { // text node
26855 * Range intersection.. the hard stuff...
26859 * [ -- selected range --- ]
26863 * if end is before start or hits it. fail.
26864 * if start is after end or hits it fail.
26866 * if either hits (but other is outside. - then it's not
26872 // @see http://www.thismuchiknow.co.uk/?p=64.
26873 rangeIntersectsNode : function(range, node)
26875 var nodeRange = node.ownerDocument.createRange();
26877 nodeRange.selectNode(node);
26879 nodeRange.selectNodeContents(node);
26882 var rangeStartRange = range.cloneRange();
26883 rangeStartRange.collapse(true);
26885 var rangeEndRange = range.cloneRange();
26886 rangeEndRange.collapse(false);
26888 var nodeStartRange = nodeRange.cloneRange();
26889 nodeStartRange.collapse(true);
26891 var nodeEndRange = nodeRange.cloneRange();
26892 nodeEndRange.collapse(false);
26894 return rangeStartRange.compareBoundaryPoints(
26895 Range.START_TO_START, nodeEndRange) == -1 &&
26896 rangeEndRange.compareBoundaryPoints(
26897 Range.START_TO_START, nodeStartRange) == 1;
26901 rangeCompareNode : function(range, node)
26903 var nodeRange = node.ownerDocument.createRange();
26905 nodeRange.selectNode(node);
26907 nodeRange.selectNodeContents(node);
26911 range.collapse(true);
26913 nodeRange.collapse(true);
26915 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26916 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26918 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26920 var nodeIsBefore = ss == 1;
26921 var nodeIsAfter = ee == -1;
26923 if (nodeIsBefore && nodeIsAfter) {
26926 if (!nodeIsBefore && nodeIsAfter) {
26927 return 1; //right trailed.
26930 if (nodeIsBefore && !nodeIsAfter) {
26931 return 2; // left trailed.
26937 // private? - in a new class?
26938 cleanUpPaste : function()
26940 // cleans up the whole document..
26941 Roo.log('cleanuppaste');
26943 this.cleanUpChildren(this.doc.body);
26944 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26945 if (clean != this.doc.body.innerHTML) {
26946 this.doc.body.innerHTML = clean;
26951 cleanWordChars : function(input) {// change the chars to hex code
26952 var he = Roo.HtmlEditorCore;
26954 var output = input;
26955 Roo.each(he.swapCodes, function(sw) {
26956 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26958 output = output.replace(swapper, sw[1]);
26965 cleanUpChildren : function (n)
26967 if (!n.childNodes.length) {
26970 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26971 this.cleanUpChild(n.childNodes[i]);
26978 cleanUpChild : function (node)
26981 //console.log(node);
26982 if (node.nodeName == "#text") {
26983 // clean up silly Windows -- stuff?
26986 if (node.nodeName == "#comment") {
26987 if (!this.allowComments) {
26988 node.parentNode.removeChild(node);
26990 // clean up silly Windows -- stuff?
26993 var lcname = node.tagName.toLowerCase();
26994 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26995 // whitelist of tags..
26997 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26999 node.parentNode.removeChild(node);
27004 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27006 // spans with no attributes - just remove them..
27007 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27008 remove_keep_children = true;
27011 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27012 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27014 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27015 // remove_keep_children = true;
27018 if (remove_keep_children) {
27019 this.cleanUpChildren(node);
27020 // inserts everything just before this node...
27021 while (node.childNodes.length) {
27022 var cn = node.childNodes[0];
27023 node.removeChild(cn);
27024 node.parentNode.insertBefore(cn, node);
27026 node.parentNode.removeChild(node);
27030 if (!node.attributes || !node.attributes.length) {
27035 this.cleanUpChildren(node);
27039 function cleanAttr(n,v)
27042 if (v.match(/^\./) || v.match(/^\//)) {
27045 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27048 if (v.match(/^#/)) {
27051 if (v.match(/^\{/)) { // allow template editing.
27054 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27055 node.removeAttribute(n);
27059 var cwhite = this.cwhite;
27060 var cblack = this.cblack;
27062 function cleanStyle(n,v)
27064 if (v.match(/expression/)) { //XSS?? should we even bother..
27065 node.removeAttribute(n);
27069 var parts = v.split(/;/);
27072 Roo.each(parts, function(p) {
27073 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27077 var l = p.split(':').shift().replace(/\s+/g,'');
27078 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27080 if ( cwhite.length && cblack.indexOf(l) > -1) {
27081 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27082 //node.removeAttribute(n);
27086 // only allow 'c whitelisted system attributes'
27087 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27088 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27089 //node.removeAttribute(n);
27099 if (clean.length) {
27100 node.setAttribute(n, clean.join(';'));
27102 node.removeAttribute(n);
27108 for (var i = node.attributes.length-1; i > -1 ; i--) {
27109 var a = node.attributes[i];
27112 if (a.name.toLowerCase().substr(0,2)=='on') {
27113 node.removeAttribute(a.name);
27116 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27117 node.removeAttribute(a.name);
27120 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27121 cleanAttr(a.name,a.value); // fixme..
27124 if (a.name == 'style') {
27125 cleanStyle(a.name,a.value);
27128 /// clean up MS crap..
27129 // tecnically this should be a list of valid class'es..
27132 if (a.name == 'class') {
27133 if (a.value.match(/^Mso/)) {
27134 node.removeAttribute('class');
27137 if (a.value.match(/^body$/)) {
27138 node.removeAttribute('class');
27149 this.cleanUpChildren(node);
27155 * Clean up MS wordisms...
27157 cleanWord : function(node)
27160 this.cleanWord(this.doc.body);
27165 node.nodeName == 'SPAN' &&
27166 !node.hasAttributes() &&
27167 node.childNodes.length == 1 &&
27168 node.firstChild.nodeName == "#text"
27170 var textNode = node.firstChild;
27171 node.removeChild(textNode);
27172 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27173 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27175 node.parentNode.insertBefore(textNode, node);
27176 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27177 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27179 node.parentNode.removeChild(node);
27182 if (node.nodeName == "#text") {
27183 // clean up silly Windows -- stuff?
27186 if (node.nodeName == "#comment") {
27187 node.parentNode.removeChild(node);
27188 // clean up silly Windows -- stuff?
27192 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27193 node.parentNode.removeChild(node);
27196 //Roo.log(node.tagName);
27197 // remove - but keep children..
27198 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27199 //Roo.log('-- removed');
27200 while (node.childNodes.length) {
27201 var cn = node.childNodes[0];
27202 node.removeChild(cn);
27203 node.parentNode.insertBefore(cn, node);
27204 // move node to parent - and clean it..
27205 this.cleanWord(cn);
27207 node.parentNode.removeChild(node);
27208 /// no need to iterate chidlren = it's got none..
27209 //this.iterateChildren(node, this.cleanWord);
27213 if (node.className.length) {
27215 var cn = node.className.split(/\W+/);
27217 Roo.each(cn, function(cls) {
27218 if (cls.match(/Mso[a-zA-Z]+/)) {
27223 node.className = cna.length ? cna.join(' ') : '';
27225 node.removeAttribute("class");
27229 if (node.hasAttribute("lang")) {
27230 node.removeAttribute("lang");
27233 if (node.hasAttribute("style")) {
27235 var styles = node.getAttribute("style").split(";");
27237 Roo.each(styles, function(s) {
27238 if (!s.match(/:/)) {
27241 var kv = s.split(":");
27242 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27245 // what ever is left... we allow.
27248 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27249 if (!nstyle.length) {
27250 node.removeAttribute('style');
27253 this.iterateChildren(node, this.cleanWord);
27259 * iterateChildren of a Node, calling fn each time, using this as the scole..
27260 * @param {DomNode} node node to iterate children of.
27261 * @param {Function} fn method of this class to call on each item.
27263 iterateChildren : function(node, fn)
27265 if (!node.childNodes.length) {
27268 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27269 fn.call(this, node.childNodes[i])
27275 * cleanTableWidths.
27277 * Quite often pasting from word etc.. results in tables with column and widths.
27278 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27281 cleanTableWidths : function(node)
27286 this.cleanTableWidths(this.doc.body);
27291 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27294 Roo.log(node.tagName);
27295 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27296 this.iterateChildren(node, this.cleanTableWidths);
27299 if (node.hasAttribute('width')) {
27300 node.removeAttribute('width');
27304 if (node.hasAttribute("style")) {
27307 var styles = node.getAttribute("style").split(";");
27309 Roo.each(styles, function(s) {
27310 if (!s.match(/:/)) {
27313 var kv = s.split(":");
27314 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27317 // what ever is left... we allow.
27320 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27321 if (!nstyle.length) {
27322 node.removeAttribute('style');
27326 this.iterateChildren(node, this.cleanTableWidths);
27334 domToHTML : function(currentElement, depth, nopadtext) {
27336 depth = depth || 0;
27337 nopadtext = nopadtext || false;
27339 if (!currentElement) {
27340 return this.domToHTML(this.doc.body);
27343 //Roo.log(currentElement);
27345 var allText = false;
27346 var nodeName = currentElement.nodeName;
27347 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27349 if (nodeName == '#text') {
27351 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27356 if (nodeName != 'BODY') {
27359 // Prints the node tagName, such as <A>, <IMG>, etc
27362 for(i = 0; i < currentElement.attributes.length;i++) {
27364 var aname = currentElement.attributes.item(i).name;
27365 if (!currentElement.attributes.item(i).value.length) {
27368 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27371 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27380 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27383 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27388 // Traverse the tree
27390 var currentElementChild = currentElement.childNodes.item(i);
27391 var allText = true;
27392 var innerHTML = '';
27394 while (currentElementChild) {
27395 // Formatting code (indent the tree so it looks nice on the screen)
27396 var nopad = nopadtext;
27397 if (lastnode == 'SPAN') {
27401 if (currentElementChild.nodeName == '#text') {
27402 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27403 toadd = nopadtext ? toadd : toadd.trim();
27404 if (!nopad && toadd.length > 80) {
27405 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27407 innerHTML += toadd;
27410 currentElementChild = currentElement.childNodes.item(i);
27416 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27418 // Recursively traverse the tree structure of the child node
27419 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27420 lastnode = currentElementChild.nodeName;
27422 currentElementChild=currentElement.childNodes.item(i);
27428 // The remaining code is mostly for formatting the tree
27429 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27434 ret+= "</"+tagName+">";
27440 applyBlacklists : function()
27442 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27443 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27447 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27448 if (b.indexOf(tag) > -1) {
27451 this.white.push(tag);
27455 Roo.each(w, function(tag) {
27456 if (b.indexOf(tag) > -1) {
27459 if (this.white.indexOf(tag) > -1) {
27462 this.white.push(tag);
27467 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27468 if (w.indexOf(tag) > -1) {
27471 this.black.push(tag);
27475 Roo.each(b, function(tag) {
27476 if (w.indexOf(tag) > -1) {
27479 if (this.black.indexOf(tag) > -1) {
27482 this.black.push(tag);
27487 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27488 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27492 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27493 if (b.indexOf(tag) > -1) {
27496 this.cwhite.push(tag);
27500 Roo.each(w, function(tag) {
27501 if (b.indexOf(tag) > -1) {
27504 if (this.cwhite.indexOf(tag) > -1) {
27507 this.cwhite.push(tag);
27512 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27513 if (w.indexOf(tag) > -1) {
27516 this.cblack.push(tag);
27520 Roo.each(b, function(tag) {
27521 if (w.indexOf(tag) > -1) {
27524 if (this.cblack.indexOf(tag) > -1) {
27527 this.cblack.push(tag);
27532 setStylesheets : function(stylesheets)
27534 if(typeof(stylesheets) == 'string'){
27535 Roo.get(this.iframe.contentDocument.head).createChild({
27537 rel : 'stylesheet',
27546 Roo.each(stylesheets, function(s) {
27551 Roo.get(_this.iframe.contentDocument.head).createChild({
27553 rel : 'stylesheet',
27562 removeStylesheets : function()
27566 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27571 setStyle : function(style)
27573 Roo.get(this.iframe.contentDocument.head).createChild({
27582 // hide stuff that is not compatible
27596 * @event specialkey
27600 * @cfg {String} fieldClass @hide
27603 * @cfg {String} focusClass @hide
27606 * @cfg {String} autoCreate @hide
27609 * @cfg {String} inputType @hide
27612 * @cfg {String} invalidClass @hide
27615 * @cfg {String} invalidText @hide
27618 * @cfg {String} msgFx @hide
27621 * @cfg {String} validateOnBlur @hide
27625 Roo.HtmlEditorCore.white = [
27626 'area', 'br', 'img', 'input', 'hr', 'wbr',
27628 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27629 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27630 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27631 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27632 'table', 'ul', 'xmp',
27634 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27637 'dir', 'menu', 'ol', 'ul', 'dl',
27643 Roo.HtmlEditorCore.black = [
27644 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27646 'base', 'basefont', 'bgsound', 'blink', 'body',
27647 'frame', 'frameset', 'head', 'html', 'ilayer',
27648 'iframe', 'layer', 'link', 'meta', 'object',
27649 'script', 'style' ,'title', 'xml' // clean later..
27651 Roo.HtmlEditorCore.clean = [
27652 'script', 'style', 'title', 'xml'
27654 Roo.HtmlEditorCore.remove = [
27659 Roo.HtmlEditorCore.ablack = [
27663 Roo.HtmlEditorCore.aclean = [
27664 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27668 Roo.HtmlEditorCore.pwhite= [
27669 'http', 'https', 'mailto'
27672 // white listed style attributes.
27673 Roo.HtmlEditorCore.cwhite= [
27674 // 'text-align', /// default is to allow most things..
27680 // black listed style attributes.
27681 Roo.HtmlEditorCore.cblack= [
27682 // 'font-size' -- this can be set by the project
27686 Roo.HtmlEditorCore.swapCodes =[
27687 [ 8211, "–" ],
27688 [ 8212, "—" ],
27705 * @class Roo.bootstrap.form.HtmlEditor
27706 * @extends Roo.bootstrap.form.TextArea
27707 * Bootstrap HtmlEditor class
27710 * Create a new HtmlEditor
27711 * @param {Object} config The config object
27714 Roo.bootstrap.form.HtmlEditor = function(config){
27715 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27716 if (!this.toolbars) {
27717 this.toolbars = [];
27720 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27723 * @event initialize
27724 * Fires when the editor is fully initialized (including the iframe)
27725 * @param {HtmlEditor} this
27730 * Fires when the editor is first receives the focus. Any insertion must wait
27731 * until after this event.
27732 * @param {HtmlEditor} this
27736 * @event beforesync
27737 * Fires before the textarea is updated with content from the editor iframe. Return false
27738 * to cancel the sync.
27739 * @param {HtmlEditor} this
27740 * @param {String} html
27744 * @event beforepush
27745 * Fires before the iframe editor is updated with content from the textarea. Return false
27746 * to cancel the push.
27747 * @param {HtmlEditor} this
27748 * @param {String} html
27753 * Fires when the textarea is updated with content from the editor iframe.
27754 * @param {HtmlEditor} this
27755 * @param {String} html
27760 * Fires when the iframe editor is updated with content from the textarea.
27761 * @param {HtmlEditor} this
27762 * @param {String} html
27766 * @event editmodechange
27767 * Fires when the editor switches edit modes
27768 * @param {HtmlEditor} this
27769 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27771 editmodechange: true,
27773 * @event editorevent
27774 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27775 * @param {HtmlEditor} this
27779 * @event firstfocus
27780 * Fires when on first focus - needed by toolbars..
27781 * @param {HtmlEditor} this
27786 * Auto save the htmlEditor value as a file into Events
27787 * @param {HtmlEditor} this
27791 * @event savedpreview
27792 * preview the saved version of htmlEditor
27793 * @param {HtmlEditor} this
27800 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27804 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27809 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27814 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27819 * @cfg {Number} height (in pixels)
27823 * @cfg {Number} width (in pixels)
27828 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27831 stylesheets: false,
27836 // private properties
27837 validationEvent : false,
27839 initialized : false,
27842 onFocus : Roo.emptyFn,
27844 hideMode:'offsets',
27846 tbContainer : false,
27850 toolbarContainer :function() {
27851 return this.wrap.select('.x-html-editor-tb',true).first();
27855 * Protected method that will not generally be called directly. It
27856 * is called when the editor creates its toolbar. Override this method if you need to
27857 * add custom toolbar buttons.
27858 * @param {HtmlEditor} editor
27860 createToolbar : function(){
27861 Roo.log('renewing');
27862 Roo.log("create toolbars");
27864 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27865 this.toolbars[0].render(this.toolbarContainer());
27869 // if (!editor.toolbars || !editor.toolbars.length) {
27870 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27873 // for (var i =0 ; i < editor.toolbars.length;i++) {
27874 // editor.toolbars[i] = Roo.factory(
27875 // typeof(editor.toolbars[i]) == 'string' ?
27876 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27877 // Roo.bootstrap.form.HtmlEditor);
27878 // editor.toolbars[i].init(editor);
27884 onRender : function(ct, position)
27886 // Roo.log("Call onRender: " + this.xtype);
27888 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27890 this.wrap = this.inputEl().wrap({
27891 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27894 this.editorcore.onRender(ct, position);
27896 if (this.resizable) {
27897 this.resizeEl = new Roo.Resizable(this.wrap, {
27901 minHeight : this.height,
27902 height: this.height,
27903 handles : this.resizable,
27906 resize : function(r, w, h) {
27907 _t.onResize(w,h); // -something
27913 this.createToolbar(this);
27916 if(!this.width && this.resizable){
27917 this.setSize(this.wrap.getSize());
27919 if (this.resizeEl) {
27920 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27921 // should trigger onReize..
27927 onResize : function(w, h)
27929 Roo.log('resize: ' +w + ',' + h );
27930 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27934 if(this.inputEl() ){
27935 if(typeof w == 'number'){
27936 var aw = w - this.wrap.getFrameWidth('lr');
27937 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27940 if(typeof h == 'number'){
27941 var tbh = -11; // fixme it needs to tool bar size!
27942 for (var i =0; i < this.toolbars.length;i++) {
27943 // fixme - ask toolbars for heights?
27944 tbh += this.toolbars[i].el.getHeight();
27945 //if (this.toolbars[i].footer) {
27946 // tbh += this.toolbars[i].footer.el.getHeight();
27954 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27955 ah -= 5; // knock a few pixes off for look..
27956 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27960 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27961 this.editorcore.onResize(ew,eh);
27966 * Toggles the editor between standard and source edit mode.
27967 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27969 toggleSourceEdit : function(sourceEditMode)
27971 this.editorcore.toggleSourceEdit(sourceEditMode);
27973 if(this.editorcore.sourceEditMode){
27974 Roo.log('editor - showing textarea');
27977 // Roo.log(this.syncValue());
27979 this.inputEl().removeClass(['hide', 'x-hidden']);
27980 this.inputEl().dom.removeAttribute('tabIndex');
27981 this.inputEl().focus();
27983 Roo.log('editor - hiding textarea');
27985 // Roo.log(this.pushValue());
27988 this.inputEl().addClass(['hide', 'x-hidden']);
27989 this.inputEl().dom.setAttribute('tabIndex', -1);
27990 //this.deferFocus();
27993 if(this.resizable){
27994 this.setSize(this.wrap.getSize());
27997 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28000 // private (for BoxComponent)
28001 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28003 // private (for BoxComponent)
28004 getResizeEl : function(){
28008 // private (for BoxComponent)
28009 getPositionEl : function(){
28014 initEvents : function(){
28015 this.originalValue = this.getValue();
28019 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28022 // markInvalid : Roo.emptyFn,
28024 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28027 // clearInvalid : Roo.emptyFn,
28029 setValue : function(v){
28030 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28031 this.editorcore.pushValue();
28036 deferFocus : function(){
28037 this.focus.defer(10, this);
28041 focus : function(){
28042 this.editorcore.focus();
28048 onDestroy : function(){
28054 for (var i =0; i < this.toolbars.length;i++) {
28055 // fixme - ask toolbars for heights?
28056 this.toolbars[i].onDestroy();
28059 this.wrap.dom.innerHTML = '';
28060 this.wrap.remove();
28065 onFirstFocus : function(){
28066 //Roo.log("onFirstFocus");
28067 this.editorcore.onFirstFocus();
28068 for (var i =0; i < this.toolbars.length;i++) {
28069 this.toolbars[i].onFirstFocus();
28075 syncValue : function()
28077 this.editorcore.syncValue();
28080 pushValue : function()
28082 this.editorcore.pushValue();
28086 // hide stuff that is not compatible
28100 * @event specialkey
28104 * @cfg {String} fieldClass @hide
28107 * @cfg {String} focusClass @hide
28110 * @cfg {String} autoCreate @hide
28113 * @cfg {String} inputType @hide
28117 * @cfg {String} invalidText @hide
28120 * @cfg {String} msgFx @hide
28123 * @cfg {String} validateOnBlur @hide
28132 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28134 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28135 * @parent Roo.bootstrap.form.HtmlEditor
28136 * @extends Roo.bootstrap.nav.Simplebar
28142 new Roo.bootstrap.form.HtmlEditor({
28145 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28146 disable : { fonts: 1 , format: 1, ..., ... , ...],
28152 * @cfg {Object} disable List of elements to disable..
28153 * @cfg {Array} btns List of additional buttons.
28157 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28160 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28163 Roo.apply(this, config);
28165 // default disabled, based on 'good practice'..
28166 this.disable = this.disable || {};
28167 Roo.applyIf(this.disable, {
28170 specialElements : true
28172 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28174 this.editor = config.editor;
28175 this.editorcore = config.editor.editorcore;
28177 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28179 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28180 // dont call parent... till later.
28182 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28187 editorcore : false,
28192 "h1","h2","h3","h4","h5","h6",
28194 "abbr", "acronym", "address", "cite", "samp", "var",
28198 onRender : function(ct, position)
28200 // Roo.log("Call onRender: " + this.xtype);
28202 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28204 this.el.dom.style.marginBottom = '0';
28206 var editorcore = this.editorcore;
28207 var editor= this.editor;
28210 var btn = function(id,cmd , toggle, handler, html){
28212 var event = toggle ? 'toggle' : 'click';
28217 xns: Roo.bootstrap,
28221 enableToggle:toggle !== false,
28223 pressed : toggle ? false : null,
28226 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28227 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28233 // var cb_box = function...
28238 xns: Roo.bootstrap,
28243 xns: Roo.bootstrap,
28247 Roo.each(this.formats, function(f) {
28248 style.menu.items.push({
28250 xns: Roo.bootstrap,
28251 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28256 editorcore.insertTag(this.tagname);
28263 children.push(style);
28265 btn('bold',false,true);
28266 btn('italic',false,true);
28267 btn('align-left', 'justifyleft',true);
28268 btn('align-center', 'justifycenter',true);
28269 btn('align-right' , 'justifyright',true);
28270 btn('link', false, false, function(btn) {
28271 //Roo.log("create link?");
28272 var url = prompt(this.createLinkText, this.defaultLinkValue);
28273 if(url && url != 'http:/'+'/'){
28274 this.editorcore.relayCmd('createlink', url);
28277 btn('list','insertunorderedlist',true);
28278 btn('pencil', false,true, function(btn){
28280 this.toggleSourceEdit(btn.pressed);
28283 if (this.editor.btns.length > 0) {
28284 for (var i = 0; i<this.editor.btns.length; i++) {
28285 children.push(this.editor.btns[i]);
28293 xns: Roo.bootstrap,
28298 xns: Roo.bootstrap,
28303 cog.menu.items.push({
28305 xns: Roo.bootstrap,
28306 html : Clean styles,
28311 editorcore.insertTag(this.tagname);
28320 this.xtype = 'NavSimplebar';
28322 for(var i=0;i< children.length;i++) {
28324 this.buttons.add(this.addxtypeChild(children[i]));
28328 editor.on('editorevent', this.updateToolbar, this);
28330 onBtnClick : function(id)
28332 this.editorcore.relayCmd(id);
28333 this.editorcore.focus();
28337 * Protected method that will not generally be called directly. It triggers
28338 * a toolbar update by reading the markup state of the current selection in the editor.
28340 updateToolbar: function(){
28342 if(!this.editorcore.activated){
28343 this.editor.onFirstFocus(); // is this neeed?
28347 var btns = this.buttons;
28348 var doc = this.editorcore.doc;
28349 btns.get('bold').setActive(doc.queryCommandState('bold'));
28350 btns.get('italic').setActive(doc.queryCommandState('italic'));
28351 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28353 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28354 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28355 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28357 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28358 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28361 var ans = this.editorcore.getAllAncestors();
28362 if (this.formatCombo) {
28365 var store = this.formatCombo.store;
28366 this.formatCombo.setValue("");
28367 for (var i =0; i < ans.length;i++) {
28368 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28370 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28378 // hides menus... - so this cant be on a menu...
28379 Roo.bootstrap.MenuMgr.hideAll();
28381 Roo.bootstrap.menu.Manager.hideAll();
28382 //this.editorsyncValue();
28384 onFirstFocus: function() {
28385 this.buttons.each(function(item){
28389 toggleSourceEdit : function(sourceEditMode){
28392 if(sourceEditMode){
28393 Roo.log("disabling buttons");
28394 this.buttons.each( function(item){
28395 if(item.cmd != 'pencil'){
28401 Roo.log("enabling buttons");
28402 if(this.editorcore.initialized){
28403 this.buttons.each( function(item){
28409 Roo.log("calling toggole on editor");
28410 // tell the editor that it's been pressed..
28411 this.editor.toggleSourceEdit(sourceEditMode);
28425 * @class Roo.bootstrap.form.Markdown
28426 * @extends Roo.bootstrap.form.TextArea
28427 * Bootstrap Showdown editable area
28428 * @cfg {string} content
28431 * Create a new Showdown
28434 Roo.bootstrap.form.Markdown = function(config){
28435 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28439 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28443 initEvents : function()
28446 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28447 this.markdownEl = this.el.createChild({
28448 cls : 'roo-markdown-area'
28450 this.inputEl().addClass('d-none');
28451 if (this.getValue() == '') {
28452 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28455 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28457 this.markdownEl.on('click', this.toggleTextEdit, this);
28458 this.on('blur', this.toggleTextEdit, this);
28459 this.on('specialkey', this.resizeTextArea, this);
28462 toggleTextEdit : function()
28464 var sh = this.markdownEl.getHeight();
28465 this.inputEl().addClass('d-none');
28466 this.markdownEl.addClass('d-none');
28467 if (!this.editing) {
28469 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28470 this.inputEl().removeClass('d-none');
28471 this.inputEl().focus();
28472 this.editing = true;
28475 // show showdown...
28476 this.updateMarkdown();
28477 this.markdownEl.removeClass('d-none');
28478 this.editing = false;
28481 updateMarkdown : function()
28483 if (this.getValue() == '') {
28484 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28488 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28491 resizeTextArea: function () {
28494 Roo.log([sh, this.getValue().split("\n").length * 30]);
28495 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28497 setValue : function(val)
28499 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28500 if (!this.editing) {
28501 this.updateMarkdown();
28507 if (!this.editing) {
28508 this.toggleTextEdit();
28516 * Ext JS Library 1.1.1
28517 * Copyright(c) 2006-2007, Ext JS, LLC.
28519 * Originally Released Under LGPL - original licence link has changed is not relivant.
28522 * <script type="text/javascript">
28526 * @class Roo.bootstrap.PagingToolbar
28527 * @extends Roo.bootstrap.nav.Simplebar
28528 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28530 * Create a new PagingToolbar
28531 * @param {Object} config The config object
28532 * @param {Roo.data.Store} store
28534 Roo.bootstrap.PagingToolbar = function(config)
28536 // old args format still supported... - xtype is prefered..
28537 // created from xtype...
28539 this.ds = config.dataSource;
28541 if (config.store && !this.ds) {
28542 this.store= Roo.factory(config.store, Roo.data);
28543 this.ds = this.store;
28544 this.ds.xmodule = this.xmodule || false;
28547 this.toolbarItems = [];
28548 if (config.items) {
28549 this.toolbarItems = config.items;
28552 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28557 this.bind(this.ds);
28560 if (Roo.bootstrap.version == 4) {
28561 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28563 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28568 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28570 * @cfg {Roo.bootstrap.Button} buttons[]
28571 * Buttons for the toolbar
28574 * @cfg {Roo.data.Store} store
28575 * The underlying data store providing the paged data
28578 * @cfg {String/HTMLElement/Element} container
28579 * container The id or element that will contain the toolbar
28582 * @cfg {Boolean} displayInfo
28583 * True to display the displayMsg (defaults to false)
28586 * @cfg {Number} pageSize
28587 * The number of records to display per page (defaults to 20)
28591 * @cfg {String} displayMsg
28592 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28594 displayMsg : 'Displaying {0} - {1} of {2}',
28596 * @cfg {String} emptyMsg
28597 * The message to display when no records are found (defaults to "No data to display")
28599 emptyMsg : 'No data to display',
28601 * Customizable piece of the default paging text (defaults to "Page")
28604 beforePageText : "Page",
28606 * Customizable piece of the default paging text (defaults to "of %0")
28609 afterPageText : "of {0}",
28611 * Customizable piece of the default paging text (defaults to "First Page")
28614 firstText : "First Page",
28616 * Customizable piece of the default paging text (defaults to "Previous Page")
28619 prevText : "Previous Page",
28621 * Customizable piece of the default paging text (defaults to "Next Page")
28624 nextText : "Next Page",
28626 * Customizable piece of the default paging text (defaults to "Last Page")
28629 lastText : "Last Page",
28631 * Customizable piece of the default paging text (defaults to "Refresh")
28634 refreshText : "Refresh",
28638 onRender : function(ct, position)
28640 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28641 this.navgroup.parentId = this.id;
28642 this.navgroup.onRender(this.el, null);
28643 // add the buttons to the navgroup
28645 if(this.displayInfo){
28646 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28647 this.displayEl = this.el.select('.x-paging-info', true).first();
28648 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28649 // this.displayEl = navel.el.select('span',true).first();
28655 Roo.each(_this.buttons, function(e){ // this might need to use render????
28656 Roo.factory(e).render(_this.el);
28660 Roo.each(_this.toolbarItems, function(e) {
28661 _this.navgroup.addItem(e);
28665 this.first = this.navgroup.addItem({
28666 tooltip: this.firstText,
28667 cls: "prev btn-outline-secondary",
28668 html : ' <i class="fa fa-step-backward"></i>',
28670 preventDefault: true,
28671 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28674 this.prev = this.navgroup.addItem({
28675 tooltip: this.prevText,
28676 cls: "prev btn-outline-secondary",
28677 html : ' <i class="fa fa-backward"></i>',
28679 preventDefault: true,
28680 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28682 //this.addSeparator();
28685 var field = this.navgroup.addItem( {
28687 cls : 'x-paging-position btn-outline-secondary',
28689 html : this.beforePageText +
28690 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28691 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28694 this.field = field.el.select('input', true).first();
28695 this.field.on("keydown", this.onPagingKeydown, this);
28696 this.field.on("focus", function(){this.dom.select();});
28699 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28700 //this.field.setHeight(18);
28701 //this.addSeparator();
28702 this.next = this.navgroup.addItem({
28703 tooltip: this.nextText,
28704 cls: "next btn-outline-secondary",
28705 html : ' <i class="fa fa-forward"></i>',
28707 preventDefault: true,
28708 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28710 this.last = this.navgroup.addItem({
28711 tooltip: this.lastText,
28712 html : ' <i class="fa fa-step-forward"></i>',
28713 cls: "next btn-outline-secondary",
28715 preventDefault: true,
28716 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28718 //this.addSeparator();
28719 this.loading = this.navgroup.addItem({
28720 tooltip: this.refreshText,
28721 cls: "btn-outline-secondary",
28722 html : ' <i class="fa fa-refresh"></i>',
28723 preventDefault: true,
28724 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28730 updateInfo : function(){
28731 if(this.displayEl){
28732 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28733 var msg = count == 0 ?
28737 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28739 this.displayEl.update(msg);
28744 onLoad : function(ds, r, o)
28746 this.cursor = o.params && o.params.start ? o.params.start : 0;
28748 var d = this.getPageData(),
28753 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28754 this.field.dom.value = ap;
28755 this.first.setDisabled(ap == 1);
28756 this.prev.setDisabled(ap == 1);
28757 this.next.setDisabled(ap == ps);
28758 this.last.setDisabled(ap == ps);
28759 this.loading.enable();
28764 getPageData : function(){
28765 var total = this.ds.getTotalCount();
28768 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28769 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28774 onLoadError : function(){
28775 this.loading.enable();
28779 onPagingKeydown : function(e){
28780 var k = e.getKey();
28781 var d = this.getPageData();
28783 var v = this.field.dom.value, pageNum;
28784 if(!v || isNaN(pageNum = parseInt(v, 10))){
28785 this.field.dom.value = d.activePage;
28788 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28789 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28792 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))
28794 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28795 this.field.dom.value = pageNum;
28796 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28799 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28801 var v = this.field.dom.value, pageNum;
28802 var increment = (e.shiftKey) ? 10 : 1;
28803 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28806 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28807 this.field.dom.value = d.activePage;
28810 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28812 this.field.dom.value = parseInt(v, 10) + increment;
28813 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28814 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28821 beforeLoad : function(){
28823 this.loading.disable();
28828 onClick : function(which){
28837 ds.load({params:{start: 0, limit: this.pageSize}});
28840 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28843 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28846 var total = ds.getTotalCount();
28847 var extra = total % this.pageSize;
28848 var lastStart = extra ? (total - extra) : total-this.pageSize;
28849 ds.load({params:{start: lastStart, limit: this.pageSize}});
28852 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28858 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28859 * @param {Roo.data.Store} store The data store to unbind
28861 unbind : function(ds){
28862 ds.un("beforeload", this.beforeLoad, this);
28863 ds.un("load", this.onLoad, this);
28864 ds.un("loadexception", this.onLoadError, this);
28865 ds.un("remove", this.updateInfo, this);
28866 ds.un("add", this.updateInfo, this);
28867 this.ds = undefined;
28871 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28872 * @param {Roo.data.Store} store The data store to bind
28874 bind : function(ds){
28875 ds.on("beforeload", this.beforeLoad, this);
28876 ds.on("load", this.onLoad, this);
28877 ds.on("loadexception", this.onLoadError, this);
28878 ds.on("remove", this.updateInfo, this);
28879 ds.on("add", this.updateInfo, this);
28890 * @class Roo.bootstrap.MessageBar
28891 * @extends Roo.bootstrap.Component
28892 * Bootstrap MessageBar class
28893 * @cfg {String} html contents of the MessageBar
28894 * @cfg {String} weight (info | success | warning | danger) default info
28895 * @cfg {String} beforeClass insert the bar before the given class
28896 * @cfg {Boolean} closable (true | false) default false
28897 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28900 * Create a new Element
28901 * @param {Object} config The config object
28904 Roo.bootstrap.MessageBar = function(config){
28905 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28908 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28914 beforeClass: 'bootstrap-sticky-wrap',
28916 getAutoCreate : function(){
28920 cls: 'alert alert-dismissable alert-' + this.weight,
28925 html: this.html || ''
28931 cfg.cls += ' alert-messages-fixed';
28945 onRender : function(ct, position)
28947 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28950 var cfg = Roo.apply({}, this.getAutoCreate());
28954 cfg.cls += ' ' + this.cls;
28957 cfg.style = this.style;
28959 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28961 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28964 this.el.select('>button.close').on('click', this.hide, this);
28970 if (!this.rendered) {
28976 this.fireEvent('show', this);
28982 if (!this.rendered) {
28988 this.fireEvent('hide', this);
28991 update : function()
28993 // var e = this.el.dom.firstChild;
28995 // if(this.closable){
28996 // e = e.nextSibling;
28999 // e.data = this.html || '';
29001 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29017 * @class Roo.bootstrap.Graph
29018 * @extends Roo.bootstrap.Component
29019 * Bootstrap Graph class
29023 @cfg {String} graphtype bar | vbar | pie
29024 @cfg {number} g_x coodinator | centre x (pie)
29025 @cfg {number} g_y coodinator | centre y (pie)
29026 @cfg {number} g_r radius (pie)
29027 @cfg {number} g_height height of the chart (respected by all elements in the set)
29028 @cfg {number} g_width width of the chart (respected by all elements in the set)
29029 @cfg {Object} title The title of the chart
29032 -opts (object) options for the chart
29034 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29035 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29037 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.
29038 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29040 o stretch (boolean)
29042 -opts (object) options for the pie
29045 o startAngle (number)
29046 o endAngle (number)
29050 * Create a new Input
29051 * @param {Object} config The config object
29054 Roo.bootstrap.Graph = function(config){
29055 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29061 * The img click event for the img.
29062 * @param {Roo.EventObject} e
29068 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29079 //g_colors: this.colors,
29086 getAutoCreate : function(){
29097 onRender : function(ct,position){
29100 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29102 if (typeof(Raphael) == 'undefined') {
29103 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29107 this.raphael = Raphael(this.el.dom);
29109 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29111 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29112 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29114 r.text(160, 10, "Single Series Chart").attr(txtattr);
29115 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29116 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29117 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29119 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29120 r.barchart(330, 10, 300, 220, data1);
29121 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29122 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29125 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29126 // r.barchart(30, 30, 560, 250, xdata, {
29127 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29128 // axis : "0 0 1 1",
29129 // axisxlabels : xdata
29130 // //yvalues : cols,
29133 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29135 // this.load(null,xdata,{
29136 // axis : "0 0 1 1",
29137 // axisxlabels : xdata
29142 load : function(graphtype,xdata,opts)
29144 this.raphael.clear();
29146 graphtype = this.graphtype;
29151 var r = this.raphael,
29152 fin = function () {
29153 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29155 fout = function () {
29156 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29158 pfin = function() {
29159 this.sector.stop();
29160 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29163 this.label[0].stop();
29164 this.label[0].attr({ r: 7.5 });
29165 this.label[1].attr({ "font-weight": 800 });
29168 pfout = function() {
29169 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29172 this.label[0].animate({ r: 5 }, 500, "bounce");
29173 this.label[1].attr({ "font-weight": 400 });
29179 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29182 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29185 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29186 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29188 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29195 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29200 setTitle: function(o)
29205 initEvents: function() {
29208 this.el.on('click', this.onClick, this);
29212 onClick : function(e)
29214 Roo.log('img onclick');
29215 this.fireEvent('click', this, e);
29227 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29230 * @class Roo.bootstrap.dash.NumberBox
29231 * @extends Roo.bootstrap.Component
29232 * Bootstrap NumberBox class
29233 * @cfg {String} headline Box headline
29234 * @cfg {String} content Box content
29235 * @cfg {String} icon Box icon
29236 * @cfg {String} footer Footer text
29237 * @cfg {String} fhref Footer href
29240 * Create a new NumberBox
29241 * @param {Object} config The config object
29245 Roo.bootstrap.dash.NumberBox = function(config){
29246 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29250 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29259 getAutoCreate : function(){
29263 cls : 'small-box ',
29271 cls : 'roo-headline',
29272 html : this.headline
29276 cls : 'roo-content',
29277 html : this.content
29291 cls : 'ion ' + this.icon
29300 cls : 'small-box-footer',
29301 href : this.fhref || '#',
29305 cfg.cn.push(footer);
29312 onRender : function(ct,position){
29313 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29320 setHeadline: function (value)
29322 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29325 setFooter: function (value, href)
29327 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29330 this.el.select('a.small-box-footer',true).first().attr('href', href);
29335 setContent: function (value)
29337 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29340 initEvents: function()
29354 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29357 * @class Roo.bootstrap.dash.TabBox
29358 * @extends Roo.bootstrap.Component
29359 * @children Roo.bootstrap.dash.TabPane
29360 * Bootstrap TabBox class
29361 * @cfg {String} title Title of the TabBox
29362 * @cfg {String} icon Icon of the TabBox
29363 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29364 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29367 * Create a new TabBox
29368 * @param {Object} config The config object
29372 Roo.bootstrap.dash.TabBox = function(config){
29373 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29378 * When a pane is added
29379 * @param {Roo.bootstrap.dash.TabPane} pane
29383 * @event activatepane
29384 * When a pane is activated
29385 * @param {Roo.bootstrap.dash.TabPane} pane
29387 "activatepane" : true
29395 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29400 tabScrollable : false,
29402 getChildContainer : function()
29404 return this.el.select('.tab-content', true).first();
29407 getAutoCreate : function(){
29411 cls: 'pull-left header',
29419 cls: 'fa ' + this.icon
29425 cls: 'nav nav-tabs pull-right',
29431 if(this.tabScrollable){
29438 cls: 'nav nav-tabs pull-right',
29449 cls: 'nav-tabs-custom',
29454 cls: 'tab-content no-padding',
29462 initEvents : function()
29464 //Roo.log('add add pane handler');
29465 this.on('addpane', this.onAddPane, this);
29468 * Updates the box title
29469 * @param {String} html to set the title to.
29471 setTitle : function(value)
29473 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29475 onAddPane : function(pane)
29477 this.panes.push(pane);
29478 //Roo.log('addpane');
29480 // tabs are rendere left to right..
29481 if(!this.showtabs){
29485 var ctr = this.el.select('.nav-tabs', true).first();
29488 var existing = ctr.select('.nav-tab',true);
29489 var qty = existing.getCount();;
29492 var tab = ctr.createChild({
29494 cls : 'nav-tab' + (qty ? '' : ' active'),
29502 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29505 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29507 pane.el.addClass('active');
29512 onTabClick : function(ev,un,ob,pane)
29514 //Roo.log('tab - prev default');
29515 ev.preventDefault();
29518 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29519 pane.tab.addClass('active');
29520 //Roo.log(pane.title);
29521 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29522 // technically we should have a deactivate event.. but maybe add later.
29523 // and it should not de-activate the selected tab...
29524 this.fireEvent('activatepane', pane);
29525 pane.el.addClass('active');
29526 pane.fireEvent('activate');
29531 getActivePane : function()
29534 Roo.each(this.panes, function(p) {
29535 if(p.el.hasClass('active')){
29556 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29558 * @class Roo.bootstrap.TabPane
29559 * @extends Roo.bootstrap.Component
29560 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29561 * Bootstrap TabPane class
29562 * @cfg {Boolean} active (false | true) Default false
29563 * @cfg {String} title title of panel
29567 * Create a new TabPane
29568 * @param {Object} config The config object
29571 Roo.bootstrap.dash.TabPane = function(config){
29572 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29578 * When a pane is activated
29579 * @param {Roo.bootstrap.dash.TabPane} pane
29586 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29591 // the tabBox that this is attached to.
29594 getAutoCreate : function()
29602 cfg.cls += ' active';
29607 initEvents : function()
29609 //Roo.log('trigger add pane handler');
29610 this.parent().fireEvent('addpane', this)
29614 * Updates the tab title
29615 * @param {String} html to set the title to.
29617 setTitle: function(str)
29623 this.tab.select('a', true).first().dom.innerHTML = str;
29642 * @class Roo.bootstrap.Tooltip
29643 * Bootstrap Tooltip class
29644 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29645 * to determine which dom element triggers the tooltip.
29647 * It needs to add support for additional attributes like tooltip-position
29650 * Create a new Toolti
29651 * @param {Object} config The config object
29654 Roo.bootstrap.Tooltip = function(config){
29655 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29657 this.alignment = Roo.bootstrap.Tooltip.alignment;
29659 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29660 this.alignment = config.alignment;
29665 Roo.apply(Roo.bootstrap.Tooltip, {
29667 * @function init initialize tooltip monitoring.
29671 currentTip : false,
29672 currentRegion : false,
29678 Roo.get(document).on('mouseover', this.enter ,this);
29679 Roo.get(document).on('mouseout', this.leave, this);
29682 this.currentTip = new Roo.bootstrap.Tooltip();
29685 enter : function(ev)
29687 var dom = ev.getTarget();
29689 //Roo.log(['enter',dom]);
29690 var el = Roo.fly(dom);
29691 if (this.currentEl) {
29693 //Roo.log(this.currentEl);
29694 //Roo.log(this.currentEl.contains(dom));
29695 if (this.currentEl == el) {
29698 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29704 if (this.currentTip.el) {
29705 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29709 if(!el || el.dom == document){
29715 if (!el.attr('tooltip')) {
29716 pel = el.findParent("[tooltip]");
29718 bindEl = Roo.get(pel);
29724 // you can not look for children, as if el is the body.. then everythign is the child..
29725 if (!pel && !el.attr('tooltip')) { //
29726 if (!el.select("[tooltip]").elements.length) {
29729 // is the mouse over this child...?
29730 bindEl = el.select("[tooltip]").first();
29731 var xy = ev.getXY();
29732 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29733 //Roo.log("not in region.");
29736 //Roo.log("child element over..");
29739 this.currentEl = el;
29740 this.currentTip.bind(bindEl);
29741 this.currentRegion = Roo.lib.Region.getRegion(dom);
29742 this.currentTip.enter();
29745 leave : function(ev)
29747 var dom = ev.getTarget();
29748 //Roo.log(['leave',dom]);
29749 if (!this.currentEl) {
29754 if (dom != this.currentEl.dom) {
29757 var xy = ev.getXY();
29758 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29761 // only activate leave if mouse cursor is outside... bounding box..
29766 if (this.currentTip) {
29767 this.currentTip.leave();
29769 //Roo.log('clear currentEl');
29770 this.currentEl = false;
29775 'left' : ['r-l', [-2,0], 'right'],
29776 'right' : ['l-r', [2,0], 'left'],
29777 'bottom' : ['t-b', [0,2], 'top'],
29778 'top' : [ 'b-t', [0,-2], 'bottom']
29784 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29789 delay : null, // can be { show : 300 , hide: 500}
29793 hoverState : null, //???
29795 placement : 'bottom',
29799 getAutoCreate : function(){
29806 cls : 'tooltip-arrow arrow'
29809 cls : 'tooltip-inner'
29816 bind : function(el)
29821 initEvents : function()
29823 this.arrowEl = this.el.select('.arrow', true).first();
29824 this.innerEl = this.el.select('.tooltip-inner', true).first();
29827 enter : function () {
29829 if (this.timeout != null) {
29830 clearTimeout(this.timeout);
29833 this.hoverState = 'in';
29834 //Roo.log("enter - show");
29835 if (!this.delay || !this.delay.show) {
29840 this.timeout = setTimeout(function () {
29841 if (_t.hoverState == 'in') {
29844 }, this.delay.show);
29848 clearTimeout(this.timeout);
29850 this.hoverState = 'out';
29851 if (!this.delay || !this.delay.hide) {
29857 this.timeout = setTimeout(function () {
29858 //Roo.log("leave - timeout");
29860 if (_t.hoverState == 'out') {
29862 Roo.bootstrap.Tooltip.currentEl = false;
29867 show : function (msg)
29870 this.render(document.body);
29873 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29875 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29877 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29879 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29880 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29882 var placement = typeof this.placement == 'function' ?
29883 this.placement.call(this, this.el, on_el) :
29886 var autoToken = /\s?auto?\s?/i;
29887 var autoPlace = autoToken.test(placement);
29889 placement = placement.replace(autoToken, '') || 'top';
29893 //this.el.setXY([0,0]);
29895 //this.el.dom.style.display='block';
29897 //this.el.appendTo(on_el);
29899 var p = this.getPosition();
29900 var box = this.el.getBox();
29906 var align = this.alignment[placement];
29908 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29910 if(placement == 'top' || placement == 'bottom'){
29912 placement = 'right';
29915 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29916 placement = 'left';
29919 var scroll = Roo.select('body', true).first().getScroll();
29921 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29925 align = this.alignment[placement];
29927 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29931 var elems = document.getElementsByTagName('div');
29932 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29933 for (var i = 0; i < elems.length; i++) {
29934 var zindex = Number.parseInt(
29935 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29938 if (zindex > highest) {
29945 this.el.dom.style.zIndex = highest;
29947 this.el.alignTo(this.bindEl, align[0],align[1]);
29948 //var arrow = this.el.select('.arrow',true).first();
29949 //arrow.set(align[2],
29951 this.el.addClass(placement);
29952 this.el.addClass("bs-tooltip-"+ placement);
29954 this.el.addClass('in fade show');
29956 this.hoverState = null;
29958 if (this.el.hasClass('fade')) {
29973 //this.el.setXY([0,0]);
29974 this.el.removeClass(['show', 'in']);
29990 * @class Roo.bootstrap.LocationPicker
29991 * @extends Roo.bootstrap.Component
29992 * Bootstrap LocationPicker class
29993 * @cfg {Number} latitude Position when init default 0
29994 * @cfg {Number} longitude Position when init default 0
29995 * @cfg {Number} zoom default 15
29996 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29997 * @cfg {Boolean} mapTypeControl default false
29998 * @cfg {Boolean} disableDoubleClickZoom default false
29999 * @cfg {Boolean} scrollwheel default true
30000 * @cfg {Boolean} streetViewControl default false
30001 * @cfg {Number} radius default 0
30002 * @cfg {String} locationName
30003 * @cfg {Boolean} draggable default true
30004 * @cfg {Boolean} enableAutocomplete default false
30005 * @cfg {Boolean} enableReverseGeocode default true
30006 * @cfg {String} markerTitle
30009 * Create a new LocationPicker
30010 * @param {Object} config The config object
30014 Roo.bootstrap.LocationPicker = function(config){
30016 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30021 * Fires when the picker initialized.
30022 * @param {Roo.bootstrap.LocationPicker} this
30023 * @param {Google Location} location
30027 * @event positionchanged
30028 * Fires when the picker position changed.
30029 * @param {Roo.bootstrap.LocationPicker} this
30030 * @param {Google Location} location
30032 positionchanged : true,
30035 * Fires when the map resize.
30036 * @param {Roo.bootstrap.LocationPicker} this
30041 * Fires when the map show.
30042 * @param {Roo.bootstrap.LocationPicker} this
30047 * Fires when the map hide.
30048 * @param {Roo.bootstrap.LocationPicker} this
30053 * Fires when click the map.
30054 * @param {Roo.bootstrap.LocationPicker} this
30055 * @param {Map event} e
30059 * @event mapRightClick
30060 * Fires when right click the map.
30061 * @param {Roo.bootstrap.LocationPicker} this
30062 * @param {Map event} e
30064 mapRightClick : true,
30066 * @event markerClick
30067 * Fires when click the marker.
30068 * @param {Roo.bootstrap.LocationPicker} this
30069 * @param {Map event} e
30071 markerClick : true,
30073 * @event markerRightClick
30074 * Fires when right click the marker.
30075 * @param {Roo.bootstrap.LocationPicker} this
30076 * @param {Map event} e
30078 markerRightClick : true,
30080 * @event OverlayViewDraw
30081 * Fires when OverlayView Draw
30082 * @param {Roo.bootstrap.LocationPicker} this
30084 OverlayViewDraw : true,
30086 * @event OverlayViewOnAdd
30087 * Fires when OverlayView Draw
30088 * @param {Roo.bootstrap.LocationPicker} this
30090 OverlayViewOnAdd : true,
30092 * @event OverlayViewOnRemove
30093 * Fires when OverlayView Draw
30094 * @param {Roo.bootstrap.LocationPicker} this
30096 OverlayViewOnRemove : true,
30098 * @event OverlayViewShow
30099 * Fires when OverlayView Draw
30100 * @param {Roo.bootstrap.LocationPicker} this
30101 * @param {Pixel} cpx
30103 OverlayViewShow : true,
30105 * @event OverlayViewHide
30106 * Fires when OverlayView Draw
30107 * @param {Roo.bootstrap.LocationPicker} this
30109 OverlayViewHide : true,
30111 * @event loadexception
30112 * Fires when load google lib failed.
30113 * @param {Roo.bootstrap.LocationPicker} this
30115 loadexception : true
30120 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30122 gMapContext: false,
30128 mapTypeControl: false,
30129 disableDoubleClickZoom: false,
30131 streetViewControl: false,
30135 enableAutocomplete: false,
30136 enableReverseGeocode: true,
30139 getAutoCreate: function()
30144 cls: 'roo-location-picker'
30150 initEvents: function(ct, position)
30152 if(!this.el.getWidth() || this.isApplied()){
30156 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30161 initial: function()
30163 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30164 this.fireEvent('loadexception', this);
30168 if(!this.mapTypeId){
30169 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30172 this.gMapContext = this.GMapContext();
30174 this.initOverlayView();
30176 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30180 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30181 _this.setPosition(_this.gMapContext.marker.position);
30184 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30185 _this.fireEvent('mapClick', this, event);
30189 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30190 _this.fireEvent('mapRightClick', this, event);
30194 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30195 _this.fireEvent('markerClick', this, event);
30199 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30200 _this.fireEvent('markerRightClick', this, event);
30204 this.setPosition(this.gMapContext.location);
30206 this.fireEvent('initial', this, this.gMapContext.location);
30209 initOverlayView: function()
30213 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30217 _this.fireEvent('OverlayViewDraw', _this);
30222 _this.fireEvent('OverlayViewOnAdd', _this);
30225 onRemove: function()
30227 _this.fireEvent('OverlayViewOnRemove', _this);
30230 show: function(cpx)
30232 _this.fireEvent('OverlayViewShow', _this, cpx);
30237 _this.fireEvent('OverlayViewHide', _this);
30243 fromLatLngToContainerPixel: function(event)
30245 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30248 isApplied: function()
30250 return this.getGmapContext() == false ? false : true;
30253 getGmapContext: function()
30255 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30258 GMapContext: function()
30260 var position = new google.maps.LatLng(this.latitude, this.longitude);
30262 var _map = new google.maps.Map(this.el.dom, {
30265 mapTypeId: this.mapTypeId,
30266 mapTypeControl: this.mapTypeControl,
30267 disableDoubleClickZoom: this.disableDoubleClickZoom,
30268 scrollwheel: this.scrollwheel,
30269 streetViewControl: this.streetViewControl,
30270 locationName: this.locationName,
30271 draggable: this.draggable,
30272 enableAutocomplete: this.enableAutocomplete,
30273 enableReverseGeocode: this.enableReverseGeocode
30276 var _marker = new google.maps.Marker({
30277 position: position,
30279 title: this.markerTitle,
30280 draggable: this.draggable
30287 location: position,
30288 radius: this.radius,
30289 locationName: this.locationName,
30290 addressComponents: {
30291 formatted_address: null,
30292 addressLine1: null,
30293 addressLine2: null,
30295 streetNumber: null,
30299 stateOrProvince: null
30302 domContainer: this.el.dom,
30303 geodecoder: new google.maps.Geocoder()
30307 drawCircle: function(center, radius, options)
30309 if (this.gMapContext.circle != null) {
30310 this.gMapContext.circle.setMap(null);
30314 options = Roo.apply({}, options, {
30315 strokeColor: "#0000FF",
30316 strokeOpacity: .35,
30318 fillColor: "#0000FF",
30322 options.map = this.gMapContext.map;
30323 options.radius = radius;
30324 options.center = center;
30325 this.gMapContext.circle = new google.maps.Circle(options);
30326 return this.gMapContext.circle;
30332 setPosition: function(location)
30334 this.gMapContext.location = location;
30335 this.gMapContext.marker.setPosition(location);
30336 this.gMapContext.map.panTo(location);
30337 this.drawCircle(location, this.gMapContext.radius, {});
30341 if (this.gMapContext.settings.enableReverseGeocode) {
30342 this.gMapContext.geodecoder.geocode({
30343 latLng: this.gMapContext.location
30344 }, function(results, status) {
30346 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30347 _this.gMapContext.locationName = results[0].formatted_address;
30348 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30350 _this.fireEvent('positionchanged', this, location);
30357 this.fireEvent('positionchanged', this, location);
30362 google.maps.event.trigger(this.gMapContext.map, "resize");
30364 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30366 this.fireEvent('resize', this);
30369 setPositionByLatLng: function(latitude, longitude)
30371 this.setPosition(new google.maps.LatLng(latitude, longitude));
30374 getCurrentPosition: function()
30377 latitude: this.gMapContext.location.lat(),
30378 longitude: this.gMapContext.location.lng()
30382 getAddressName: function()
30384 return this.gMapContext.locationName;
30387 getAddressComponents: function()
30389 return this.gMapContext.addressComponents;
30392 address_component_from_google_geocode: function(address_components)
30396 for (var i = 0; i < address_components.length; i++) {
30397 var component = address_components[i];
30398 if (component.types.indexOf("postal_code") >= 0) {
30399 result.postalCode = component.short_name;
30400 } else if (component.types.indexOf("street_number") >= 0) {
30401 result.streetNumber = component.short_name;
30402 } else if (component.types.indexOf("route") >= 0) {
30403 result.streetName = component.short_name;
30404 } else if (component.types.indexOf("neighborhood") >= 0) {
30405 result.city = component.short_name;
30406 } else if (component.types.indexOf("locality") >= 0) {
30407 result.city = component.short_name;
30408 } else if (component.types.indexOf("sublocality") >= 0) {
30409 result.district = component.short_name;
30410 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30411 result.stateOrProvince = component.short_name;
30412 } else if (component.types.indexOf("country") >= 0) {
30413 result.country = component.short_name;
30417 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30418 result.addressLine2 = "";
30422 setZoomLevel: function(zoom)
30424 this.gMapContext.map.setZoom(zoom);
30437 this.fireEvent('show', this);
30448 this.fireEvent('hide', this);
30453 Roo.apply(Roo.bootstrap.LocationPicker, {
30455 OverlayView : function(map, options)
30457 options = options || {};
30464 * @class Roo.bootstrap.Alert
30465 * @extends Roo.bootstrap.Component
30466 * Bootstrap Alert class - shows an alert area box
30468 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30469 Enter a valid email address
30472 * @cfg {String} title The title of alert
30473 * @cfg {String} html The content of alert
30474 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30475 * @cfg {String} fa font-awesomeicon
30476 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30477 * @cfg {Boolean} close true to show a x closer
30481 * Create a new alert
30482 * @param {Object} config The config object
30486 Roo.bootstrap.Alert = function(config){
30487 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30491 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30497 faicon: false, // BC
30501 getAutoCreate : function()
30513 style : this.close ? '' : 'display:none'
30517 cls : 'roo-alert-icon'
30522 cls : 'roo-alert-title',
30527 cls : 'roo-alert-text',
30534 cfg.cn[0].cls += ' fa ' + this.faicon;
30537 cfg.cn[0].cls += ' fa ' + this.fa;
30541 cfg.cls += ' alert-' + this.weight;
30547 initEvents: function()
30549 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30550 this.titleEl = this.el.select('.roo-alert-title',true).first();
30551 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30552 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30553 if (this.seconds > 0) {
30554 this.hide.defer(this.seconds, this);
30558 * Set the Title Message HTML
30559 * @param {String} html
30561 setTitle : function(str)
30563 this.titleEl.dom.innerHTML = str;
30567 * Set the Body Message HTML
30568 * @param {String} html
30570 setHtml : function(str)
30572 this.htmlEl.dom.innerHTML = str;
30575 * Set the Weight of the alert
30576 * @param {String} (success|info|warning|danger) weight
30579 setWeight : function(weight)
30582 this.el.removeClass('alert-' + this.weight);
30585 this.weight = weight;
30587 this.el.addClass('alert-' + this.weight);
30590 * Set the Icon of the alert
30591 * @param {String} see fontawsome names (name without the 'fa-' bit)
30593 setIcon : function(icon)
30596 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30599 this.faicon = icon;
30601 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30626 * @class Roo.bootstrap.UploadCropbox
30627 * @extends Roo.bootstrap.Component
30628 * Bootstrap UploadCropbox class
30629 * @cfg {String} emptyText show when image has been loaded
30630 * @cfg {String} rotateNotify show when image too small to rotate
30631 * @cfg {Number} errorTimeout default 3000
30632 * @cfg {Number} minWidth default 300
30633 * @cfg {Number} minHeight default 300
30634 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30635 * @cfg {Boolean} isDocument (true|false) default false
30636 * @cfg {String} url action url
30637 * @cfg {String} paramName default 'imageUpload'
30638 * @cfg {String} method default POST
30639 * @cfg {Boolean} loadMask (true|false) default true
30640 * @cfg {Boolean} loadingText default 'Loading...'
30643 * Create a new UploadCropbox
30644 * @param {Object} config The config object
30647 Roo.bootstrap.UploadCropbox = function(config){
30648 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30652 * @event beforeselectfile
30653 * Fire before select file
30654 * @param {Roo.bootstrap.UploadCropbox} this
30656 "beforeselectfile" : true,
30659 * Fire after initEvent
30660 * @param {Roo.bootstrap.UploadCropbox} this
30665 * Fire after initEvent
30666 * @param {Roo.bootstrap.UploadCropbox} this
30667 * @param {String} data
30672 * Fire when preparing the file data
30673 * @param {Roo.bootstrap.UploadCropbox} this
30674 * @param {Object} file
30679 * Fire when get exception
30680 * @param {Roo.bootstrap.UploadCropbox} this
30681 * @param {XMLHttpRequest} xhr
30683 "exception" : true,
30685 * @event beforeloadcanvas
30686 * Fire before load the canvas
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {String} src
30690 "beforeloadcanvas" : true,
30693 * Fire when trash image
30694 * @param {Roo.bootstrap.UploadCropbox} this
30699 * Fire when download the image
30700 * @param {Roo.bootstrap.UploadCropbox} this
30704 * @event footerbuttonclick
30705 * Fire when footerbuttonclick
30706 * @param {Roo.bootstrap.UploadCropbox} this
30707 * @param {String} type
30709 "footerbuttonclick" : true,
30713 * @param {Roo.bootstrap.UploadCropbox} this
30718 * Fire when rotate the image
30719 * @param {Roo.bootstrap.UploadCropbox} this
30720 * @param {String} pos
30725 * Fire when inspect the file
30726 * @param {Roo.bootstrap.UploadCropbox} this
30727 * @param {Object} file
30732 * Fire when xhr upload the file
30733 * @param {Roo.bootstrap.UploadCropbox} this
30734 * @param {Object} data
30739 * Fire when arrange the file data
30740 * @param {Roo.bootstrap.UploadCropbox} this
30741 * @param {Object} formData
30746 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30749 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30751 emptyText : 'Click to upload image',
30752 rotateNotify : 'Image is too small to rotate',
30753 errorTimeout : 3000,
30767 cropType : 'image/jpeg',
30769 canvasLoaded : false,
30770 isDocument : false,
30772 paramName : 'imageUpload',
30774 loadingText : 'Loading...',
30777 getAutoCreate : function()
30781 cls : 'roo-upload-cropbox',
30785 cls : 'roo-upload-cropbox-selector',
30790 cls : 'roo-upload-cropbox-body',
30791 style : 'cursor:pointer',
30795 cls : 'roo-upload-cropbox-preview'
30799 cls : 'roo-upload-cropbox-thumb'
30803 cls : 'roo-upload-cropbox-empty-notify',
30804 html : this.emptyText
30808 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30809 html : this.rotateNotify
30815 cls : 'roo-upload-cropbox-footer',
30818 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30828 onRender : function(ct, position)
30830 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30832 if (this.buttons.length) {
30834 Roo.each(this.buttons, function(bb) {
30836 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30838 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30844 this.maskEl = this.el;
30848 initEvents : function()
30850 this.urlAPI = (window.createObjectURL && window) ||
30851 (window.URL && URL.revokeObjectURL && URL) ||
30852 (window.webkitURL && webkitURL);
30854 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30855 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30857 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30858 this.selectorEl.hide();
30860 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30861 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30864 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30865 this.thumbEl.hide();
30867 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30868 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30871 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30872 this.errorEl.hide();
30874 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30875 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876 this.footerEl.hide();
30878 this.setThumbBoxSize();
30884 this.fireEvent('initial', this);
30891 window.addEventListener("resize", function() { _this.resize(); } );
30893 this.bodyEl.on('click', this.beforeSelectFile, this);
30896 this.bodyEl.on('touchstart', this.onTouchStart, this);
30897 this.bodyEl.on('touchmove', this.onTouchMove, this);
30898 this.bodyEl.on('touchend', this.onTouchEnd, this);
30902 this.bodyEl.on('mousedown', this.onMouseDown, this);
30903 this.bodyEl.on('mousemove', this.onMouseMove, this);
30904 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30905 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30906 Roo.get(document).on('mouseup', this.onMouseUp, this);
30909 this.selectorEl.on('change', this.onFileSelected, this);
30915 this.baseScale = 1;
30917 this.baseRotate = 1;
30918 this.dragable = false;
30919 this.pinching = false;
30922 this.cropData = false;
30923 this.notifyEl.dom.innerHTML = this.emptyText;
30925 this.selectorEl.dom.value = '';
30929 resize : function()
30931 if(this.fireEvent('resize', this) != false){
30932 this.setThumbBoxPosition();
30933 this.setCanvasPosition();
30937 onFooterButtonClick : function(e, el, o, type)
30940 case 'rotate-left' :
30941 this.onRotateLeft(e);
30943 case 'rotate-right' :
30944 this.onRotateRight(e);
30947 this.beforeSelectFile(e);
30962 this.fireEvent('footerbuttonclick', this, type);
30965 beforeSelectFile : function(e)
30967 e.preventDefault();
30969 if(this.fireEvent('beforeselectfile', this) != false){
30970 this.selectorEl.dom.click();
30974 onFileSelected : function(e)
30976 e.preventDefault();
30978 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30982 var file = this.selectorEl.dom.files[0];
30984 if(this.fireEvent('inspect', this, file) != false){
30985 this.prepare(file);
30990 trash : function(e)
30992 this.fireEvent('trash', this);
30995 download : function(e)
30997 this.fireEvent('download', this);
31000 loadCanvas : function(src)
31002 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31006 this.imageEl = document.createElement('img');
31010 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31012 this.imageEl.src = src;
31016 onLoadCanvas : function()
31018 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31019 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31021 this.bodyEl.un('click', this.beforeSelectFile, this);
31023 this.notifyEl.hide();
31024 this.thumbEl.show();
31025 this.footerEl.show();
31027 this.baseRotateLevel();
31029 if(this.isDocument){
31030 this.setThumbBoxSize();
31033 this.setThumbBoxPosition();
31035 this.baseScaleLevel();
31041 this.canvasLoaded = true;
31044 this.maskEl.unmask();
31049 setCanvasPosition : function()
31051 if(!this.canvasEl){
31055 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31056 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31058 this.previewEl.setLeft(pw);
31059 this.previewEl.setTop(ph);
31063 onMouseDown : function(e)
31067 this.dragable = true;
31068 this.pinching = false;
31070 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31071 this.dragable = false;
31075 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31076 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31080 onMouseMove : function(e)
31084 if(!this.canvasLoaded){
31088 if (!this.dragable){
31092 var minX = Math.ceil(this.thumbEl.getLeft(true));
31093 var minY = Math.ceil(this.thumbEl.getTop(true));
31095 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31096 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31098 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31101 x = x - this.mouseX;
31102 y = y - this.mouseY;
31104 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31105 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31107 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31108 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31110 this.previewEl.setLeft(bgX);
31111 this.previewEl.setTop(bgY);
31113 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31114 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31117 onMouseUp : function(e)
31121 this.dragable = false;
31124 onMouseWheel : function(e)
31128 this.startScale = this.scale;
31130 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31132 if(!this.zoomable()){
31133 this.scale = this.startScale;
31142 zoomable : function()
31144 var minScale = this.thumbEl.getWidth() / this.minWidth;
31146 if(this.minWidth < this.minHeight){
31147 minScale = this.thumbEl.getHeight() / this.minHeight;
31150 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31151 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31155 (this.rotate == 0 || this.rotate == 180) &&
31157 width > this.imageEl.OriginWidth ||
31158 height > this.imageEl.OriginHeight ||
31159 (width < this.minWidth && height < this.minHeight)
31167 (this.rotate == 90 || this.rotate == 270) &&
31169 width > this.imageEl.OriginWidth ||
31170 height > this.imageEl.OriginHeight ||
31171 (width < this.minHeight && height < this.minWidth)
31178 !this.isDocument &&
31179 (this.rotate == 0 || this.rotate == 180) &&
31181 width < this.minWidth ||
31182 width > this.imageEl.OriginWidth ||
31183 height < this.minHeight ||
31184 height > this.imageEl.OriginHeight
31191 !this.isDocument &&
31192 (this.rotate == 90 || this.rotate == 270) &&
31194 width < this.minHeight ||
31195 width > this.imageEl.OriginWidth ||
31196 height < this.minWidth ||
31197 height > this.imageEl.OriginHeight
31207 onRotateLeft : function(e)
31209 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31211 var minScale = this.thumbEl.getWidth() / this.minWidth;
31213 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31214 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31216 this.startScale = this.scale;
31218 while (this.getScaleLevel() < minScale){
31220 this.scale = this.scale + 1;
31222 if(!this.zoomable()){
31227 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31228 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31233 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31240 this.scale = this.startScale;
31242 this.onRotateFail();
31247 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31249 if(this.isDocument){
31250 this.setThumbBoxSize();
31251 this.setThumbBoxPosition();
31252 this.setCanvasPosition();
31257 this.fireEvent('rotate', this, 'left');
31261 onRotateRight : function(e)
31263 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31265 var minScale = this.thumbEl.getWidth() / this.minWidth;
31267 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31268 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31270 this.startScale = this.scale;
31272 while (this.getScaleLevel() < minScale){
31274 this.scale = this.scale + 1;
31276 if(!this.zoomable()){
31281 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31282 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31287 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31294 this.scale = this.startScale;
31296 this.onRotateFail();
31301 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31303 if(this.isDocument){
31304 this.setThumbBoxSize();
31305 this.setThumbBoxPosition();
31306 this.setCanvasPosition();
31311 this.fireEvent('rotate', this, 'right');
31314 onRotateFail : function()
31316 this.errorEl.show(true);
31320 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31325 this.previewEl.dom.innerHTML = '';
31327 var canvasEl = document.createElement("canvas");
31329 var contextEl = canvasEl.getContext("2d");
31331 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31332 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31333 var center = this.imageEl.OriginWidth / 2;
31335 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31336 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31337 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31338 center = this.imageEl.OriginHeight / 2;
31341 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31343 contextEl.translate(center, center);
31344 contextEl.rotate(this.rotate * Math.PI / 180);
31346 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31348 this.canvasEl = document.createElement("canvas");
31350 this.contextEl = this.canvasEl.getContext("2d");
31352 switch (this.rotate) {
31355 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31356 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31358 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31363 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31364 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31366 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31367 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);
31371 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31376 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31377 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31379 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31380 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);
31384 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);
31389 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31390 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31392 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31393 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31397 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);
31404 this.previewEl.appendChild(this.canvasEl);
31406 this.setCanvasPosition();
31411 if(!this.canvasLoaded){
31415 var imageCanvas = document.createElement("canvas");
31417 var imageContext = imageCanvas.getContext("2d");
31419 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31420 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31422 var center = imageCanvas.width / 2;
31424 imageContext.translate(center, center);
31426 imageContext.rotate(this.rotate * Math.PI / 180);
31428 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31430 var canvas = document.createElement("canvas");
31432 var context = canvas.getContext("2d");
31434 canvas.width = this.minWidth;
31435 canvas.height = this.minHeight;
31437 switch (this.rotate) {
31440 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31441 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31443 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31444 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31446 var targetWidth = this.minWidth - 2 * x;
31447 var targetHeight = this.minHeight - 2 * y;
31451 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31452 scale = targetWidth / width;
31455 if(x > 0 && y == 0){
31456 scale = targetHeight / height;
31459 if(x > 0 && y > 0){
31460 scale = targetWidth / width;
31462 if(width < height){
31463 scale = targetHeight / height;
31467 context.scale(scale, scale);
31469 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31470 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31472 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31473 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31475 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31480 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31481 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31483 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31484 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31486 var targetWidth = this.minWidth - 2 * x;
31487 var targetHeight = this.minHeight - 2 * y;
31491 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31492 scale = targetWidth / width;
31495 if(x > 0 && y == 0){
31496 scale = targetHeight / height;
31499 if(x > 0 && y > 0){
31500 scale = targetWidth / width;
31502 if(width < height){
31503 scale = targetHeight / height;
31507 context.scale(scale, scale);
31509 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31510 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31512 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31513 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31515 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31517 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31522 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31523 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31525 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31526 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31528 var targetWidth = this.minWidth - 2 * x;
31529 var targetHeight = this.minHeight - 2 * y;
31533 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31534 scale = targetWidth / width;
31537 if(x > 0 && y == 0){
31538 scale = targetHeight / height;
31541 if(x > 0 && y > 0){
31542 scale = targetWidth / width;
31544 if(width < height){
31545 scale = targetHeight / height;
31549 context.scale(scale, scale);
31551 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31552 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31554 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31555 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31557 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31558 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31560 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31565 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31566 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31568 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31569 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31571 var targetWidth = this.minWidth - 2 * x;
31572 var targetHeight = this.minHeight - 2 * y;
31576 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31577 scale = targetWidth / width;
31580 if(x > 0 && y == 0){
31581 scale = targetHeight / height;
31584 if(x > 0 && y > 0){
31585 scale = targetWidth / width;
31587 if(width < height){
31588 scale = targetHeight / height;
31592 context.scale(scale, scale);
31594 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31595 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31597 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31598 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31600 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31602 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31609 this.cropData = canvas.toDataURL(this.cropType);
31611 if(this.fireEvent('crop', this, this.cropData) !== false){
31612 this.process(this.file, this.cropData);
31619 setThumbBoxSize : function()
31623 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31624 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31625 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31627 this.minWidth = width;
31628 this.minHeight = height;
31630 if(this.rotate == 90 || this.rotate == 270){
31631 this.minWidth = height;
31632 this.minHeight = width;
31637 width = Math.ceil(this.minWidth * height / this.minHeight);
31639 if(this.minWidth > this.minHeight){
31641 height = Math.ceil(this.minHeight * width / this.minWidth);
31644 this.thumbEl.setStyle({
31645 width : width + 'px',
31646 height : height + 'px'
31653 setThumbBoxPosition : function()
31655 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31656 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31658 this.thumbEl.setLeft(x);
31659 this.thumbEl.setTop(y);
31663 baseRotateLevel : function()
31665 this.baseRotate = 1;
31668 typeof(this.exif) != 'undefined' &&
31669 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31670 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31672 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31675 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31679 baseScaleLevel : function()
31683 if(this.isDocument){
31685 if(this.baseRotate == 6 || this.baseRotate == 8){
31687 height = this.thumbEl.getHeight();
31688 this.baseScale = height / this.imageEl.OriginWidth;
31690 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31691 width = this.thumbEl.getWidth();
31692 this.baseScale = width / this.imageEl.OriginHeight;
31698 height = this.thumbEl.getHeight();
31699 this.baseScale = height / this.imageEl.OriginHeight;
31701 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31702 width = this.thumbEl.getWidth();
31703 this.baseScale = width / this.imageEl.OriginWidth;
31709 if(this.baseRotate == 6 || this.baseRotate == 8){
31711 width = this.thumbEl.getHeight();
31712 this.baseScale = width / this.imageEl.OriginHeight;
31714 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31715 height = this.thumbEl.getWidth();
31716 this.baseScale = height / this.imageEl.OriginHeight;
31719 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31720 height = this.thumbEl.getWidth();
31721 this.baseScale = height / this.imageEl.OriginHeight;
31723 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31724 width = this.thumbEl.getHeight();
31725 this.baseScale = width / this.imageEl.OriginWidth;
31732 width = this.thumbEl.getWidth();
31733 this.baseScale = width / this.imageEl.OriginWidth;
31735 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31736 height = this.thumbEl.getHeight();
31737 this.baseScale = height / this.imageEl.OriginHeight;
31740 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31742 height = this.thumbEl.getHeight();
31743 this.baseScale = height / this.imageEl.OriginHeight;
31745 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31746 width = this.thumbEl.getWidth();
31747 this.baseScale = width / this.imageEl.OriginWidth;
31755 getScaleLevel : function()
31757 return this.baseScale * Math.pow(1.1, this.scale);
31760 onTouchStart : function(e)
31762 if(!this.canvasLoaded){
31763 this.beforeSelectFile(e);
31767 var touches = e.browserEvent.touches;
31773 if(touches.length == 1){
31774 this.onMouseDown(e);
31778 if(touches.length != 2){
31784 for(var i = 0, finger; finger = touches[i]; i++){
31785 coords.push(finger.pageX, finger.pageY);
31788 var x = Math.pow(coords[0] - coords[2], 2);
31789 var y = Math.pow(coords[1] - coords[3], 2);
31791 this.startDistance = Math.sqrt(x + y);
31793 this.startScale = this.scale;
31795 this.pinching = true;
31796 this.dragable = false;
31800 onTouchMove : function(e)
31802 if(!this.pinching && !this.dragable){
31806 var touches = e.browserEvent.touches;
31813 this.onMouseMove(e);
31819 for(var i = 0, finger; finger = touches[i]; i++){
31820 coords.push(finger.pageX, finger.pageY);
31823 var x = Math.pow(coords[0] - coords[2], 2);
31824 var y = Math.pow(coords[1] - coords[3], 2);
31826 this.endDistance = Math.sqrt(x + y);
31828 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31830 if(!this.zoomable()){
31831 this.scale = this.startScale;
31839 onTouchEnd : function(e)
31841 this.pinching = false;
31842 this.dragable = false;
31846 process : function(file, crop)
31849 this.maskEl.mask(this.loadingText);
31852 this.xhr = new XMLHttpRequest();
31854 file.xhr = this.xhr;
31856 this.xhr.open(this.method, this.url, true);
31859 "Accept": "application/json",
31860 "Cache-Control": "no-cache",
31861 "X-Requested-With": "XMLHttpRequest"
31864 for (var headerName in headers) {
31865 var headerValue = headers[headerName];
31867 this.xhr.setRequestHeader(headerName, headerValue);
31873 this.xhr.onload = function()
31875 _this.xhrOnLoad(_this.xhr);
31878 this.xhr.onerror = function()
31880 _this.xhrOnError(_this.xhr);
31883 var formData = new FormData();
31885 formData.append('returnHTML', 'NO');
31888 formData.append('crop', crop);
31891 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31892 formData.append(this.paramName, file, file.name);
31895 if(typeof(file.filename) != 'undefined'){
31896 formData.append('filename', file.filename);
31899 if(typeof(file.mimetype) != 'undefined'){
31900 formData.append('mimetype', file.mimetype);
31903 if(this.fireEvent('arrange', this, formData) != false){
31904 this.xhr.send(formData);
31908 xhrOnLoad : function(xhr)
31911 this.maskEl.unmask();
31914 if (xhr.readyState !== 4) {
31915 this.fireEvent('exception', this, xhr);
31919 var response = Roo.decode(xhr.responseText);
31921 if(!response.success){
31922 this.fireEvent('exception', this, xhr);
31926 var response = Roo.decode(xhr.responseText);
31928 this.fireEvent('upload', this, response);
31932 xhrOnError : function()
31935 this.maskEl.unmask();
31938 Roo.log('xhr on error');
31940 var response = Roo.decode(xhr.responseText);
31946 prepare : function(file)
31949 this.maskEl.mask(this.loadingText);
31955 if(typeof(file) === 'string'){
31956 this.loadCanvas(file);
31960 if(!file || !this.urlAPI){
31965 this.cropType = file.type;
31969 if(this.fireEvent('prepare', this, this.file) != false){
31971 var reader = new FileReader();
31973 reader.onload = function (e) {
31974 if (e.target.error) {
31975 Roo.log(e.target.error);
31979 var buffer = e.target.result,
31980 dataView = new DataView(buffer),
31982 maxOffset = dataView.byteLength - 4,
31986 if (dataView.getUint16(0) === 0xffd8) {
31987 while (offset < maxOffset) {
31988 markerBytes = dataView.getUint16(offset);
31990 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31991 markerLength = dataView.getUint16(offset + 2) + 2;
31992 if (offset + markerLength > dataView.byteLength) {
31993 Roo.log('Invalid meta data: Invalid segment size.');
31997 if(markerBytes == 0xffe1){
31998 _this.parseExifData(
32005 offset += markerLength;
32015 var url = _this.urlAPI.createObjectURL(_this.file);
32017 _this.loadCanvas(url);
32022 reader.readAsArrayBuffer(this.file);
32028 parseExifData : function(dataView, offset, length)
32030 var tiffOffset = offset + 10,
32034 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32035 // No Exif data, might be XMP data instead
32039 // Check for the ASCII code for "Exif" (0x45786966):
32040 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32041 // No Exif data, might be XMP data instead
32044 if (tiffOffset + 8 > dataView.byteLength) {
32045 Roo.log('Invalid Exif data: Invalid segment size.');
32048 // Check for the two null bytes:
32049 if (dataView.getUint16(offset + 8) !== 0x0000) {
32050 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32053 // Check the byte alignment:
32054 switch (dataView.getUint16(tiffOffset)) {
32056 littleEndian = true;
32059 littleEndian = false;
32062 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32065 // Check for the TIFF tag marker (0x002A):
32066 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32067 Roo.log('Invalid Exif data: Missing TIFF marker.');
32070 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32071 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32073 this.parseExifTags(
32076 tiffOffset + dirOffset,
32081 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32086 if (dirOffset + 6 > dataView.byteLength) {
32087 Roo.log('Invalid Exif data: Invalid directory offset.');
32090 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32091 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32092 if (dirEndOffset + 4 > dataView.byteLength) {
32093 Roo.log('Invalid Exif data: Invalid directory size.');
32096 for (i = 0; i < tagsNumber; i += 1) {
32100 dirOffset + 2 + 12 * i, // tag offset
32104 // Return the offset to the next directory:
32105 return dataView.getUint32(dirEndOffset, littleEndian);
32108 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32110 var tag = dataView.getUint16(offset, littleEndian);
32112 this.exif[tag] = this.getExifValue(
32116 dataView.getUint16(offset + 2, littleEndian), // tag type
32117 dataView.getUint32(offset + 4, littleEndian), // tag length
32122 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32124 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32133 Roo.log('Invalid Exif data: Invalid tag type.');
32137 tagSize = tagType.size * length;
32138 // Determine if the value is contained in the dataOffset bytes,
32139 // or if the value at the dataOffset is a pointer to the actual data:
32140 dataOffset = tagSize > 4 ?
32141 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32142 if (dataOffset + tagSize > dataView.byteLength) {
32143 Roo.log('Invalid Exif data: Invalid data offset.');
32146 if (length === 1) {
32147 return tagType.getValue(dataView, dataOffset, littleEndian);
32150 for (i = 0; i < length; i += 1) {
32151 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32154 if (tagType.ascii) {
32156 // Concatenate the chars:
32157 for (i = 0; i < values.length; i += 1) {
32159 // Ignore the terminating NULL byte(s):
32160 if (c === '\u0000') {
32172 Roo.apply(Roo.bootstrap.UploadCropbox, {
32174 'Orientation': 0x0112
32178 1: 0, //'top-left',
32180 3: 180, //'bottom-right',
32181 // 4: 'bottom-left',
32183 6: 90, //'right-top',
32184 // 7: 'right-bottom',
32185 8: 270 //'left-bottom'
32189 // byte, 8-bit unsigned int:
32191 getValue: function (dataView, dataOffset) {
32192 return dataView.getUint8(dataOffset);
32196 // ascii, 8-bit byte:
32198 getValue: function (dataView, dataOffset) {
32199 return String.fromCharCode(dataView.getUint8(dataOffset));
32204 // short, 16 bit int:
32206 getValue: function (dataView, dataOffset, littleEndian) {
32207 return dataView.getUint16(dataOffset, littleEndian);
32211 // long, 32 bit int:
32213 getValue: function (dataView, dataOffset, littleEndian) {
32214 return dataView.getUint32(dataOffset, littleEndian);
32218 // rational = two long values, first is numerator, second is denominator:
32220 getValue: function (dataView, dataOffset, littleEndian) {
32221 return dataView.getUint32(dataOffset, littleEndian) /
32222 dataView.getUint32(dataOffset + 4, littleEndian);
32226 // slong, 32 bit signed int:
32228 getValue: function (dataView, dataOffset, littleEndian) {
32229 return dataView.getInt32(dataOffset, littleEndian);
32233 // srational, two slongs, first is numerator, second is denominator:
32235 getValue: function (dataView, dataOffset, littleEndian) {
32236 return dataView.getInt32(dataOffset, littleEndian) /
32237 dataView.getInt32(dataOffset + 4, littleEndian);
32247 cls : 'btn-group roo-upload-cropbox-rotate-left',
32248 action : 'rotate-left',
32252 cls : 'btn btn-default',
32253 html : '<i class="fa fa-undo"></i>'
32259 cls : 'btn-group roo-upload-cropbox-picture',
32260 action : 'picture',
32264 cls : 'btn btn-default',
32265 html : '<i class="fa fa-picture-o"></i>'
32271 cls : 'btn-group roo-upload-cropbox-rotate-right',
32272 action : 'rotate-right',
32276 cls : 'btn btn-default',
32277 html : '<i class="fa fa-repeat"></i>'
32285 cls : 'btn-group roo-upload-cropbox-rotate-left',
32286 action : 'rotate-left',
32290 cls : 'btn btn-default',
32291 html : '<i class="fa fa-undo"></i>'
32297 cls : 'btn-group roo-upload-cropbox-download',
32298 action : 'download',
32302 cls : 'btn btn-default',
32303 html : '<i class="fa fa-download"></i>'
32309 cls : 'btn-group roo-upload-cropbox-crop',
32314 cls : 'btn btn-default',
32315 html : '<i class="fa fa-crop"></i>'
32321 cls : 'btn-group roo-upload-cropbox-trash',
32326 cls : 'btn btn-default',
32327 html : '<i class="fa fa-trash"></i>'
32333 cls : 'btn-group roo-upload-cropbox-rotate-right',
32334 action : 'rotate-right',
32338 cls : 'btn btn-default',
32339 html : '<i class="fa fa-repeat"></i>'
32347 cls : 'btn-group roo-upload-cropbox-rotate-left',
32348 action : 'rotate-left',
32352 cls : 'btn btn-default',
32353 html : '<i class="fa fa-undo"></i>'
32359 cls : 'btn-group roo-upload-cropbox-rotate-right',
32360 action : 'rotate-right',
32364 cls : 'btn btn-default',
32365 html : '<i class="fa fa-repeat"></i>'
32378 * @class Roo.bootstrap.DocumentManager
32379 * @extends Roo.bootstrap.Component
32380 * Bootstrap DocumentManager class
32381 * @cfg {String} paramName default 'imageUpload'
32382 * @cfg {String} toolTipName default 'filename'
32383 * @cfg {String} method default POST
32384 * @cfg {String} url action url
32385 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32386 * @cfg {Boolean} multiple multiple upload default true
32387 * @cfg {Number} thumbSize default 300
32388 * @cfg {String} fieldLabel
32389 * @cfg {Number} labelWidth default 4
32390 * @cfg {String} labelAlign (left|top) default left
32391 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32392 * @cfg {Number} labellg set the width of label (1-12)
32393 * @cfg {Number} labelmd set the width of label (1-12)
32394 * @cfg {Number} labelsm set the width of label (1-12)
32395 * @cfg {Number} labelxs set the width of label (1-12)
32398 * Create a new DocumentManager
32399 * @param {Object} config The config object
32402 Roo.bootstrap.DocumentManager = function(config){
32403 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32406 this.delegates = [];
32411 * Fire when initial the DocumentManager
32412 * @param {Roo.bootstrap.DocumentManager} this
32417 * inspect selected file
32418 * @param {Roo.bootstrap.DocumentManager} this
32419 * @param {File} file
32424 * Fire when xhr load exception
32425 * @param {Roo.bootstrap.DocumentManager} this
32426 * @param {XMLHttpRequest} xhr
32428 "exception" : true,
32430 * @event afterupload
32431 * Fire when xhr load exception
32432 * @param {Roo.bootstrap.DocumentManager} this
32433 * @param {XMLHttpRequest} xhr
32435 "afterupload" : true,
32438 * prepare the form data
32439 * @param {Roo.bootstrap.DocumentManager} this
32440 * @param {Object} formData
32445 * Fire when remove the file
32446 * @param {Roo.bootstrap.DocumentManager} this
32447 * @param {Object} file
32452 * Fire after refresh the file
32453 * @param {Roo.bootstrap.DocumentManager} this
32458 * Fire after click the image
32459 * @param {Roo.bootstrap.DocumentManager} this
32460 * @param {Object} file
32465 * Fire when upload a image and editable set to true
32466 * @param {Roo.bootstrap.DocumentManager} this
32467 * @param {Object} file
32471 * @event beforeselectfile
32472 * Fire before select file
32473 * @param {Roo.bootstrap.DocumentManager} this
32475 "beforeselectfile" : true,
32478 * Fire before process file
32479 * @param {Roo.bootstrap.DocumentManager} this
32480 * @param {Object} file
32484 * @event previewrendered
32485 * Fire when preview rendered
32486 * @param {Roo.bootstrap.DocumentManager} this
32487 * @param {Object} file
32489 "previewrendered" : true,
32492 "previewResize" : true
32497 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32506 paramName : 'imageUpload',
32507 toolTipName : 'filename',
32510 labelAlign : 'left',
32520 getAutoCreate : function()
32522 var managerWidget = {
32524 cls : 'roo-document-manager',
32528 cls : 'roo-document-manager-selector',
32533 cls : 'roo-document-manager-uploader',
32537 cls : 'roo-document-manager-upload-btn',
32538 html : '<i class="fa fa-plus"></i>'
32549 cls : 'column col-md-12',
32554 if(this.fieldLabel.length){
32559 cls : 'column col-md-12',
32560 html : this.fieldLabel
32564 cls : 'column col-md-12',
32569 if(this.labelAlign == 'left'){
32574 html : this.fieldLabel
32583 if(this.labelWidth > 12){
32584 content[0].style = "width: " + this.labelWidth + 'px';
32587 if(this.labelWidth < 13 && this.labelmd == 0){
32588 this.labelmd = this.labelWidth;
32591 if(this.labellg > 0){
32592 content[0].cls += ' col-lg-' + this.labellg;
32593 content[1].cls += ' col-lg-' + (12 - this.labellg);
32596 if(this.labelmd > 0){
32597 content[0].cls += ' col-md-' + this.labelmd;
32598 content[1].cls += ' col-md-' + (12 - this.labelmd);
32601 if(this.labelsm > 0){
32602 content[0].cls += ' col-sm-' + this.labelsm;
32603 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32606 if(this.labelxs > 0){
32607 content[0].cls += ' col-xs-' + this.labelxs;
32608 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32616 cls : 'row clearfix',
32624 initEvents : function()
32626 this.managerEl = this.el.select('.roo-document-manager', true).first();
32627 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32629 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32630 this.selectorEl.hide();
32633 this.selectorEl.attr('multiple', 'multiple');
32636 this.selectorEl.on('change', this.onFileSelected, this);
32638 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32639 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32641 this.uploader.on('click', this.onUploaderClick, this);
32643 this.renderProgressDialog();
32647 window.addEventListener("resize", function() { _this.refresh(); } );
32649 this.fireEvent('initial', this);
32652 renderProgressDialog : function()
32656 this.progressDialog = new Roo.bootstrap.Modal({
32657 cls : 'roo-document-manager-progress-dialog',
32658 allow_close : false,
32669 btnclick : function() {
32670 _this.uploadCancel();
32676 this.progressDialog.render(Roo.get(document.body));
32678 this.progress = new Roo.bootstrap.Progress({
32679 cls : 'roo-document-manager-progress',
32684 this.progress.render(this.progressDialog.getChildContainer());
32686 this.progressBar = new Roo.bootstrap.ProgressBar({
32687 cls : 'roo-document-manager-progress-bar',
32690 aria_valuemax : 12,
32694 this.progressBar.render(this.progress.getChildContainer());
32697 onUploaderClick : function(e)
32699 e.preventDefault();
32701 if(this.fireEvent('beforeselectfile', this) != false){
32702 this.selectorEl.dom.click();
32707 onFileSelected : function(e)
32709 e.preventDefault();
32711 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32715 Roo.each(this.selectorEl.dom.files, function(file){
32716 if(this.fireEvent('inspect', this, file) != false){
32717 this.files.push(file);
32727 this.selectorEl.dom.value = '';
32729 if(!this.files || !this.files.length){
32733 if(this.boxes > 0 && this.files.length > this.boxes){
32734 this.files = this.files.slice(0, this.boxes);
32737 this.uploader.show();
32739 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32740 this.uploader.hide();
32749 Roo.each(this.files, function(file){
32751 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32752 var f = this.renderPreview(file);
32757 if(file.type.indexOf('image') != -1){
32758 this.delegates.push(
32760 _this.process(file);
32761 }).createDelegate(this)
32769 _this.process(file);
32770 }).createDelegate(this)
32775 this.files = files;
32777 this.delegates = this.delegates.concat(docs);
32779 if(!this.delegates.length){
32784 this.progressBar.aria_valuemax = this.delegates.length;
32791 arrange : function()
32793 if(!this.delegates.length){
32794 this.progressDialog.hide();
32799 var delegate = this.delegates.shift();
32801 this.progressDialog.show();
32803 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32805 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32810 refresh : function()
32812 this.uploader.show();
32814 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32815 this.uploader.hide();
32818 Roo.isTouch ? this.closable(false) : this.closable(true);
32820 this.fireEvent('refresh', this);
32823 onRemove : function(e, el, o)
32825 e.preventDefault();
32827 this.fireEvent('remove', this, o);
32831 remove : function(o)
32835 Roo.each(this.files, function(file){
32836 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32845 this.files = files;
32852 Roo.each(this.files, function(file){
32857 file.target.remove();
32866 onClick : function(e, el, o)
32868 e.preventDefault();
32870 this.fireEvent('click', this, o);
32874 closable : function(closable)
32876 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32878 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32890 xhrOnLoad : function(xhr)
32892 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32896 if (xhr.readyState !== 4) {
32898 this.fireEvent('exception', this, xhr);
32902 var response = Roo.decode(xhr.responseText);
32904 if(!response.success){
32906 this.fireEvent('exception', this, xhr);
32910 var file = this.renderPreview(response.data);
32912 this.files.push(file);
32916 this.fireEvent('afterupload', this, xhr);
32920 xhrOnError : function(xhr)
32922 Roo.log('xhr on error');
32924 var response = Roo.decode(xhr.responseText);
32931 process : function(file)
32933 if(this.fireEvent('process', this, file) !== false){
32934 if(this.editable && file.type.indexOf('image') != -1){
32935 this.fireEvent('edit', this, file);
32939 this.uploadStart(file, false);
32946 uploadStart : function(file, crop)
32948 this.xhr = new XMLHttpRequest();
32950 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32955 file.xhr = this.xhr;
32957 this.managerEl.createChild({
32959 cls : 'roo-document-manager-loading',
32963 tooltip : file.name,
32964 cls : 'roo-document-manager-thumb',
32965 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32971 this.xhr.open(this.method, this.url, true);
32974 "Accept": "application/json",
32975 "Cache-Control": "no-cache",
32976 "X-Requested-With": "XMLHttpRequest"
32979 for (var headerName in headers) {
32980 var headerValue = headers[headerName];
32982 this.xhr.setRequestHeader(headerName, headerValue);
32988 this.xhr.onload = function()
32990 _this.xhrOnLoad(_this.xhr);
32993 this.xhr.onerror = function()
32995 _this.xhrOnError(_this.xhr);
32998 var formData = new FormData();
33000 formData.append('returnHTML', 'NO');
33003 formData.append('crop', crop);
33006 formData.append(this.paramName, file, file.name);
33013 if(this.fireEvent('prepare', this, formData, options) != false){
33015 if(options.manually){
33019 this.xhr.send(formData);
33023 this.uploadCancel();
33026 uploadCancel : function()
33032 this.delegates = [];
33034 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33041 renderPreview : function(file)
33043 if(typeof(file.target) != 'undefined' && file.target){
33047 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33049 var previewEl = this.managerEl.createChild({
33051 cls : 'roo-document-manager-preview',
33055 tooltip : file[this.toolTipName],
33056 cls : 'roo-document-manager-thumb',
33057 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33062 html : '<i class="fa fa-times-circle"></i>'
33067 var close = previewEl.select('button.close', true).first();
33069 close.on('click', this.onRemove, this, file);
33071 file.target = previewEl;
33073 var image = previewEl.select('img', true).first();
33077 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33079 image.on('click', this.onClick, this, file);
33081 this.fireEvent('previewrendered', this, file);
33087 onPreviewLoad : function(file, image)
33089 if(typeof(file.target) == 'undefined' || !file.target){
33093 var width = image.dom.naturalWidth || image.dom.width;
33094 var height = image.dom.naturalHeight || image.dom.height;
33096 if(!this.previewResize) {
33100 if(width > height){
33101 file.target.addClass('wide');
33105 file.target.addClass('tall');
33110 uploadFromSource : function(file, crop)
33112 this.xhr = new XMLHttpRequest();
33114 this.managerEl.createChild({
33116 cls : 'roo-document-manager-loading',
33120 tooltip : file.name,
33121 cls : 'roo-document-manager-thumb',
33122 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33128 this.xhr.open(this.method, this.url, true);
33131 "Accept": "application/json",
33132 "Cache-Control": "no-cache",
33133 "X-Requested-With": "XMLHttpRequest"
33136 for (var headerName in headers) {
33137 var headerValue = headers[headerName];
33139 this.xhr.setRequestHeader(headerName, headerValue);
33145 this.xhr.onload = function()
33147 _this.xhrOnLoad(_this.xhr);
33150 this.xhr.onerror = function()
33152 _this.xhrOnError(_this.xhr);
33155 var formData = new FormData();
33157 formData.append('returnHTML', 'NO');
33159 formData.append('crop', crop);
33161 if(typeof(file.filename) != 'undefined'){
33162 formData.append('filename', file.filename);
33165 if(typeof(file.mimetype) != 'undefined'){
33166 formData.append('mimetype', file.mimetype);
33171 if(this.fireEvent('prepare', this, formData) != false){
33172 this.xhr.send(formData);
33182 * @class Roo.bootstrap.DocumentViewer
33183 * @extends Roo.bootstrap.Component
33184 * Bootstrap DocumentViewer class
33185 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33186 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33189 * Create a new DocumentViewer
33190 * @param {Object} config The config object
33193 Roo.bootstrap.DocumentViewer = function(config){
33194 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33199 * Fire after initEvent
33200 * @param {Roo.bootstrap.DocumentViewer} this
33206 * @param {Roo.bootstrap.DocumentViewer} this
33211 * Fire after download button
33212 * @param {Roo.bootstrap.DocumentViewer} this
33217 * Fire after trash button
33218 * @param {Roo.bootstrap.DocumentViewer} this
33225 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33227 showDownload : true,
33231 getAutoCreate : function()
33235 cls : 'roo-document-viewer',
33239 cls : 'roo-document-viewer-body',
33243 cls : 'roo-document-viewer-thumb',
33247 cls : 'roo-document-viewer-image'
33255 cls : 'roo-document-viewer-footer',
33258 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33262 cls : 'btn-group roo-document-viewer-download',
33266 cls : 'btn btn-default',
33267 html : '<i class="fa fa-download"></i>'
33273 cls : 'btn-group roo-document-viewer-trash',
33277 cls : 'btn btn-default',
33278 html : '<i class="fa fa-trash"></i>'
33291 initEvents : function()
33293 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33294 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33296 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33297 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33299 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33300 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33302 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33303 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33305 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33306 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33308 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33309 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33311 this.bodyEl.on('click', this.onClick, this);
33312 this.downloadBtn.on('click', this.onDownload, this);
33313 this.trashBtn.on('click', this.onTrash, this);
33315 this.downloadBtn.hide();
33316 this.trashBtn.hide();
33318 if(this.showDownload){
33319 this.downloadBtn.show();
33322 if(this.showTrash){
33323 this.trashBtn.show();
33326 if(!this.showDownload && !this.showTrash) {
33327 this.footerEl.hide();
33332 initial : function()
33334 this.fireEvent('initial', this);
33338 onClick : function(e)
33340 e.preventDefault();
33342 this.fireEvent('click', this);
33345 onDownload : function(e)
33347 e.preventDefault();
33349 this.fireEvent('download', this);
33352 onTrash : function(e)
33354 e.preventDefault();
33356 this.fireEvent('trash', this);
33368 * @class Roo.bootstrap.form.FieldLabel
33369 * @extends Roo.bootstrap.Component
33370 * Bootstrap FieldLabel class
33371 * @cfg {String} html contents of the element
33372 * @cfg {String} tag tag of the element default label
33373 * @cfg {String} cls class of the element
33374 * @cfg {String} target label target
33375 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33376 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33377 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33378 * @cfg {String} iconTooltip default "This field is required"
33379 * @cfg {String} indicatorpos (left|right) default left
33382 * Create a new FieldLabel
33383 * @param {Object} config The config object
33386 Roo.bootstrap.form.FieldLabel = function(config){
33387 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33392 * Fires after the field has been marked as invalid.
33393 * @param {Roo.form.FieldLabel} this
33394 * @param {String} msg The validation message
33399 * Fires after the field has been validated with no errors.
33400 * @param {Roo.form.FieldLabel} this
33406 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33413 invalidClass : 'has-warning',
33414 validClass : 'has-success',
33415 iconTooltip : 'This field is required',
33416 indicatorpos : 'left',
33418 getAutoCreate : function(){
33421 if (!this.allowBlank) {
33427 cls : 'roo-bootstrap-field-label ' + this.cls,
33432 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33433 tooltip : this.iconTooltip
33442 if(this.indicatorpos == 'right'){
33445 cls : 'roo-bootstrap-field-label ' + this.cls,
33454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33455 tooltip : this.iconTooltip
33464 initEvents: function()
33466 Roo.bootstrap.Element.superclass.initEvents.call(this);
33468 this.indicator = this.indicatorEl();
33470 if(this.indicator){
33471 this.indicator.removeClass('visible');
33472 this.indicator.addClass('invisible');
33475 Roo.bootstrap.form.FieldLabel.register(this);
33478 indicatorEl : function()
33480 var indicator = this.el.select('i.roo-required-indicator',true).first();
33491 * Mark this field as valid
33493 markValid : function()
33495 if(this.indicator){
33496 this.indicator.removeClass('visible');
33497 this.indicator.addClass('invisible');
33499 if (Roo.bootstrap.version == 3) {
33500 this.el.removeClass(this.invalidClass);
33501 this.el.addClass(this.validClass);
33503 this.el.removeClass('is-invalid');
33504 this.el.addClass('is-valid');
33508 this.fireEvent('valid', this);
33512 * Mark this field as invalid
33513 * @param {String} msg The validation message
33515 markInvalid : function(msg)
33517 if(this.indicator){
33518 this.indicator.removeClass('invisible');
33519 this.indicator.addClass('visible');
33521 if (Roo.bootstrap.version == 3) {
33522 this.el.removeClass(this.validClass);
33523 this.el.addClass(this.invalidClass);
33525 this.el.removeClass('is-valid');
33526 this.el.addClass('is-invalid');
33530 this.fireEvent('invalid', this, msg);
33536 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33541 * register a FieldLabel Group
33542 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33544 register : function(label)
33546 if(this.groups.hasOwnProperty(label.target)){
33550 this.groups[label.target] = label;
33554 * fetch a FieldLabel Group based on the target
33555 * @param {string} target
33556 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33558 get: function(target) {
33559 if (typeof(this.groups[target]) == 'undefined') {
33563 return this.groups[target] ;
33572 * page DateSplitField.
33578 * @class Roo.bootstrap.form.DateSplitField
33579 * @extends Roo.bootstrap.Component
33580 * Bootstrap DateSplitField class
33581 * @cfg {string} fieldLabel - the label associated
33582 * @cfg {Number} labelWidth set the width of label (0-12)
33583 * @cfg {String} labelAlign (top|left)
33584 * @cfg {Boolean} dayAllowBlank (true|false) default false
33585 * @cfg {Boolean} monthAllowBlank (true|false) default false
33586 * @cfg {Boolean} yearAllowBlank (true|false) default false
33587 * @cfg {string} dayPlaceholder
33588 * @cfg {string} monthPlaceholder
33589 * @cfg {string} yearPlaceholder
33590 * @cfg {string} dayFormat default 'd'
33591 * @cfg {string} monthFormat default 'm'
33592 * @cfg {string} yearFormat default 'Y'
33593 * @cfg {Number} labellg set the width of label (1-12)
33594 * @cfg {Number} labelmd set the width of label (1-12)
33595 * @cfg {Number} labelsm set the width of label (1-12)
33596 * @cfg {Number} labelxs set the width of label (1-12)
33600 * Create a new DateSplitField
33601 * @param {Object} config The config object
33604 Roo.bootstrap.form.DateSplitField = function(config){
33605 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33611 * getting the data of years
33612 * @param {Roo.bootstrap.form.DateSplitField} this
33613 * @param {Object} years
33618 * getting the data of days
33619 * @param {Roo.bootstrap.form.DateSplitField} this
33620 * @param {Object} days
33625 * Fires after the field has been marked as invalid.
33626 * @param {Roo.form.Field} this
33627 * @param {String} msg The validation message
33632 * Fires after the field has been validated with no errors.
33633 * @param {Roo.form.Field} this
33639 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33642 labelAlign : 'top',
33644 dayAllowBlank : false,
33645 monthAllowBlank : false,
33646 yearAllowBlank : false,
33647 dayPlaceholder : '',
33648 monthPlaceholder : '',
33649 yearPlaceholder : '',
33653 isFormField : true,
33659 getAutoCreate : function()
33663 cls : 'row roo-date-split-field-group',
33668 cls : 'form-hidden-field roo-date-split-field-group-value',
33674 var labelCls = 'col-md-12';
33675 var contentCls = 'col-md-4';
33677 if(this.fieldLabel){
33681 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33685 html : this.fieldLabel
33690 if(this.labelAlign == 'left'){
33692 if(this.labelWidth > 12){
33693 label.style = "width: " + this.labelWidth + 'px';
33696 if(this.labelWidth < 13 && this.labelmd == 0){
33697 this.labelmd = this.labelWidth;
33700 if(this.labellg > 0){
33701 labelCls = ' col-lg-' + this.labellg;
33702 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33705 if(this.labelmd > 0){
33706 labelCls = ' col-md-' + this.labelmd;
33707 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33710 if(this.labelsm > 0){
33711 labelCls = ' col-sm-' + this.labelsm;
33712 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33715 if(this.labelxs > 0){
33716 labelCls = ' col-xs-' + this.labelxs;
33717 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33721 label.cls += ' ' + labelCls;
33723 cfg.cn.push(label);
33726 Roo.each(['day', 'month', 'year'], function(t){
33729 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33736 inputEl: function ()
33738 return this.el.select('.roo-date-split-field-group-value', true).first();
33741 onRender : function(ct, position)
33745 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33747 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33749 this.dayField = new Roo.bootstrap.form.ComboBox({
33750 allowBlank : this.dayAllowBlank,
33751 alwaysQuery : true,
33752 displayField : 'value',
33755 forceSelection : true,
33757 placeholder : this.dayPlaceholder,
33758 selectOnFocus : true,
33759 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33760 triggerAction : 'all',
33762 valueField : 'value',
33763 store : new Roo.data.SimpleStore({
33764 data : (function() {
33766 _this.fireEvent('days', _this, days);
33769 fields : [ 'value' ]
33772 select : function (_self, record, index)
33774 _this.setValue(_this.getValue());
33779 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33781 this.monthField = new Roo.bootstrap.form.MonthField({
33782 after : '<i class=\"fa fa-calendar\"></i>',
33783 allowBlank : this.monthAllowBlank,
33784 placeholder : this.monthPlaceholder,
33787 render : function (_self)
33789 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33790 e.preventDefault();
33794 select : function (_self, oldvalue, newvalue)
33796 _this.setValue(_this.getValue());
33801 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33803 this.yearField = new Roo.bootstrap.form.ComboBox({
33804 allowBlank : this.yearAllowBlank,
33805 alwaysQuery : true,
33806 displayField : 'value',
33809 forceSelection : true,
33811 placeholder : this.yearPlaceholder,
33812 selectOnFocus : true,
33813 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33814 triggerAction : 'all',
33816 valueField : 'value',
33817 store : new Roo.data.SimpleStore({
33818 data : (function() {
33820 _this.fireEvent('years', _this, years);
33823 fields : [ 'value' ]
33826 select : function (_self, record, index)
33828 _this.setValue(_this.getValue());
33833 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33836 setValue : function(v, format)
33838 this.inputEl.dom.value = v;
33840 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33842 var d = Date.parseDate(v, f);
33849 this.setDay(d.format(this.dayFormat));
33850 this.setMonth(d.format(this.monthFormat));
33851 this.setYear(d.format(this.yearFormat));
33858 setDay : function(v)
33860 this.dayField.setValue(v);
33861 this.inputEl.dom.value = this.getValue();
33866 setMonth : function(v)
33868 this.monthField.setValue(v, true);
33869 this.inputEl.dom.value = this.getValue();
33874 setYear : function(v)
33876 this.yearField.setValue(v);
33877 this.inputEl.dom.value = this.getValue();
33882 getDay : function()
33884 return this.dayField.getValue();
33887 getMonth : function()
33889 return this.monthField.getValue();
33892 getYear : function()
33894 return this.yearField.getValue();
33897 getValue : function()
33899 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33901 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33911 this.inputEl.dom.value = '';
33916 validate : function()
33918 var d = this.dayField.validate();
33919 var m = this.monthField.validate();
33920 var y = this.yearField.validate();
33925 (!this.dayAllowBlank && !d) ||
33926 (!this.monthAllowBlank && !m) ||
33927 (!this.yearAllowBlank && !y)
33932 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33941 this.markInvalid();
33946 markValid : function()
33949 var label = this.el.select('label', true).first();
33950 var icon = this.el.select('i.fa-star', true).first();
33956 this.fireEvent('valid', this);
33960 * Mark this field as invalid
33961 * @param {String} msg The validation message
33963 markInvalid : function(msg)
33966 var label = this.el.select('label', true).first();
33967 var icon = this.el.select('i.fa-star', true).first();
33969 if(label && !icon){
33970 this.el.select('.roo-date-split-field-label', true).createChild({
33972 cls : 'text-danger fa fa-lg fa-star',
33973 tooltip : 'This field is required',
33974 style : 'margin-right:5px;'
33978 this.fireEvent('invalid', this, msg);
33981 clearInvalid : function()
33983 var label = this.el.select('label', true).first();
33984 var icon = this.el.select('i.fa-star', true).first();
33990 this.fireEvent('valid', this);
33993 getName: function()
34003 * @class Roo.bootstrap.LayoutMasonry
34004 * @extends Roo.bootstrap.Component
34005 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34006 * Bootstrap Layout Masonry class
34009 * http://masonry.desandro.com
34011 * The idea is to render all the bricks based on vertical width...
34013 * The original code extends 'outlayer' - we might need to use that....
34016 * Create a new Element
34017 * @param {Object} config The config object
34020 Roo.bootstrap.LayoutMasonry = function(config){
34022 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34026 Roo.bootstrap.LayoutMasonry.register(this);
34032 * Fire after layout the items
34033 * @param {Roo.bootstrap.LayoutMasonry} this
34034 * @param {Roo.EventObject} e
34041 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34044 * @cfg {Boolean} isLayoutInstant = no animation?
34046 isLayoutInstant : false, // needed?
34049 * @cfg {Number} boxWidth width of the columns
34054 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34059 * @cfg {Number} padWidth padding below box..
34064 * @cfg {Number} gutter gutter width..
34069 * @cfg {Number} maxCols maximum number of columns
34075 * @cfg {Boolean} isAutoInitial defalut true
34077 isAutoInitial : true,
34082 * @cfg {Boolean} isHorizontal defalut false
34084 isHorizontal : false,
34086 currentSize : null,
34092 bricks: null, //CompositeElement
34096 _isLayoutInited : false,
34098 // isAlternative : false, // only use for vertical layout...
34101 * @cfg {Number} alternativePadWidth padding below box..
34103 alternativePadWidth : 50,
34105 selectedBrick : [],
34107 getAutoCreate : function(){
34109 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34113 cls: 'blog-masonary-wrapper ' + this.cls,
34115 cls : 'mas-boxes masonary'
34122 getChildContainer: function( )
34124 if (this.boxesEl) {
34125 return this.boxesEl;
34128 this.boxesEl = this.el.select('.mas-boxes').first();
34130 return this.boxesEl;
34134 initEvents : function()
34138 if(this.isAutoInitial){
34139 Roo.log('hook children rendered');
34140 this.on('childrenrendered', function() {
34141 Roo.log('children rendered');
34147 initial : function()
34149 this.selectedBrick = [];
34151 this.currentSize = this.el.getBox(true);
34153 Roo.EventManager.onWindowResize(this.resize, this);
34155 if(!this.isAutoInitial){
34163 //this.layout.defer(500,this);
34167 resize : function()
34169 var cs = this.el.getBox(true);
34172 this.currentSize.width == cs.width &&
34173 this.currentSize.x == cs.x &&
34174 this.currentSize.height == cs.height &&
34175 this.currentSize.y == cs.y
34177 Roo.log("no change in with or X or Y");
34181 this.currentSize = cs;
34187 layout : function()
34189 this._resetLayout();
34191 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34193 this.layoutItems( isInstant );
34195 this._isLayoutInited = true;
34197 this.fireEvent('layout', this);
34201 _resetLayout : function()
34203 if(this.isHorizontal){
34204 this.horizontalMeasureColumns();
34208 this.verticalMeasureColumns();
34212 verticalMeasureColumns : function()
34214 this.getContainerWidth();
34216 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34217 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34221 var boxWidth = this.boxWidth + this.padWidth;
34223 if(this.containerWidth < this.boxWidth){
34224 boxWidth = this.containerWidth
34227 var containerWidth = this.containerWidth;
34229 var cols = Math.floor(containerWidth / boxWidth);
34231 this.cols = Math.max( cols, 1 );
34233 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34235 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34237 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34239 this.colWidth = boxWidth + avail - this.padWidth;
34241 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34242 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34245 horizontalMeasureColumns : function()
34247 this.getContainerWidth();
34249 var boxWidth = this.boxWidth;
34251 if(this.containerWidth < boxWidth){
34252 boxWidth = this.containerWidth;
34255 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34257 this.el.setHeight(boxWidth);
34261 getContainerWidth : function()
34263 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34266 layoutItems : function( isInstant )
34268 Roo.log(this.bricks);
34270 var items = Roo.apply([], this.bricks);
34272 if(this.isHorizontal){
34273 this._horizontalLayoutItems( items , isInstant );
34277 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34278 // this._verticalAlternativeLayoutItems( items , isInstant );
34282 this._verticalLayoutItems( items , isInstant );
34286 _verticalLayoutItems : function ( items , isInstant)
34288 if ( !items || !items.length ) {
34293 ['xs', 'xs', 'xs', 'tall'],
34294 ['xs', 'xs', 'tall'],
34295 ['xs', 'xs', 'sm'],
34296 ['xs', 'xs', 'xs'],
34302 ['sm', 'xs', 'xs'],
34306 ['tall', 'xs', 'xs', 'xs'],
34307 ['tall', 'xs', 'xs'],
34319 Roo.each(items, function(item, k){
34321 switch (item.size) {
34322 // these layouts take up a full box,
34333 boxes.push([item]);
34356 var filterPattern = function(box, length)
34364 var pattern = box.slice(0, length);
34368 Roo.each(pattern, function(i){
34369 format.push(i.size);
34372 Roo.each(standard, function(s){
34374 if(String(s) != String(format)){
34383 if(!match && length == 1){
34388 filterPattern(box, length - 1);
34392 queue.push(pattern);
34394 box = box.slice(length, box.length);
34396 filterPattern(box, 4);
34402 Roo.each(boxes, function(box, k){
34408 if(box.length == 1){
34413 filterPattern(box, 4);
34417 this._processVerticalLayoutQueue( queue, isInstant );
34421 // _verticalAlternativeLayoutItems : function( items , isInstant )
34423 // if ( !items || !items.length ) {
34427 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34431 _horizontalLayoutItems : function ( items , isInstant)
34433 if ( !items || !items.length || items.length < 3) {
34439 var eItems = items.slice(0, 3);
34441 items = items.slice(3, items.length);
34444 ['xs', 'xs', 'xs', 'wide'],
34445 ['xs', 'xs', 'wide'],
34446 ['xs', 'xs', 'sm'],
34447 ['xs', 'xs', 'xs'],
34453 ['sm', 'xs', 'xs'],
34457 ['wide', 'xs', 'xs', 'xs'],
34458 ['wide', 'xs', 'xs'],
34471 Roo.each(items, function(item, k){
34473 switch (item.size) {
34484 boxes.push([item]);
34508 var filterPattern = function(box, length)
34516 var pattern = box.slice(0, length);
34520 Roo.each(pattern, function(i){
34521 format.push(i.size);
34524 Roo.each(standard, function(s){
34526 if(String(s) != String(format)){
34535 if(!match && length == 1){
34540 filterPattern(box, length - 1);
34544 queue.push(pattern);
34546 box = box.slice(length, box.length);
34548 filterPattern(box, 4);
34554 Roo.each(boxes, function(box, k){
34560 if(box.length == 1){
34565 filterPattern(box, 4);
34572 var pos = this.el.getBox(true);
34576 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34578 var hit_end = false;
34580 Roo.each(queue, function(box){
34584 Roo.each(box, function(b){
34586 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34596 Roo.each(box, function(b){
34598 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34601 mx = Math.max(mx, b.x);
34605 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34609 Roo.each(box, function(b){
34611 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34625 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34628 /** Sets position of item in DOM
34629 * @param {Element} item
34630 * @param {Number} x - horizontal position
34631 * @param {Number} y - vertical position
34632 * @param {Boolean} isInstant - disables transitions
34634 _processVerticalLayoutQueue : function( queue, isInstant )
34636 var pos = this.el.getBox(true);
34641 for (var i = 0; i < this.cols; i++){
34645 Roo.each(queue, function(box, k){
34647 var col = k % this.cols;
34649 Roo.each(box, function(b,kk){
34651 b.el.position('absolute');
34653 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34654 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34656 if(b.size == 'md-left' || b.size == 'md-right'){
34657 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34658 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34661 b.el.setWidth(width);
34662 b.el.setHeight(height);
34664 b.el.select('iframe',true).setSize(width,height);
34668 for (var i = 0; i < this.cols; i++){
34670 if(maxY[i] < maxY[col]){
34675 col = Math.min(col, i);
34679 x = pos.x + col * (this.colWidth + this.padWidth);
34683 var positions = [];
34685 switch (box.length){
34687 positions = this.getVerticalOneBoxColPositions(x, y, box);
34690 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34693 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34696 positions = this.getVerticalFourBoxColPositions(x, y, box);
34702 Roo.each(box, function(b,kk){
34704 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34706 var sz = b.el.getSize();
34708 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34716 for (var i = 0; i < this.cols; i++){
34717 mY = Math.max(mY, maxY[i]);
34720 this.el.setHeight(mY - pos.y);
34724 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34726 // var pos = this.el.getBox(true);
34729 // var maxX = pos.right;
34731 // var maxHeight = 0;
34733 // Roo.each(items, function(item, k){
34737 // item.el.position('absolute');
34739 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34741 // item.el.setWidth(width);
34743 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34745 // item.el.setHeight(height);
34748 // item.el.setXY([x, y], isInstant ? false : true);
34750 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34753 // y = y + height + this.alternativePadWidth;
34755 // maxHeight = maxHeight + height + this.alternativePadWidth;
34759 // this.el.setHeight(maxHeight);
34763 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34765 var pos = this.el.getBox(true);
34770 var maxX = pos.right;
34772 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34774 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34776 Roo.each(queue, function(box, k){
34778 Roo.each(box, function(b, kk){
34780 b.el.position('absolute');
34782 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34783 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34785 if(b.size == 'md-left' || b.size == 'md-right'){
34786 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34787 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34790 b.el.setWidth(width);
34791 b.el.setHeight(height);
34799 var positions = [];
34801 switch (box.length){
34803 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34806 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34809 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34812 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34818 Roo.each(box, function(b,kk){
34820 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34822 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34830 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34832 Roo.each(eItems, function(b,k){
34834 b.size = (k == 0) ? 'sm' : 'xs';
34835 b.x = (k == 0) ? 2 : 1;
34836 b.y = (k == 0) ? 2 : 1;
34838 b.el.position('absolute');
34840 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34842 b.el.setWidth(width);
34844 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34846 b.el.setHeight(height);
34850 var positions = [];
34853 x : maxX - this.unitWidth * 2 - this.gutter,
34858 x : maxX - this.unitWidth,
34859 y : minY + (this.unitWidth + this.gutter) * 2
34863 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34867 Roo.each(eItems, function(b,k){
34869 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34875 getVerticalOneBoxColPositions : function(x, y, box)
34879 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34881 if(box[0].size == 'md-left'){
34885 if(box[0].size == 'md-right'){
34890 x : x + (this.unitWidth + this.gutter) * rand,
34897 getVerticalTwoBoxColPositions : function(x, y, box)
34901 if(box[0].size == 'xs'){
34905 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34909 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34923 x : x + (this.unitWidth + this.gutter) * 2,
34924 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34931 getVerticalThreeBoxColPositions : function(x, y, box)
34935 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34943 x : x + (this.unitWidth + this.gutter) * 1,
34948 x : x + (this.unitWidth + this.gutter) * 2,
34956 if(box[0].size == 'xs' && box[1].size == 'xs'){
34965 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34969 x : x + (this.unitWidth + this.gutter) * 1,
34983 x : x + (this.unitWidth + this.gutter) * 2,
34988 x : x + (this.unitWidth + this.gutter) * 2,
34989 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34996 getVerticalFourBoxColPositions : function(x, y, box)
35000 if(box[0].size == 'xs'){
35009 y : y + (this.unitHeight + this.gutter) * 1
35014 y : y + (this.unitHeight + this.gutter) * 2
35018 x : x + (this.unitWidth + this.gutter) * 1,
35032 x : x + (this.unitWidth + this.gutter) * 2,
35037 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35038 y : y + (this.unitHeight + this.gutter) * 1
35042 x : x + (this.unitWidth + this.gutter) * 2,
35043 y : y + (this.unitWidth + this.gutter) * 2
35050 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35054 if(box[0].size == 'md-left'){
35056 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35063 if(box[0].size == 'md-right'){
35065 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35066 y : minY + (this.unitWidth + this.gutter) * 1
35072 var rand = Math.floor(Math.random() * (4 - box[0].y));
35075 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35076 y : minY + (this.unitWidth + this.gutter) * rand
35083 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35087 if(box[0].size == 'xs'){
35090 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35095 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35096 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35104 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35109 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35110 y : minY + (this.unitWidth + this.gutter) * 2
35117 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35121 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35124 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35129 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35130 y : minY + (this.unitWidth + this.gutter) * 1
35134 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35135 y : minY + (this.unitWidth + this.gutter) * 2
35142 if(box[0].size == 'xs' && box[1].size == 'xs'){
35145 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35150 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35155 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35156 y : minY + (this.unitWidth + this.gutter) * 1
35164 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35169 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35170 y : minY + (this.unitWidth + this.gutter) * 2
35174 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35175 y : minY + (this.unitWidth + this.gutter) * 2
35182 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35186 if(box[0].size == 'xs'){
35189 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35194 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35199 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),
35204 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35205 y : minY + (this.unitWidth + this.gutter) * 1
35213 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35218 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35219 y : minY + (this.unitWidth + this.gutter) * 2
35223 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35224 y : minY + (this.unitWidth + this.gutter) * 2
35228 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),
35229 y : minY + (this.unitWidth + this.gutter) * 2
35237 * remove a Masonry Brick
35238 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35240 removeBrick : function(brick_id)
35246 for (var i = 0; i<this.bricks.length; i++) {
35247 if (this.bricks[i].id == brick_id) {
35248 this.bricks.splice(i,1);
35249 this.el.dom.removeChild(Roo.get(brick_id).dom);
35256 * adds a Masonry Brick
35257 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35259 addBrick : function(cfg)
35261 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35262 //this.register(cn);
35263 cn.parentId = this.id;
35264 cn.render(this.el);
35269 * register a Masonry Brick
35270 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35273 register : function(brick)
35275 this.bricks.push(brick);
35276 brick.masonryId = this.id;
35280 * clear all the Masonry Brick
35282 clearAll : function()
35285 //this.getChildContainer().dom.innerHTML = "";
35286 this.el.dom.innerHTML = '';
35289 getSelected : function()
35291 if (!this.selectedBrick) {
35295 return this.selectedBrick;
35299 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35303 * register a Masonry Layout
35304 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35307 register : function(layout)
35309 this.groups[layout.id] = layout;
35312 * fetch a Masonry Layout based on the masonry layout ID
35313 * @param {string} the masonry layout to add
35314 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35317 get: function(layout_id) {
35318 if (typeof(this.groups[layout_id]) == 'undefined') {
35321 return this.groups[layout_id] ;
35333 * http://masonry.desandro.com
35335 * The idea is to render all the bricks based on vertical width...
35337 * The original code extends 'outlayer' - we might need to use that....
35343 * @class Roo.bootstrap.LayoutMasonryAuto
35344 * @extends Roo.bootstrap.Component
35345 * Bootstrap Layout Masonry class
35348 * Create a new Element
35349 * @param {Object} config The config object
35352 Roo.bootstrap.LayoutMasonryAuto = function(config){
35353 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35356 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35359 * @cfg {Boolean} isFitWidth - resize the width..
35361 isFitWidth : false, // options..
35363 * @cfg {Boolean} isOriginLeft = left align?
35365 isOriginLeft : true,
35367 * @cfg {Boolean} isOriginTop = top align?
35369 isOriginTop : false,
35371 * @cfg {Boolean} isLayoutInstant = no animation?
35373 isLayoutInstant : false, // needed?
35375 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35377 isResizingContainer : true,
35379 * @cfg {Number} columnWidth width of the columns
35385 * @cfg {Number} maxCols maximum number of columns
35390 * @cfg {Number} padHeight padding below box..
35396 * @cfg {Boolean} isAutoInitial defalut true
35399 isAutoInitial : true,
35405 initialColumnWidth : 0,
35406 currentSize : null,
35408 colYs : null, // array.
35415 bricks: null, //CompositeElement
35416 cols : 0, // array?
35417 // element : null, // wrapped now this.el
35418 _isLayoutInited : null,
35421 getAutoCreate : function(){
35425 cls: 'blog-masonary-wrapper ' + this.cls,
35427 cls : 'mas-boxes masonary'
35434 getChildContainer: function( )
35436 if (this.boxesEl) {
35437 return this.boxesEl;
35440 this.boxesEl = this.el.select('.mas-boxes').first();
35442 return this.boxesEl;
35446 initEvents : function()
35450 if(this.isAutoInitial){
35451 Roo.log('hook children rendered');
35452 this.on('childrenrendered', function() {
35453 Roo.log('children rendered');
35460 initial : function()
35462 this.reloadItems();
35464 this.currentSize = this.el.getBox(true);
35466 /// was window resize... - let's see if this works..
35467 Roo.EventManager.onWindowResize(this.resize, this);
35469 if(!this.isAutoInitial){
35474 this.layout.defer(500,this);
35477 reloadItems: function()
35479 this.bricks = this.el.select('.masonry-brick', true);
35481 this.bricks.each(function(b) {
35482 //Roo.log(b.getSize());
35483 if (!b.attr('originalwidth')) {
35484 b.attr('originalwidth', b.getSize().width);
35489 Roo.log(this.bricks.elements.length);
35492 resize : function()
35495 var cs = this.el.getBox(true);
35497 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35498 Roo.log("no change in with or X");
35501 this.currentSize = cs;
35505 layout : function()
35508 this._resetLayout();
35509 //this._manageStamps();
35511 // don't animate first layout
35512 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35513 this.layoutItems( isInstant );
35515 // flag for initalized
35516 this._isLayoutInited = true;
35519 layoutItems : function( isInstant )
35521 //var items = this._getItemsForLayout( this.items );
35522 // original code supports filtering layout items.. we just ignore it..
35524 this._layoutItems( this.bricks , isInstant );
35526 this._postLayout();
35528 _layoutItems : function ( items , isInstant)
35530 //this.fireEvent( 'layout', this, items );
35533 if ( !items || !items.elements.length ) {
35534 // no items, emit event with empty array
35539 items.each(function(item) {
35540 Roo.log("layout item");
35542 // get x/y object from method
35543 var position = this._getItemLayoutPosition( item );
35545 position.item = item;
35546 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35547 queue.push( position );
35550 this._processLayoutQueue( queue );
35552 /** Sets position of item in DOM
35553 * @param {Element} item
35554 * @param {Number} x - horizontal position
35555 * @param {Number} y - vertical position
35556 * @param {Boolean} isInstant - disables transitions
35558 _processLayoutQueue : function( queue )
35560 for ( var i=0, len = queue.length; i < len; i++ ) {
35561 var obj = queue[i];
35562 obj.item.position('absolute');
35563 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35569 * Any logic you want to do after each layout,
35570 * i.e. size the container
35572 _postLayout : function()
35574 this.resizeContainer();
35577 resizeContainer : function()
35579 if ( !this.isResizingContainer ) {
35582 var size = this._getContainerSize();
35584 this.el.setSize(size.width,size.height);
35585 this.boxesEl.setSize(size.width,size.height);
35591 _resetLayout : function()
35593 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35594 this.colWidth = this.el.getWidth();
35595 //this.gutter = this.el.getWidth();
35597 this.measureColumns();
35603 this.colYs.push( 0 );
35609 measureColumns : function()
35611 this.getContainerWidth();
35612 // if columnWidth is 0, default to outerWidth of first item
35613 if ( !this.columnWidth ) {
35614 var firstItem = this.bricks.first();
35615 Roo.log(firstItem);
35616 this.columnWidth = this.containerWidth;
35617 if (firstItem && firstItem.attr('originalwidth') ) {
35618 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35620 // columnWidth fall back to item of first element
35621 Roo.log("set column width?");
35622 this.initialColumnWidth = this.columnWidth ;
35624 // if first elem has no width, default to size of container
35629 if (this.initialColumnWidth) {
35630 this.columnWidth = this.initialColumnWidth;
35635 // column width is fixed at the top - however if container width get's smaller we should
35638 // this bit calcs how man columns..
35640 var columnWidth = this.columnWidth += this.gutter;
35642 // calculate columns
35643 var containerWidth = this.containerWidth + this.gutter;
35645 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35646 // fix rounding errors, typically with gutters
35647 var excess = columnWidth - containerWidth % columnWidth;
35650 // if overshoot is less than a pixel, round up, otherwise floor it
35651 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35652 cols = Math[ mathMethod ]( cols );
35653 this.cols = Math.max( cols, 1 );
35654 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35656 // padding positioning..
35657 var totalColWidth = this.cols * this.columnWidth;
35658 var padavail = this.containerWidth - totalColWidth;
35659 // so for 2 columns - we need 3 'pads'
35661 var padNeeded = (1+this.cols) * this.padWidth;
35663 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35665 this.columnWidth += padExtra
35666 //this.padWidth = Math.floor(padavail / ( this.cols));
35668 // adjust colum width so that padding is fixed??
35670 // we have 3 columns ... total = width * 3
35671 // we have X left over... that should be used by
35673 //if (this.expandC) {
35681 getContainerWidth : function()
35683 /* // container is parent if fit width
35684 var container = this.isFitWidth ? this.element.parentNode : this.element;
35685 // check that this.size and size are there
35686 // IE8 triggers resize on body size change, so they might not be
35688 var size = getSize( container ); //FIXME
35689 this.containerWidth = size && size.innerWidth; //FIXME
35692 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35696 _getItemLayoutPosition : function( item ) // what is item?
35698 // we resize the item to our columnWidth..
35700 item.setWidth(this.columnWidth);
35701 item.autoBoxAdjust = false;
35703 var sz = item.getSize();
35705 // how many columns does this brick span
35706 var remainder = this.containerWidth % this.columnWidth;
35708 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35709 // round if off by 1 pixel, otherwise use ceil
35710 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35711 colSpan = Math.min( colSpan, this.cols );
35713 // normally this should be '1' as we dont' currently allow multi width columns..
35715 var colGroup = this._getColGroup( colSpan );
35716 // get the minimum Y value from the columns
35717 var minimumY = Math.min.apply( Math, colGroup );
35718 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35720 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35722 // position the brick
35724 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35725 y: this.currentSize.y + minimumY + this.padHeight
35729 // apply setHeight to necessary columns
35730 var setHeight = minimumY + sz.height + this.padHeight;
35731 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35733 var setSpan = this.cols + 1 - colGroup.length;
35734 for ( var i = 0; i < setSpan; i++ ) {
35735 this.colYs[ shortColIndex + i ] = setHeight ;
35742 * @param {Number} colSpan - number of columns the element spans
35743 * @returns {Array} colGroup
35745 _getColGroup : function( colSpan )
35747 if ( colSpan < 2 ) {
35748 // if brick spans only one column, use all the column Ys
35753 // how many different places could this brick fit horizontally
35754 var groupCount = this.cols + 1 - colSpan;
35755 // for each group potential horizontal position
35756 for ( var i = 0; i < groupCount; i++ ) {
35757 // make an array of colY values for that one group
35758 var groupColYs = this.colYs.slice( i, i + colSpan );
35759 // and get the max value of the array
35760 colGroup[i] = Math.max.apply( Math, groupColYs );
35765 _manageStamp : function( stamp )
35767 var stampSize = stamp.getSize();
35768 var offset = stamp.getBox();
35769 // get the columns that this stamp affects
35770 var firstX = this.isOriginLeft ? offset.x : offset.right;
35771 var lastX = firstX + stampSize.width;
35772 var firstCol = Math.floor( firstX / this.columnWidth );
35773 firstCol = Math.max( 0, firstCol );
35775 var lastCol = Math.floor( lastX / this.columnWidth );
35776 // lastCol should not go over if multiple of columnWidth #425
35777 lastCol -= lastX % this.columnWidth ? 0 : 1;
35778 lastCol = Math.min( this.cols - 1, lastCol );
35780 // set colYs to bottom of the stamp
35781 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35784 for ( var i = firstCol; i <= lastCol; i++ ) {
35785 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35790 _getContainerSize : function()
35792 this.maxY = Math.max.apply( Math, this.colYs );
35797 if ( this.isFitWidth ) {
35798 size.width = this._getContainerFitWidth();
35804 _getContainerFitWidth : function()
35806 var unusedCols = 0;
35807 // count unused columns
35810 if ( this.colYs[i] !== 0 ) {
35815 // fit container to columns that have been used
35816 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35819 needsResizeLayout : function()
35821 var previousWidth = this.containerWidth;
35822 this.getContainerWidth();
35823 return previousWidth !== this.containerWidth;
35838 * @class Roo.bootstrap.MasonryBrick
35839 * @extends Roo.bootstrap.Component
35840 * Bootstrap MasonryBrick class
35843 * Create a new MasonryBrick
35844 * @param {Object} config The config object
35847 Roo.bootstrap.MasonryBrick = function(config){
35849 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35851 Roo.bootstrap.MasonryBrick.register(this);
35857 * When a MasonryBrick is clcik
35858 * @param {Roo.bootstrap.MasonryBrick} this
35859 * @param {Roo.EventObject} e
35865 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35868 * @cfg {String} title
35872 * @cfg {String} html
35876 * @cfg {String} bgimage
35880 * @cfg {String} videourl
35884 * @cfg {String} cls
35888 * @cfg {String} href
35892 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35897 * @cfg {String} placetitle (center|bottom)
35902 * @cfg {Boolean} isFitContainer defalut true
35904 isFitContainer : true,
35907 * @cfg {Boolean} preventDefault defalut false
35909 preventDefault : false,
35912 * @cfg {Boolean} inverse defalut false
35914 maskInverse : false,
35916 getAutoCreate : function()
35918 if(!this.isFitContainer){
35919 return this.getSplitAutoCreate();
35922 var cls = 'masonry-brick masonry-brick-full';
35924 if(this.href.length){
35925 cls += ' masonry-brick-link';
35928 if(this.bgimage.length){
35929 cls += ' masonry-brick-image';
35932 if(this.maskInverse){
35933 cls += ' mask-inverse';
35936 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35937 cls += ' enable-mask';
35941 cls += ' masonry-' + this.size + '-brick';
35944 if(this.placetitle.length){
35946 switch (this.placetitle) {
35948 cls += ' masonry-center-title';
35951 cls += ' masonry-bottom-title';
35958 if(!this.html.length && !this.bgimage.length){
35959 cls += ' masonry-center-title';
35962 if(!this.html.length && this.bgimage.length){
35963 cls += ' masonry-bottom-title';
35968 cls += ' ' + this.cls;
35972 tag: (this.href.length) ? 'a' : 'div',
35977 cls: 'masonry-brick-mask'
35981 cls: 'masonry-brick-paragraph',
35987 if(this.href.length){
35988 cfg.href = this.href;
35991 var cn = cfg.cn[1].cn;
35993 if(this.title.length){
35996 cls: 'masonry-brick-title',
36001 if(this.html.length){
36004 cls: 'masonry-brick-text',
36009 if (!this.title.length && !this.html.length) {
36010 cfg.cn[1].cls += ' hide';
36013 if(this.bgimage.length){
36016 cls: 'masonry-brick-image-view',
36021 if(this.videourl.length){
36022 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36023 // youtube support only?
36026 cls: 'masonry-brick-image-view',
36029 allowfullscreen : true
36037 getSplitAutoCreate : function()
36039 var cls = 'masonry-brick masonry-brick-split';
36041 if(this.href.length){
36042 cls += ' masonry-brick-link';
36045 if(this.bgimage.length){
36046 cls += ' masonry-brick-image';
36050 cls += ' masonry-' + this.size + '-brick';
36053 switch (this.placetitle) {
36055 cls += ' masonry-center-title';
36058 cls += ' masonry-bottom-title';
36061 if(!this.bgimage.length){
36062 cls += ' masonry-center-title';
36065 if(this.bgimage.length){
36066 cls += ' masonry-bottom-title';
36072 cls += ' ' + this.cls;
36076 tag: (this.href.length) ? 'a' : 'div',
36081 cls: 'masonry-brick-split-head',
36085 cls: 'masonry-brick-paragraph',
36092 cls: 'masonry-brick-split-body',
36098 if(this.href.length){
36099 cfg.href = this.href;
36102 if(this.title.length){
36103 cfg.cn[0].cn[0].cn.push({
36105 cls: 'masonry-brick-title',
36110 if(this.html.length){
36111 cfg.cn[1].cn.push({
36113 cls: 'masonry-brick-text',
36118 if(this.bgimage.length){
36119 cfg.cn[0].cn.push({
36121 cls: 'masonry-brick-image-view',
36126 if(this.videourl.length){
36127 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36128 // youtube support only?
36129 cfg.cn[0].cn.cn.push({
36131 cls: 'masonry-brick-image-view',
36134 allowfullscreen : true
36141 initEvents: function()
36143 switch (this.size) {
36176 this.el.on('touchstart', this.onTouchStart, this);
36177 this.el.on('touchmove', this.onTouchMove, this);
36178 this.el.on('touchend', this.onTouchEnd, this);
36179 this.el.on('contextmenu', this.onContextMenu, this);
36181 this.el.on('mouseenter' ,this.enter, this);
36182 this.el.on('mouseleave', this.leave, this);
36183 this.el.on('click', this.onClick, this);
36186 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36187 this.parent().bricks.push(this);
36192 onClick: function(e, el)
36194 var time = this.endTimer - this.startTimer;
36195 // Roo.log(e.preventDefault());
36198 e.preventDefault();
36203 if(!this.preventDefault){
36207 e.preventDefault();
36209 if (this.activeClass != '') {
36210 this.selectBrick();
36213 this.fireEvent('click', this, e);
36216 enter: function(e, el)
36218 e.preventDefault();
36220 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36224 if(this.bgimage.length && this.html.length){
36225 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36229 leave: function(e, el)
36231 e.preventDefault();
36233 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36237 if(this.bgimage.length && this.html.length){
36238 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36242 onTouchStart: function(e, el)
36244 // e.preventDefault();
36246 this.touchmoved = false;
36248 if(!this.isFitContainer){
36252 if(!this.bgimage.length || !this.html.length){
36256 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36258 this.timer = new Date().getTime();
36262 onTouchMove: function(e, el)
36264 this.touchmoved = true;
36267 onContextMenu : function(e,el)
36269 e.preventDefault();
36270 e.stopPropagation();
36274 onTouchEnd: function(e, el)
36276 // e.preventDefault();
36278 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36285 if(!this.bgimage.length || !this.html.length){
36287 if(this.href.length){
36288 window.location.href = this.href;
36294 if(!this.isFitContainer){
36298 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36300 window.location.href = this.href;
36303 //selection on single brick only
36304 selectBrick : function() {
36306 if (!this.parentId) {
36310 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36311 var index = m.selectedBrick.indexOf(this.id);
36314 m.selectedBrick.splice(index,1);
36315 this.el.removeClass(this.activeClass);
36319 for(var i = 0; i < m.selectedBrick.length; i++) {
36320 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36321 b.el.removeClass(b.activeClass);
36324 m.selectedBrick = [];
36326 m.selectedBrick.push(this.id);
36327 this.el.addClass(this.activeClass);
36331 isSelected : function(){
36332 return this.el.hasClass(this.activeClass);
36337 Roo.apply(Roo.bootstrap.MasonryBrick, {
36340 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36342 * register a Masonry Brick
36343 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36346 register : function(brick)
36348 //this.groups[brick.id] = brick;
36349 this.groups.add(brick.id, brick);
36352 * fetch a masonry brick based on the masonry brick ID
36353 * @param {string} the masonry brick to add
36354 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36357 get: function(brick_id)
36359 // if (typeof(this.groups[brick_id]) == 'undefined') {
36362 // return this.groups[brick_id] ;
36364 if(this.groups.key(brick_id)) {
36365 return this.groups.key(brick_id);
36383 * @class Roo.bootstrap.Brick
36384 * @extends Roo.bootstrap.Component
36385 * Bootstrap Brick class
36388 * Create a new Brick
36389 * @param {Object} config The config object
36392 Roo.bootstrap.Brick = function(config){
36393 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36399 * When a Brick is click
36400 * @param {Roo.bootstrap.Brick} this
36401 * @param {Roo.EventObject} e
36407 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36410 * @cfg {String} title
36414 * @cfg {String} html
36418 * @cfg {String} bgimage
36422 * @cfg {String} cls
36426 * @cfg {String} href
36430 * @cfg {String} video
36434 * @cfg {Boolean} square
36438 getAutoCreate : function()
36440 var cls = 'roo-brick';
36442 if(this.href.length){
36443 cls += ' roo-brick-link';
36446 if(this.bgimage.length){
36447 cls += ' roo-brick-image';
36450 if(!this.html.length && !this.bgimage.length){
36451 cls += ' roo-brick-center-title';
36454 if(!this.html.length && this.bgimage.length){
36455 cls += ' roo-brick-bottom-title';
36459 cls += ' ' + this.cls;
36463 tag: (this.href.length) ? 'a' : 'div',
36468 cls: 'roo-brick-paragraph',
36474 if(this.href.length){
36475 cfg.href = this.href;
36478 var cn = cfg.cn[0].cn;
36480 if(this.title.length){
36483 cls: 'roo-brick-title',
36488 if(this.html.length){
36491 cls: 'roo-brick-text',
36498 if(this.bgimage.length){
36501 cls: 'roo-brick-image-view',
36509 initEvents: function()
36511 if(this.title.length || this.html.length){
36512 this.el.on('mouseenter' ,this.enter, this);
36513 this.el.on('mouseleave', this.leave, this);
36516 Roo.EventManager.onWindowResize(this.resize, this);
36518 if(this.bgimage.length){
36519 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36520 this.imageEl.on('load', this.onImageLoad, this);
36527 onImageLoad : function()
36532 resize : function()
36534 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36536 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36538 if(this.bgimage.length){
36539 var image = this.el.select('.roo-brick-image-view', true).first();
36541 image.setWidth(paragraph.getWidth());
36544 image.setHeight(paragraph.getWidth());
36547 this.el.setHeight(image.getHeight());
36548 paragraph.setHeight(image.getHeight());
36554 enter: function(e, el)
36556 e.preventDefault();
36558 if(this.bgimage.length){
36559 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36560 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36564 leave: function(e, el)
36566 e.preventDefault();
36568 if(this.bgimage.length){
36569 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36570 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36585 * @class Roo.bootstrap.form.NumberField
36586 * @extends Roo.bootstrap.form.Input
36587 * Bootstrap NumberField class
36593 * Create a new NumberField
36594 * @param {Object} config The config object
36597 Roo.bootstrap.form.NumberField = function(config){
36598 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36601 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36604 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36606 allowDecimals : true,
36608 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36610 decimalSeparator : ".",
36612 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36614 decimalPrecision : 2,
36616 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36618 allowNegative : true,
36621 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36625 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36627 minValue : Number.NEGATIVE_INFINITY,
36629 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36631 maxValue : Number.MAX_VALUE,
36633 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36635 minText : "The minimum value for this field is {0}",
36637 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36639 maxText : "The maximum value for this field is {0}",
36641 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36642 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36644 nanText : "{0} is not a valid number",
36646 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36648 thousandsDelimiter : false,
36650 * @cfg {String} valueAlign alignment of value
36652 valueAlign : "left",
36654 getAutoCreate : function()
36656 var hiddenInput = {
36660 cls: 'hidden-number-input'
36664 hiddenInput.name = this.name;
36669 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36671 this.name = hiddenInput.name;
36673 if(cfg.cn.length > 0) {
36674 cfg.cn.push(hiddenInput);
36681 initEvents : function()
36683 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36685 var allowed = "0123456789";
36687 if(this.allowDecimals){
36688 allowed += this.decimalSeparator;
36691 if(this.allowNegative){
36695 if(this.thousandsDelimiter) {
36699 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36701 var keyPress = function(e){
36703 var k = e.getKey();
36705 var c = e.getCharCode();
36708 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36709 allowed.indexOf(String.fromCharCode(c)) === -1
36715 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36719 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36724 this.el.on("keypress", keyPress, this);
36727 validateValue : function(value)
36730 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36734 var num = this.parseValue(value);
36737 this.markInvalid(String.format(this.nanText, value));
36741 if(num < this.minValue){
36742 this.markInvalid(String.format(this.minText, this.minValue));
36746 if(num > this.maxValue){
36747 this.markInvalid(String.format(this.maxText, this.maxValue));
36754 getValue : function()
36756 var v = this.hiddenEl().getValue();
36758 return this.fixPrecision(this.parseValue(v));
36761 parseValue : function(value)
36763 if(this.thousandsDelimiter) {
36765 r = new RegExp(",", "g");
36766 value = value.replace(r, "");
36769 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36770 return isNaN(value) ? '' : value;
36773 fixPrecision : function(value)
36775 if(this.thousandsDelimiter) {
36777 r = new RegExp(",", "g");
36778 value = value.replace(r, "");
36781 var nan = isNaN(value);
36783 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36784 return nan ? '' : value;
36786 return parseFloat(value).toFixed(this.decimalPrecision);
36789 setValue : function(v)
36791 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36797 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36799 this.inputEl().dom.value = (v == '') ? '' :
36800 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36802 if(!this.allowZero && v === '0') {
36803 this.hiddenEl().dom.value = '';
36804 this.inputEl().dom.value = '';
36811 decimalPrecisionFcn : function(v)
36813 return Math.floor(v);
36816 beforeBlur : function()
36818 var v = this.parseValue(this.getRawValue());
36820 if(v || v === 0 || v === ''){
36825 hiddenEl : function()
36827 return this.el.select('input.hidden-number-input',true).first();
36839 * @class Roo.bootstrap.DocumentSlider
36840 * @extends Roo.bootstrap.Component
36841 * Bootstrap DocumentSlider class
36844 * Create a new DocumentViewer
36845 * @param {Object} config The config object
36848 Roo.bootstrap.DocumentSlider = function(config){
36849 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36856 * Fire after initEvent
36857 * @param {Roo.bootstrap.DocumentSlider} this
36862 * Fire after update
36863 * @param {Roo.bootstrap.DocumentSlider} this
36869 * @param {Roo.bootstrap.DocumentSlider} this
36875 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36881 getAutoCreate : function()
36885 cls : 'roo-document-slider',
36889 cls : 'roo-document-slider-header',
36893 cls : 'roo-document-slider-header-title'
36899 cls : 'roo-document-slider-body',
36903 cls : 'roo-document-slider-prev',
36907 cls : 'fa fa-chevron-left'
36913 cls : 'roo-document-slider-thumb',
36917 cls : 'roo-document-slider-image'
36923 cls : 'roo-document-slider-next',
36927 cls : 'fa fa-chevron-right'
36939 initEvents : function()
36941 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36942 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36944 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36945 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36947 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36948 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36950 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36951 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36953 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36954 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36956 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36957 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36959 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36960 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36962 this.thumbEl.on('click', this.onClick, this);
36964 this.prevIndicator.on('click', this.prev, this);
36966 this.nextIndicator.on('click', this.next, this);
36970 initial : function()
36972 if(this.files.length){
36973 this.indicator = 1;
36977 this.fireEvent('initial', this);
36980 update : function()
36982 this.imageEl.attr('src', this.files[this.indicator - 1]);
36984 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36986 this.prevIndicator.show();
36988 if(this.indicator == 1){
36989 this.prevIndicator.hide();
36992 this.nextIndicator.show();
36994 if(this.indicator == this.files.length){
36995 this.nextIndicator.hide();
36998 this.thumbEl.scrollTo('top');
37000 this.fireEvent('update', this);
37003 onClick : function(e)
37005 e.preventDefault();
37007 this.fireEvent('click', this);
37012 e.preventDefault();
37014 this.indicator = Math.max(1, this.indicator - 1);
37021 e.preventDefault();
37023 this.indicator = Math.min(this.files.length, this.indicator + 1);
37037 * @class Roo.bootstrap.form.RadioSet
37038 * @extends Roo.bootstrap.form.Input
37039 * @children Roo.bootstrap.form.Radio
37040 * Bootstrap RadioSet class
37041 * @cfg {String} indicatorpos (left|right) default left
37042 * @cfg {Boolean} inline (true|false) inline the element (default true)
37043 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37045 * Create a new RadioSet
37046 * @param {Object} config The config object
37049 Roo.bootstrap.form.RadioSet = function(config){
37051 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37055 Roo.bootstrap.form.RadioSet.register(this);
37060 * Fires when the element is checked or unchecked.
37061 * @param {Roo.bootstrap.form.RadioSet} this This radio
37062 * @param {Roo.bootstrap.form.Radio} item The checked item
37067 * Fires when the element is click.
37068 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37069 * @param {Roo.bootstrap.form.Radio} item The checked item
37070 * @param {Roo.EventObject} e The event object
37077 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37085 indicatorpos : 'left',
37087 getAutoCreate : function()
37091 cls : 'roo-radio-set-label',
37095 html : this.fieldLabel
37099 if (Roo.bootstrap.version == 3) {
37102 if(this.indicatorpos == 'left'){
37105 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37106 tooltip : 'This field is required'
37111 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37112 tooltip : 'This field is required'
37118 cls : 'roo-radio-set-items'
37121 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37123 if (align === 'left' && this.fieldLabel.length) {
37126 cls : "roo-radio-set-right",
37132 if(this.labelWidth > 12){
37133 label.style = "width: " + this.labelWidth + 'px';
37136 if(this.labelWidth < 13 && this.labelmd == 0){
37137 this.labelmd = this.labelWidth;
37140 if(this.labellg > 0){
37141 label.cls += ' col-lg-' + this.labellg;
37142 items.cls += ' col-lg-' + (12 - this.labellg);
37145 if(this.labelmd > 0){
37146 label.cls += ' col-md-' + this.labelmd;
37147 items.cls += ' col-md-' + (12 - this.labelmd);
37150 if(this.labelsm > 0){
37151 label.cls += ' col-sm-' + this.labelsm;
37152 items.cls += ' col-sm-' + (12 - this.labelsm);
37155 if(this.labelxs > 0){
37156 label.cls += ' col-xs-' + this.labelxs;
37157 items.cls += ' col-xs-' + (12 - this.labelxs);
37163 cls : 'roo-radio-set',
37167 cls : 'roo-radio-set-input',
37170 value : this.value ? this.value : ''
37177 if(this.weight.length){
37178 cfg.cls += ' roo-radio-' + this.weight;
37182 cfg.cls += ' roo-radio-set-inline';
37186 ['xs','sm','md','lg'].map(function(size){
37187 if (settings[size]) {
37188 cfg.cls += ' col-' + size + '-' + settings[size];
37196 initEvents : function()
37198 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37199 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37201 if(!this.fieldLabel.length){
37202 this.labelEl.hide();
37205 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37206 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37208 this.indicator = this.indicatorEl();
37210 if(this.indicator){
37211 this.indicator.addClass('invisible');
37214 this.originalValue = this.getValue();
37218 inputEl: function ()
37220 return this.el.select('.roo-radio-set-input', true).first();
37223 getChildContainer : function()
37225 return this.itemsEl;
37228 register : function(item)
37230 this.radioes.push(item);
37234 validate : function()
37236 if(this.getVisibilityEl().hasClass('hidden')){
37242 Roo.each(this.radioes, function(i){
37251 if(this.allowBlank) {
37255 if(this.disabled || valid){
37260 this.markInvalid();
37265 markValid : function()
37267 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37268 this.indicatorEl().removeClass('visible');
37269 this.indicatorEl().addClass('invisible');
37273 if (Roo.bootstrap.version == 3) {
37274 this.el.removeClass([this.invalidClass, this.validClass]);
37275 this.el.addClass(this.validClass);
37277 this.el.removeClass(['is-invalid','is-valid']);
37278 this.el.addClass(['is-valid']);
37280 this.fireEvent('valid', this);
37283 markInvalid : function(msg)
37285 if(this.allowBlank || this.disabled){
37289 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37290 this.indicatorEl().removeClass('invisible');
37291 this.indicatorEl().addClass('visible');
37293 if (Roo.bootstrap.version == 3) {
37294 this.el.removeClass([this.invalidClass, this.validClass]);
37295 this.el.addClass(this.invalidClass);
37297 this.el.removeClass(['is-invalid','is-valid']);
37298 this.el.addClass(['is-invalid']);
37301 this.fireEvent('invalid', this, msg);
37305 setValue : function(v, suppressEvent)
37307 if(this.value === v){
37314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37317 Roo.each(this.radioes, function(i){
37319 i.el.removeClass('checked');
37322 Roo.each(this.radioes, function(i){
37324 if(i.value === v || i.value.toString() === v.toString()){
37326 i.el.addClass('checked');
37328 if(suppressEvent !== true){
37329 this.fireEvent('check', this, i);
37340 clearInvalid : function(){
37342 if(!this.el || this.preventMark){
37346 this.el.removeClass([this.invalidClass]);
37348 this.fireEvent('valid', this);
37353 Roo.apply(Roo.bootstrap.form.RadioSet, {
37357 register : function(set)
37359 this.groups[set.name] = set;
37362 get: function(name)
37364 if (typeof(this.groups[name]) == 'undefined') {
37368 return this.groups[name] ;
37374 * Ext JS Library 1.1.1
37375 * Copyright(c) 2006-2007, Ext JS, LLC.
37377 * Originally Released Under LGPL - original licence link has changed is not relivant.
37380 * <script type="text/javascript">
37385 * @class Roo.bootstrap.SplitBar
37386 * @extends Roo.util.Observable
37387 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37391 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37392 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37393 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37394 split.minSize = 100;
37395 split.maxSize = 600;
37396 split.animate = true;
37397 split.on('moved', splitterMoved);
37400 * Create a new SplitBar
37401 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37402 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37403 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37404 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37405 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37406 position of the SplitBar).
37408 Roo.bootstrap.SplitBar = function(cfg){
37413 // dragElement : elm
37414 // resizingElement: el,
37416 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37417 // placement : Roo.bootstrap.SplitBar.LEFT ,
37418 // existingProxy ???
37421 this.el = Roo.get(cfg.dragElement, true);
37422 this.el.dom.unselectable = "on";
37424 this.resizingEl = Roo.get(cfg.resizingElement, true);
37428 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37429 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37432 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37435 * The minimum size of the resizing element. (Defaults to 0)
37441 * The maximum size of the resizing element. (Defaults to 2000)
37444 this.maxSize = 2000;
37447 * Whether to animate the transition to the new size
37450 this.animate = false;
37453 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37456 this.useShim = false;
37461 if(!cfg.existingProxy){
37463 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37465 this.proxy = Roo.get(cfg.existingProxy).dom;
37468 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37471 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37474 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37477 this.dragSpecs = {};
37480 * @private The adapter to use to positon and resize elements
37482 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37483 this.adapter.init(this);
37485 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37487 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37488 this.el.addClass("roo-splitbar-h");
37491 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37492 this.el.addClass("roo-splitbar-v");
37498 * Fires when the splitter is moved (alias for {@link #event-moved})
37499 * @param {Roo.bootstrap.SplitBar} this
37500 * @param {Number} newSize the new width or height
37505 * Fires when the splitter is moved
37506 * @param {Roo.bootstrap.SplitBar} this
37507 * @param {Number} newSize the new width or height
37511 * @event beforeresize
37512 * Fires before the splitter is dragged
37513 * @param {Roo.bootstrap.SplitBar} this
37515 "beforeresize" : true,
37517 "beforeapply" : true
37520 Roo.util.Observable.call(this);
37523 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37524 onStartProxyDrag : function(x, y){
37525 this.fireEvent("beforeresize", this);
37527 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37529 o.enableDisplayMode("block");
37530 // all splitbars share the same overlay
37531 Roo.bootstrap.SplitBar.prototype.overlay = o;
37533 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37534 this.overlay.show();
37535 Roo.get(this.proxy).setDisplayed("block");
37536 var size = this.adapter.getElementSize(this);
37537 this.activeMinSize = this.getMinimumSize();;
37538 this.activeMaxSize = this.getMaximumSize();;
37539 var c1 = size - this.activeMinSize;
37540 var c2 = Math.max(this.activeMaxSize - size, 0);
37541 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37542 this.dd.resetConstraints();
37543 this.dd.setXConstraint(
37544 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37545 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37547 this.dd.setYConstraint(0, 0);
37549 this.dd.resetConstraints();
37550 this.dd.setXConstraint(0, 0);
37551 this.dd.setYConstraint(
37552 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37553 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37556 this.dragSpecs.startSize = size;
37557 this.dragSpecs.startPoint = [x, y];
37558 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37562 * @private Called after the drag operation by the DDProxy
37564 onEndProxyDrag : function(e){
37565 Roo.get(this.proxy).setDisplayed(false);
37566 var endPoint = Roo.lib.Event.getXY(e);
37568 this.overlay.hide();
37571 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37572 newSize = this.dragSpecs.startSize +
37573 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37574 endPoint[0] - this.dragSpecs.startPoint[0] :
37575 this.dragSpecs.startPoint[0] - endPoint[0]
37578 newSize = this.dragSpecs.startSize +
37579 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37580 endPoint[1] - this.dragSpecs.startPoint[1] :
37581 this.dragSpecs.startPoint[1] - endPoint[1]
37584 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37585 if(newSize != this.dragSpecs.startSize){
37586 if(this.fireEvent('beforeapply', this, newSize) !== false){
37587 this.adapter.setElementSize(this, newSize);
37588 this.fireEvent("moved", this, newSize);
37589 this.fireEvent("resize", this, newSize);
37595 * Get the adapter this SplitBar uses
37596 * @return The adapter object
37598 getAdapter : function(){
37599 return this.adapter;
37603 * Set the adapter this SplitBar uses
37604 * @param {Object} adapter A SplitBar adapter object
37606 setAdapter : function(adapter){
37607 this.adapter = adapter;
37608 this.adapter.init(this);
37612 * Gets the minimum size for the resizing element
37613 * @return {Number} The minimum size
37615 getMinimumSize : function(){
37616 return this.minSize;
37620 * Sets the minimum size for the resizing element
37621 * @param {Number} minSize The minimum size
37623 setMinimumSize : function(minSize){
37624 this.minSize = minSize;
37628 * Gets the maximum size for the resizing element
37629 * @return {Number} The maximum size
37631 getMaximumSize : function(){
37632 return this.maxSize;
37636 * Sets the maximum size for the resizing element
37637 * @param {Number} maxSize The maximum size
37639 setMaximumSize : function(maxSize){
37640 this.maxSize = maxSize;
37644 * Sets the initialize size for the resizing element
37645 * @param {Number} size The initial size
37647 setCurrentSize : function(size){
37648 var oldAnimate = this.animate;
37649 this.animate = false;
37650 this.adapter.setElementSize(this, size);
37651 this.animate = oldAnimate;
37655 * Destroy this splitbar.
37656 * @param {Boolean} removeEl True to remove the element
37658 destroy : function(removeEl){
37660 this.shim.remove();
37663 this.proxy.parentNode.removeChild(this.proxy);
37671 * @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.
37673 Roo.bootstrap.SplitBar.createProxy = function(dir){
37674 var proxy = new Roo.Element(document.createElement("div"));
37675 proxy.unselectable();
37676 var cls = 'roo-splitbar-proxy';
37677 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37678 document.body.appendChild(proxy.dom);
37683 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37684 * Default Adapter. It assumes the splitter and resizing element are not positioned
37685 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37690 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37691 // do nothing for now
37692 init : function(s){
37696 * Called before drag operations to get the current size of the resizing element.
37697 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37699 getElementSize : function(s){
37700 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37701 return s.resizingEl.getWidth();
37703 return s.resizingEl.getHeight();
37708 * Called after drag operations to set the size of the resizing element.
37709 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37710 * @param {Number} newSize The new size to set
37711 * @param {Function} onComplete A function to be invoked when resizing is complete
37713 setElementSize : function(s, newSize, onComplete){
37714 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37716 s.resizingEl.setWidth(newSize);
37718 onComplete(s, newSize);
37721 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37726 s.resizingEl.setHeight(newSize);
37728 onComplete(s, newSize);
37731 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37738 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37739 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37740 * Adapter that moves the splitter element to align with the resized sizing element.
37741 * Used with an absolute positioned SplitBar.
37742 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37743 * document.body, make sure you assign an id to the body element.
37745 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37746 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37747 this.container = Roo.get(container);
37750 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37751 init : function(s){
37752 this.basic.init(s);
37755 getElementSize : function(s){
37756 return this.basic.getElementSize(s);
37759 setElementSize : function(s, newSize, onComplete){
37760 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37763 moveSplitter : function(s){
37764 var yes = Roo.bootstrap.SplitBar;
37765 switch(s.placement){
37767 s.el.setX(s.resizingEl.getRight());
37770 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37773 s.el.setY(s.resizingEl.getBottom());
37776 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37783 * Orientation constant - Create a vertical SplitBar
37787 Roo.bootstrap.SplitBar.VERTICAL = 1;
37790 * Orientation constant - Create a horizontal SplitBar
37794 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37797 * Placement constant - The resizing element is to the left of the splitter element
37801 Roo.bootstrap.SplitBar.LEFT = 1;
37804 * Placement constant - The resizing element is to the right of the splitter element
37808 Roo.bootstrap.SplitBar.RIGHT = 2;
37811 * Placement constant - The resizing element is positioned above the splitter element
37815 Roo.bootstrap.SplitBar.TOP = 3;
37818 * Placement constant - The resizing element is positioned under splitter element
37822 Roo.bootstrap.SplitBar.BOTTOM = 4;
37825 * Ext JS Library 1.1.1
37826 * Copyright(c) 2006-2007, Ext JS, LLC.
37828 * Originally Released Under LGPL - original licence link has changed is not relivant.
37831 * <script type="text/javascript">
37835 * @class Roo.bootstrap.layout.Manager
37836 * @extends Roo.bootstrap.Component
37838 * Base class for layout managers.
37840 Roo.bootstrap.layout.Manager = function(config)
37842 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37848 /** false to disable window resize monitoring @type Boolean */
37849 this.monitorWindowResize = true;
37854 * Fires when a layout is performed.
37855 * @param {Roo.LayoutManager} this
37859 * @event regionresized
37860 * Fires when the user resizes a region.
37861 * @param {Roo.LayoutRegion} region The resized region
37862 * @param {Number} newSize The new size (width for east/west, height for north/south)
37864 "regionresized" : true,
37866 * @event regioncollapsed
37867 * Fires when a region is collapsed.
37868 * @param {Roo.LayoutRegion} region The collapsed region
37870 "regioncollapsed" : true,
37872 * @event regionexpanded
37873 * Fires when a region is expanded.
37874 * @param {Roo.LayoutRegion} region The expanded region
37876 "regionexpanded" : true
37878 this.updating = false;
37881 this.el = Roo.get(config.el);
37887 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37892 monitorWindowResize : true,
37898 onRender : function(ct, position)
37901 this.el = Roo.get(ct);
37904 //this.fireEvent('render',this);
37908 initEvents: function()
37912 // ie scrollbar fix
37913 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37914 document.body.scroll = "no";
37915 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37916 this.el.position('relative');
37918 this.id = this.el.id;
37919 this.el.addClass("roo-layout-container");
37920 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37921 if(this.el.dom != document.body ) {
37922 this.el.on('resize', this.layout,this);
37923 this.el.on('show', this.layout,this);
37929 * Returns true if this layout is currently being updated
37930 * @return {Boolean}
37932 isUpdating : function(){
37933 return this.updating;
37937 * Suspend the LayoutManager from doing auto-layouts while
37938 * making multiple add or remove calls
37940 beginUpdate : function(){
37941 this.updating = true;
37945 * Restore auto-layouts and optionally disable the manager from performing a layout
37946 * @param {Boolean} noLayout true to disable a layout update
37948 endUpdate : function(noLayout){
37949 this.updating = false;
37955 layout: function(){
37959 onRegionResized : function(region, newSize){
37960 this.fireEvent("regionresized", region, newSize);
37964 onRegionCollapsed : function(region){
37965 this.fireEvent("regioncollapsed", region);
37968 onRegionExpanded : function(region){
37969 this.fireEvent("regionexpanded", region);
37973 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37974 * performs box-model adjustments.
37975 * @return {Object} The size as an object {width: (the width), height: (the height)}
37977 getViewSize : function()
37980 if(this.el.dom != document.body){
37981 size = this.el.getSize();
37983 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37985 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37986 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37991 * Returns the Element this layout is bound to.
37992 * @return {Roo.Element}
37994 getEl : function(){
37999 * Returns the specified region.
38000 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38001 * @return {Roo.LayoutRegion}
38003 getRegion : function(target){
38004 return this.regions[target.toLowerCase()];
38007 onWindowResize : function(){
38008 if(this.monitorWindowResize){
38015 * Ext JS Library 1.1.1
38016 * Copyright(c) 2006-2007, Ext JS, LLC.
38018 * Originally Released Under LGPL - original licence link has changed is not relivant.
38021 * <script type="text/javascript">
38024 * @class Roo.bootstrap.layout.Border
38025 * @extends Roo.bootstrap.layout.Manager
38027 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38028 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38029 * please see: examples/bootstrap/nested.html<br><br>
38031 <b>The container the layout is rendered into can be either the body element or any other element.
38032 If it is not the body element, the container needs to either be an absolute positioned element,
38033 or you will need to add "position:relative" to the css of the container. You will also need to specify
38034 the container size if it is not the body element.</b>
38037 * Create a new Border
38038 * @param {Object} config Configuration options
38040 Roo.bootstrap.layout.Border = function(config){
38041 config = config || {};
38042 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38046 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38047 if(config[region]){
38048 config[region].region = region;
38049 this.addRegion(config[region]);
38055 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38057 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38060 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38063 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38066 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38069 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38072 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38078 parent : false, // this might point to a 'nest' or a ???
38081 * Creates and adds a new region if it doesn't already exist.
38082 * @param {String} target The target region key (north, south, east, west or center).
38083 * @param {Object} config The regions config object
38084 * @return {BorderLayoutRegion} The new region
38086 addRegion : function(config)
38088 if(!this.regions[config.region]){
38089 var r = this.factory(config);
38090 this.bindRegion(r);
38092 return this.regions[config.region];
38096 bindRegion : function(r){
38097 this.regions[r.config.region] = r;
38099 r.on("visibilitychange", this.layout, this);
38100 r.on("paneladded", this.layout, this);
38101 r.on("panelremoved", this.layout, this);
38102 r.on("invalidated", this.layout, this);
38103 r.on("resized", this.onRegionResized, this);
38104 r.on("collapsed", this.onRegionCollapsed, this);
38105 r.on("expanded", this.onRegionExpanded, this);
38109 * Performs a layout update.
38111 layout : function()
38113 if(this.updating) {
38117 // render all the rebions if they have not been done alreayd?
38118 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38119 if(this.regions[region] && !this.regions[region].bodyEl){
38120 this.regions[region].onRender(this.el)
38124 var size = this.getViewSize();
38125 var w = size.width;
38126 var h = size.height;
38131 //var x = 0, y = 0;
38133 var rs = this.regions;
38134 var north = rs["north"];
38135 var south = rs["south"];
38136 var west = rs["west"];
38137 var east = rs["east"];
38138 var center = rs["center"];
38139 //if(this.hideOnLayout){ // not supported anymore
38140 //c.el.setStyle("display", "none");
38142 if(north && north.isVisible()){
38143 var b = north.getBox();
38144 var m = north.getMargins();
38145 b.width = w - (m.left+m.right);
38148 centerY = b.height + b.y + m.bottom;
38149 centerH -= centerY;
38150 north.updateBox(this.safeBox(b));
38152 if(south && south.isVisible()){
38153 var b = south.getBox();
38154 var m = south.getMargins();
38155 b.width = w - (m.left+m.right);
38157 var totalHeight = (b.height + m.top + m.bottom);
38158 b.y = h - totalHeight + m.top;
38159 centerH -= totalHeight;
38160 south.updateBox(this.safeBox(b));
38162 if(west && west.isVisible()){
38163 var b = west.getBox();
38164 var m = west.getMargins();
38165 b.height = centerH - (m.top+m.bottom);
38167 b.y = centerY + m.top;
38168 var totalWidth = (b.width + m.left + m.right);
38169 centerX += totalWidth;
38170 centerW -= totalWidth;
38171 west.updateBox(this.safeBox(b));
38173 if(east && east.isVisible()){
38174 var b = east.getBox();
38175 var m = east.getMargins();
38176 b.height = centerH - (m.top+m.bottom);
38177 var totalWidth = (b.width + m.left + m.right);
38178 b.x = w - totalWidth + m.left;
38179 b.y = centerY + m.top;
38180 centerW -= totalWidth;
38181 east.updateBox(this.safeBox(b));
38184 var m = center.getMargins();
38186 x: centerX + m.left,
38187 y: centerY + m.top,
38188 width: centerW - (m.left+m.right),
38189 height: centerH - (m.top+m.bottom)
38191 //if(this.hideOnLayout){
38192 //center.el.setStyle("display", "block");
38194 center.updateBox(this.safeBox(centerBox));
38197 this.fireEvent("layout", this);
38201 safeBox : function(box){
38202 box.width = Math.max(0, box.width);
38203 box.height = Math.max(0, box.height);
38208 * Adds a ContentPanel (or subclass) to this layout.
38209 * @param {String} target The target region key (north, south, east, west or center).
38210 * @param {Roo.ContentPanel} panel The panel to add
38211 * @return {Roo.ContentPanel} The added panel
38213 add : function(target, panel){
38215 target = target.toLowerCase();
38216 return this.regions[target].add(panel);
38220 * Remove a ContentPanel (or subclass) to this layout.
38221 * @param {String} target The target region key (north, south, east, west or center).
38222 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38223 * @return {Roo.ContentPanel} The removed panel
38225 remove : function(target, panel){
38226 target = target.toLowerCase();
38227 return this.regions[target].remove(panel);
38231 * Searches all regions for a panel with the specified id
38232 * @param {String} panelId
38233 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38235 findPanel : function(panelId){
38236 var rs = this.regions;
38237 for(var target in rs){
38238 if(typeof rs[target] != "function"){
38239 var p = rs[target].getPanel(panelId);
38249 * Searches all regions for a panel with the specified id and activates (shows) it.
38250 * @param {String/ContentPanel} panelId The panels id or the panel itself
38251 * @return {Roo.ContentPanel} The shown panel or null
38253 showPanel : function(panelId) {
38254 var rs = this.regions;
38255 for(var target in rs){
38256 var r = rs[target];
38257 if(typeof r != "function"){
38258 if(r.hasPanel(panelId)){
38259 return r.showPanel(panelId);
38267 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38268 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38271 restoreState : function(provider){
38273 provider = Roo.state.Manager;
38275 var sm = new Roo.LayoutStateManager();
38276 sm.init(this, provider);
38282 * Adds a xtype elements to the layout.
38286 xtype : 'ContentPanel',
38293 xtype : 'NestedLayoutPanel',
38299 items : [ ... list of content panels or nested layout panels.. ]
38303 * @param {Object} cfg Xtype definition of item to add.
38305 addxtype : function(cfg)
38307 // basically accepts a pannel...
38308 // can accept a layout region..!?!?
38309 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38312 // theory? children can only be panels??
38314 //if (!cfg.xtype.match(/Panel$/)) {
38319 if (typeof(cfg.region) == 'undefined') {
38320 Roo.log("Failed to add Panel, region was not set");
38324 var region = cfg.region;
38330 xitems = cfg.items;
38335 if ( region == 'center') {
38336 Roo.log("Center: " + cfg.title);
38342 case 'Content': // ContentPanel (el, cfg)
38343 case 'Scroll': // ContentPanel (el, cfg)
38345 cfg.autoCreate = cfg.autoCreate || true;
38346 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38348 // var el = this.el.createChild();
38349 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38352 this.add(region, ret);
38356 case 'TreePanel': // our new panel!
38357 cfg.el = this.el.createChild();
38358 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38359 this.add(region, ret);
38364 // create a new Layout (which is a Border Layout...
38366 var clayout = cfg.layout;
38367 clayout.el = this.el.createChild();
38368 clayout.items = clayout.items || [];
38372 // replace this exitems with the clayout ones..
38373 xitems = clayout.items;
38375 // force background off if it's in center...
38376 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38377 cfg.background = false;
38379 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38382 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38383 //console.log('adding nested layout panel ' + cfg.toSource());
38384 this.add(region, ret);
38385 nb = {}; /// find first...
38390 // needs grid and region
38392 //var el = this.getRegion(region).el.createChild();
38394 *var el = this.el.createChild();
38395 // create the grid first...
38396 cfg.grid.container = el;
38397 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38400 if (region == 'center' && this.active ) {
38401 cfg.background = false;
38404 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38406 this.add(region, ret);
38408 if (cfg.background) {
38409 // render grid on panel activation (if panel background)
38410 ret.on('activate', function(gp) {
38411 if (!gp.grid.rendered) {
38412 // gp.grid.render(el);
38416 // cfg.grid.render(el);
38422 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38423 // it was the old xcomponent building that caused this before.
38424 // espeically if border is the top element in the tree.
38434 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38436 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38437 this.add(region, ret);
38441 throw "Can not add '" + cfg.xtype + "' to Border";
38447 this.beginUpdate();
38451 Roo.each(xitems, function(i) {
38452 region = nb && i.region ? i.region : false;
38454 var add = ret.addxtype(i);
38457 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38458 if (!i.background) {
38459 abn[region] = nb[region] ;
38466 // make the last non-background panel active..
38467 //if (nb) { Roo.log(abn); }
38470 for(var r in abn) {
38471 region = this.getRegion(r);
38473 // tried using nb[r], but it does not work..
38475 region.showPanel(abn[r]);
38486 factory : function(cfg)
38489 var validRegions = Roo.bootstrap.layout.Border.regions;
38491 var target = cfg.region;
38494 var r = Roo.bootstrap.layout;
38498 return new r.North(cfg);
38500 return new r.South(cfg);
38502 return new r.East(cfg);
38504 return new r.West(cfg);
38506 return new r.Center(cfg);
38508 throw 'Layout region "'+target+'" not supported.';
38515 * Ext JS Library 1.1.1
38516 * Copyright(c) 2006-2007, Ext JS, LLC.
38518 * Originally Released Under LGPL - original licence link has changed is not relivant.
38521 * <script type="text/javascript">
38525 * @class Roo.bootstrap.layout.Basic
38526 * @extends Roo.util.Observable
38527 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38528 * and does not have a titlebar, tabs or any other features. All it does is size and position
38529 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38530 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38531 * @cfg {string} region the region that it inhabits..
38532 * @cfg {bool} skipConfig skip config?
38536 Roo.bootstrap.layout.Basic = function(config){
38538 this.mgr = config.mgr;
38540 this.position = config.region;
38542 var skipConfig = config.skipConfig;
38546 * @scope Roo.BasicLayoutRegion
38550 * @event beforeremove
38551 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38552 * @param {Roo.LayoutRegion} this
38553 * @param {Roo.ContentPanel} panel The panel
38554 * @param {Object} e The cancel event object
38556 "beforeremove" : true,
38558 * @event invalidated
38559 * Fires when the layout for this region is changed.
38560 * @param {Roo.LayoutRegion} this
38562 "invalidated" : true,
38564 * @event visibilitychange
38565 * Fires when this region is shown or hidden
38566 * @param {Roo.LayoutRegion} this
38567 * @param {Boolean} visibility true or false
38569 "visibilitychange" : true,
38571 * @event paneladded
38572 * Fires when a panel is added.
38573 * @param {Roo.LayoutRegion} this
38574 * @param {Roo.ContentPanel} panel The panel
38576 "paneladded" : true,
38578 * @event panelremoved
38579 * Fires when a panel is removed.
38580 * @param {Roo.LayoutRegion} this
38581 * @param {Roo.ContentPanel} panel The panel
38583 "panelremoved" : true,
38585 * @event beforecollapse
38586 * Fires when this region before collapse.
38587 * @param {Roo.LayoutRegion} this
38589 "beforecollapse" : true,
38592 * Fires when this region is collapsed.
38593 * @param {Roo.LayoutRegion} this
38595 "collapsed" : true,
38598 * Fires when this region is expanded.
38599 * @param {Roo.LayoutRegion} this
38604 * Fires when this region is slid into view.
38605 * @param {Roo.LayoutRegion} this
38607 "slideshow" : true,
38610 * Fires when this region slides out of view.
38611 * @param {Roo.LayoutRegion} this
38613 "slidehide" : true,
38615 * @event panelactivated
38616 * Fires when a panel is activated.
38617 * @param {Roo.LayoutRegion} this
38618 * @param {Roo.ContentPanel} panel The activated panel
38620 "panelactivated" : true,
38623 * Fires when the user resizes this region.
38624 * @param {Roo.LayoutRegion} this
38625 * @param {Number} newSize The new size (width for east/west, height for north/south)
38629 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38630 this.panels = new Roo.util.MixedCollection();
38631 this.panels.getKey = this.getPanelId.createDelegate(this);
38633 this.activePanel = null;
38634 // ensure listeners are added...
38636 if (config.listeners || config.events) {
38637 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38638 listeners : config.listeners || {},
38639 events : config.events || {}
38643 if(skipConfig !== true){
38644 this.applyConfig(config);
38648 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38650 getPanelId : function(p){
38654 applyConfig : function(config){
38655 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38656 this.config = config;
38661 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38662 * the width, for horizontal (north, south) the height.
38663 * @param {Number} newSize The new width or height
38665 resizeTo : function(newSize){
38666 var el = this.el ? this.el :
38667 (this.activePanel ? this.activePanel.getEl() : null);
38669 switch(this.position){
38672 el.setWidth(newSize);
38673 this.fireEvent("resized", this, newSize);
38677 el.setHeight(newSize);
38678 this.fireEvent("resized", this, newSize);
38684 getBox : function(){
38685 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38688 getMargins : function(){
38689 return this.margins;
38692 updateBox : function(box){
38694 var el = this.activePanel.getEl();
38695 el.dom.style.left = box.x + "px";
38696 el.dom.style.top = box.y + "px";
38697 this.activePanel.setSize(box.width, box.height);
38701 * Returns the container element for this region.
38702 * @return {Roo.Element}
38704 getEl : function(){
38705 return this.activePanel;
38709 * Returns true if this region is currently visible.
38710 * @return {Boolean}
38712 isVisible : function(){
38713 return this.activePanel ? true : false;
38716 setActivePanel : function(panel){
38717 panel = this.getPanel(panel);
38718 if(this.activePanel && this.activePanel != panel){
38719 this.activePanel.setActiveState(false);
38720 this.activePanel.getEl().setLeftTop(-10000,-10000);
38722 this.activePanel = panel;
38723 panel.setActiveState(true);
38725 panel.setSize(this.box.width, this.box.height);
38727 this.fireEvent("panelactivated", this, panel);
38728 this.fireEvent("invalidated");
38732 * Show the specified panel.
38733 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38734 * @return {Roo.ContentPanel} The shown panel or null
38736 showPanel : function(panel){
38737 panel = this.getPanel(panel);
38739 this.setActivePanel(panel);
38745 * Get the active panel for this region.
38746 * @return {Roo.ContentPanel} The active panel or null
38748 getActivePanel : function(){
38749 return this.activePanel;
38753 * Add the passed ContentPanel(s)
38754 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38755 * @return {Roo.ContentPanel} The panel added (if only one was added)
38757 add : function(panel){
38758 if(arguments.length > 1){
38759 for(var i = 0, len = arguments.length; i < len; i++) {
38760 this.add(arguments[i]);
38764 if(this.hasPanel(panel)){
38765 this.showPanel(panel);
38768 var el = panel.getEl();
38769 if(el.dom.parentNode != this.mgr.el.dom){
38770 this.mgr.el.dom.appendChild(el.dom);
38772 if(panel.setRegion){
38773 panel.setRegion(this);
38775 this.panels.add(panel);
38776 el.setStyle("position", "absolute");
38777 if(!panel.background){
38778 this.setActivePanel(panel);
38779 if(this.config.initialSize && this.panels.getCount()==1){
38780 this.resizeTo(this.config.initialSize);
38783 this.fireEvent("paneladded", this, panel);
38788 * Returns true if the panel is in this region.
38789 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38790 * @return {Boolean}
38792 hasPanel : function(panel){
38793 if(typeof panel == "object"){ // must be panel obj
38794 panel = panel.getId();
38796 return this.getPanel(panel) ? true : false;
38800 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38801 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38802 * @param {Boolean} preservePanel Overrides the config preservePanel option
38803 * @return {Roo.ContentPanel} The panel that was removed
38805 remove : function(panel, preservePanel){
38806 panel = this.getPanel(panel);
38811 this.fireEvent("beforeremove", this, panel, e);
38812 if(e.cancel === true){
38815 var panelId = panel.getId();
38816 this.panels.removeKey(panelId);
38821 * Returns the panel specified or null if it's not in this region.
38822 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38823 * @return {Roo.ContentPanel}
38825 getPanel : function(id){
38826 if(typeof id == "object"){ // must be panel obj
38829 return this.panels.get(id);
38833 * Returns this regions position (north/south/east/west/center).
38836 getPosition: function(){
38837 return this.position;
38841 * Ext JS Library 1.1.1
38842 * Copyright(c) 2006-2007, Ext JS, LLC.
38844 * Originally Released Under LGPL - original licence link has changed is not relivant.
38847 * <script type="text/javascript">
38851 * @class Roo.bootstrap.layout.Region
38852 * @extends Roo.bootstrap.layout.Basic
38853 * This class represents a region in a layout manager.
38855 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38856 * @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})
38857 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38858 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38859 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38860 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38861 * @cfg {String} title The title for the region (overrides panel titles)
38862 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38863 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38864 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38865 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38866 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38867 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38868 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38869 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38870 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38871 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38873 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38874 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38875 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38876 * @cfg {Number} width For East/West panels
38877 * @cfg {Number} height For North/South panels
38878 * @cfg {Boolean} split To show the splitter
38879 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38881 * @cfg {string} cls Extra CSS classes to add to region
38883 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38884 * @cfg {string} region the region that it inhabits..
38887 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38888 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38890 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38891 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38892 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38894 Roo.bootstrap.layout.Region = function(config)
38896 this.applyConfig(config);
38898 var mgr = config.mgr;
38899 var pos = config.region;
38900 config.skipConfig = true;
38901 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38904 this.onRender(mgr.el);
38907 this.visible = true;
38908 this.collapsed = false;
38909 this.unrendered_panels = [];
38912 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38914 position: '', // set by wrapper (eg. north/south etc..)
38915 unrendered_panels : null, // unrendered panels.
38917 tabPosition : false,
38919 mgr: false, // points to 'Border'
38922 createBody : function(){
38923 /** This region's body element
38924 * @type Roo.Element */
38925 this.bodyEl = this.el.createChild({
38927 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38931 onRender: function(ctr, pos)
38933 var dh = Roo.DomHelper;
38934 /** This region's container element
38935 * @type Roo.Element */
38936 this.el = dh.append(ctr.dom, {
38938 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38940 /** This region's title element
38941 * @type Roo.Element */
38943 this.titleEl = dh.append(this.el.dom, {
38945 unselectable: "on",
38946 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38948 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38949 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38953 this.titleEl.enableDisplayMode();
38954 /** This region's title text element
38955 * @type HTMLElement */
38956 this.titleTextEl = this.titleEl.dom.firstChild;
38957 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38959 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38960 this.closeBtn.enableDisplayMode();
38961 this.closeBtn.on("click", this.closeClicked, this);
38962 this.closeBtn.hide();
38964 this.createBody(this.config);
38965 if(this.config.hideWhenEmpty){
38967 this.on("paneladded", this.validateVisibility, this);
38968 this.on("panelremoved", this.validateVisibility, this);
38970 if(this.autoScroll){
38971 this.bodyEl.setStyle("overflow", "auto");
38973 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38975 //if(c.titlebar !== false){
38976 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38977 this.titleEl.hide();
38979 this.titleEl.show();
38980 if(this.config.title){
38981 this.titleTextEl.innerHTML = this.config.title;
38985 if(this.config.collapsed){
38986 this.collapse(true);
38988 if(this.config.hidden){
38992 if (this.unrendered_panels && this.unrendered_panels.length) {
38993 for (var i =0;i< this.unrendered_panels.length; i++) {
38994 this.add(this.unrendered_panels[i]);
38996 this.unrendered_panels = null;
39002 applyConfig : function(c)
39005 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39006 var dh = Roo.DomHelper;
39007 if(c.titlebar !== false){
39008 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39009 this.collapseBtn.on("click", this.collapse, this);
39010 this.collapseBtn.enableDisplayMode();
39012 if(c.showPin === true || this.showPin){
39013 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39014 this.stickBtn.enableDisplayMode();
39015 this.stickBtn.on("click", this.expand, this);
39016 this.stickBtn.hide();
39021 /** This region's collapsed element
39022 * @type Roo.Element */
39025 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39026 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39029 if(c.floatable !== false){
39030 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39031 this.collapsedEl.on("click", this.collapseClick, this);
39034 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39035 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39036 id: "message", unselectable: "on", style:{"float":"left"}});
39037 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39039 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39040 this.expandBtn.on("click", this.expand, this);
39044 if(this.collapseBtn){
39045 this.collapseBtn.setVisible(c.collapsible == true);
39048 this.cmargins = c.cmargins || this.cmargins ||
39049 (this.position == "west" || this.position == "east" ?
39050 {top: 0, left: 2, right:2, bottom: 0} :
39051 {top: 2, left: 0, right:0, bottom: 2});
39053 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39056 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39058 this.autoScroll = c.autoScroll || false;
39063 this.duration = c.duration || .30;
39064 this.slideDuration = c.slideDuration || .45;
39069 * Returns true if this region is currently visible.
39070 * @return {Boolean}
39072 isVisible : function(){
39073 return this.visible;
39077 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39078 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39080 //setCollapsedTitle : function(title){
39081 // title = title || " ";
39082 // if(this.collapsedTitleTextEl){
39083 // this.collapsedTitleTextEl.innerHTML = title;
39087 getBox : function(){
39089 // if(!this.collapsed){
39090 b = this.el.getBox(false, true);
39092 // b = this.collapsedEl.getBox(false, true);
39097 getMargins : function(){
39098 return this.margins;
39099 //return this.collapsed ? this.cmargins : this.margins;
39102 highlight : function(){
39103 this.el.addClass("x-layout-panel-dragover");
39106 unhighlight : function(){
39107 this.el.removeClass("x-layout-panel-dragover");
39110 updateBox : function(box)
39112 if (!this.bodyEl) {
39113 return; // not rendered yet..
39117 if(!this.collapsed){
39118 this.el.dom.style.left = box.x + "px";
39119 this.el.dom.style.top = box.y + "px";
39120 this.updateBody(box.width, box.height);
39122 this.collapsedEl.dom.style.left = box.x + "px";
39123 this.collapsedEl.dom.style.top = box.y + "px";
39124 this.collapsedEl.setSize(box.width, box.height);
39127 this.tabs.autoSizeTabs();
39131 updateBody : function(w, h)
39134 this.el.setWidth(w);
39135 w -= this.el.getBorderWidth("rl");
39136 if(this.config.adjustments){
39137 w += this.config.adjustments[0];
39140 if(h !== null && h > 0){
39141 this.el.setHeight(h);
39142 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39143 h -= this.el.getBorderWidth("tb");
39144 if(this.config.adjustments){
39145 h += this.config.adjustments[1];
39147 this.bodyEl.setHeight(h);
39149 h = this.tabs.syncHeight(h);
39152 if(this.panelSize){
39153 w = w !== null ? w : this.panelSize.width;
39154 h = h !== null ? h : this.panelSize.height;
39156 if(this.activePanel){
39157 var el = this.activePanel.getEl();
39158 w = w !== null ? w : el.getWidth();
39159 h = h !== null ? h : el.getHeight();
39160 this.panelSize = {width: w, height: h};
39161 this.activePanel.setSize(w, h);
39163 if(Roo.isIE && this.tabs){
39164 this.tabs.el.repaint();
39169 * Returns the container element for this region.
39170 * @return {Roo.Element}
39172 getEl : function(){
39177 * Hides this region.
39180 //if(!this.collapsed){
39181 this.el.dom.style.left = "-2000px";
39184 // this.collapsedEl.dom.style.left = "-2000px";
39185 // this.collapsedEl.hide();
39187 this.visible = false;
39188 this.fireEvent("visibilitychange", this, false);
39192 * Shows this region if it was previously hidden.
39195 //if(!this.collapsed){
39198 // this.collapsedEl.show();
39200 this.visible = true;
39201 this.fireEvent("visibilitychange", this, true);
39204 closeClicked : function(){
39205 if(this.activePanel){
39206 this.remove(this.activePanel);
39210 collapseClick : function(e){
39212 e.stopPropagation();
39215 e.stopPropagation();
39221 * Collapses this region.
39222 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39225 collapse : function(skipAnim, skipCheck = false){
39226 if(this.collapsed) {
39230 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39232 this.collapsed = true;
39234 this.split.el.hide();
39236 if(this.config.animate && skipAnim !== true){
39237 this.fireEvent("invalidated", this);
39238 this.animateCollapse();
39240 this.el.setLocation(-20000,-20000);
39242 this.collapsedEl.show();
39243 this.fireEvent("collapsed", this);
39244 this.fireEvent("invalidated", this);
39250 animateCollapse : function(){
39255 * Expands this region if it was previously collapsed.
39256 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39257 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39260 expand : function(e, skipAnim){
39262 e.stopPropagation();
39264 if(!this.collapsed || this.el.hasActiveFx()) {
39268 this.afterSlideIn();
39271 this.collapsed = false;
39272 if(this.config.animate && skipAnim !== true){
39273 this.animateExpand();
39277 this.split.el.show();
39279 this.collapsedEl.setLocation(-2000,-2000);
39280 this.collapsedEl.hide();
39281 this.fireEvent("invalidated", this);
39282 this.fireEvent("expanded", this);
39286 animateExpand : function(){
39290 initTabs : function()
39292 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39294 var ts = new Roo.bootstrap.panel.Tabs({
39295 el: this.bodyEl.dom,
39297 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39298 disableTooltips: this.config.disableTabTips,
39299 toolbar : this.config.toolbar
39302 if(this.config.hideTabs){
39303 ts.stripWrap.setDisplayed(false);
39306 ts.resizeTabs = this.config.resizeTabs === true;
39307 ts.minTabWidth = this.config.minTabWidth || 40;
39308 ts.maxTabWidth = this.config.maxTabWidth || 250;
39309 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39310 ts.monitorResize = false;
39311 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39312 ts.bodyEl.addClass('roo-layout-tabs-body');
39313 this.panels.each(this.initPanelAsTab, this);
39316 initPanelAsTab : function(panel){
39317 var ti = this.tabs.addTab(
39321 this.config.closeOnTab && panel.isClosable(),
39324 if(panel.tabTip !== undefined){
39325 ti.setTooltip(panel.tabTip);
39327 ti.on("activate", function(){
39328 this.setActivePanel(panel);
39331 if(this.config.closeOnTab){
39332 ti.on("beforeclose", function(t, e){
39334 this.remove(panel);
39338 panel.tabItem = ti;
39343 updatePanelTitle : function(panel, title)
39345 if(this.activePanel == panel){
39346 this.updateTitle(title);
39349 var ti = this.tabs.getTab(panel.getEl().id);
39351 if(panel.tabTip !== undefined){
39352 ti.setTooltip(panel.tabTip);
39357 updateTitle : function(title){
39358 if(this.titleTextEl && !this.config.title){
39359 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39363 setActivePanel : function(panel)
39365 panel = this.getPanel(panel);
39366 if(this.activePanel && this.activePanel != panel){
39367 if(this.activePanel.setActiveState(false) === false){
39371 this.activePanel = panel;
39372 panel.setActiveState(true);
39373 if(this.panelSize){
39374 panel.setSize(this.panelSize.width, this.panelSize.height);
39377 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39379 this.updateTitle(panel.getTitle());
39381 this.fireEvent("invalidated", this);
39383 this.fireEvent("panelactivated", this, panel);
39387 * Shows the specified panel.
39388 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39389 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39391 showPanel : function(panel)
39393 panel = this.getPanel(panel);
39396 var tab = this.tabs.getTab(panel.getEl().id);
39397 if(tab.isHidden()){
39398 this.tabs.unhideTab(tab.id);
39402 this.setActivePanel(panel);
39409 * Get the active panel for this region.
39410 * @return {Roo.ContentPanel} The active panel or null
39412 getActivePanel : function(){
39413 return this.activePanel;
39416 validateVisibility : function(){
39417 if(this.panels.getCount() < 1){
39418 this.updateTitle(" ");
39419 this.closeBtn.hide();
39422 if(!this.isVisible()){
39429 * Adds the passed ContentPanel(s) to this region.
39430 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39431 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39433 add : function(panel)
39435 if(arguments.length > 1){
39436 for(var i = 0, len = arguments.length; i < len; i++) {
39437 this.add(arguments[i]);
39442 // if we have not been rendered yet, then we can not really do much of this..
39443 if (!this.bodyEl) {
39444 this.unrendered_panels.push(panel);
39451 if(this.hasPanel(panel)){
39452 this.showPanel(panel);
39455 panel.setRegion(this);
39456 this.panels.add(panel);
39457 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39458 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39459 // and hide them... ???
39460 this.bodyEl.dom.appendChild(panel.getEl().dom);
39461 if(panel.background !== true){
39462 this.setActivePanel(panel);
39464 this.fireEvent("paneladded", this, panel);
39471 this.initPanelAsTab(panel);
39475 if(panel.background !== true){
39476 this.tabs.activate(panel.getEl().id);
39478 this.fireEvent("paneladded", this, panel);
39483 * Hides the tab for the specified panel.
39484 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39486 hidePanel : function(panel){
39487 if(this.tabs && (panel = this.getPanel(panel))){
39488 this.tabs.hideTab(panel.getEl().id);
39493 * Unhides the tab for a previously hidden panel.
39494 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39496 unhidePanel : function(panel){
39497 if(this.tabs && (panel = this.getPanel(panel))){
39498 this.tabs.unhideTab(panel.getEl().id);
39502 clearPanels : function(){
39503 while(this.panels.getCount() > 0){
39504 this.remove(this.panels.first());
39509 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39510 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39511 * @param {Boolean} preservePanel Overrides the config preservePanel option
39512 * @return {Roo.ContentPanel} The panel that was removed
39514 remove : function(panel, preservePanel)
39516 panel = this.getPanel(panel);
39521 this.fireEvent("beforeremove", this, panel, e);
39522 if(e.cancel === true){
39525 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39526 var panelId = panel.getId();
39527 this.panels.removeKey(panelId);
39529 document.body.appendChild(panel.getEl().dom);
39532 this.tabs.removeTab(panel.getEl().id);
39533 }else if (!preservePanel){
39534 this.bodyEl.dom.removeChild(panel.getEl().dom);
39536 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39537 var p = this.panels.first();
39538 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39539 tempEl.appendChild(p.getEl().dom);
39540 this.bodyEl.update("");
39541 this.bodyEl.dom.appendChild(p.getEl().dom);
39543 this.updateTitle(p.getTitle());
39545 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39546 this.setActivePanel(p);
39548 panel.setRegion(null);
39549 if(this.activePanel == panel){
39550 this.activePanel = null;
39552 if(this.config.autoDestroy !== false && preservePanel !== true){
39553 try{panel.destroy();}catch(e){}
39555 this.fireEvent("panelremoved", this, panel);
39560 * Returns the TabPanel component used by this region
39561 * @return {Roo.TabPanel}
39563 getTabs : function(){
39567 createTool : function(parentEl, className){
39568 var btn = Roo.DomHelper.append(parentEl, {
39570 cls: "x-layout-tools-button",
39573 cls: "roo-layout-tools-button-inner " + className,
39577 btn.addClassOnOver("roo-layout-tools-button-over");
39582 * Ext JS Library 1.1.1
39583 * Copyright(c) 2006-2007, Ext JS, LLC.
39585 * Originally Released Under LGPL - original licence link has changed is not relivant.
39588 * <script type="text/javascript">
39594 * @class Roo.SplitLayoutRegion
39595 * @extends Roo.LayoutRegion
39596 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39598 Roo.bootstrap.layout.Split = function(config){
39599 this.cursor = config.cursor;
39600 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39603 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39605 splitTip : "Drag to resize.",
39606 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39607 useSplitTips : false,
39609 applyConfig : function(config){
39610 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39613 onRender : function(ctr,pos) {
39615 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39616 if(!this.config.split){
39621 var splitEl = Roo.DomHelper.append(ctr.dom, {
39623 id: this.el.id + "-split",
39624 cls: "roo-layout-split roo-layout-split-"+this.position,
39627 /** The SplitBar for this region
39628 * @type Roo.SplitBar */
39629 // does not exist yet...
39630 Roo.log([this.position, this.orientation]);
39632 this.split = new Roo.bootstrap.SplitBar({
39633 dragElement : splitEl,
39634 resizingElement: this.el,
39635 orientation : this.orientation
39638 this.split.on("moved", this.onSplitMove, this);
39639 this.split.useShim = this.config.useShim === true;
39640 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39641 if(this.useSplitTips){
39642 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39644 //if(config.collapsible){
39645 // this.split.el.on("dblclick", this.collapse, this);
39648 if(typeof this.config.minSize != "undefined"){
39649 this.split.minSize = this.config.minSize;
39651 if(typeof this.config.maxSize != "undefined"){
39652 this.split.maxSize = this.config.maxSize;
39654 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39655 this.hideSplitter();
39660 getHMaxSize : function(){
39661 var cmax = this.config.maxSize || 10000;
39662 var center = this.mgr.getRegion("center");
39663 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39666 getVMaxSize : function(){
39667 var cmax = this.config.maxSize || 10000;
39668 var center = this.mgr.getRegion("center");
39669 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39672 onSplitMove : function(split, newSize){
39673 this.fireEvent("resized", this, newSize);
39677 * Returns the {@link Roo.SplitBar} for this region.
39678 * @return {Roo.SplitBar}
39680 getSplitBar : function(){
39685 this.hideSplitter();
39686 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39689 hideSplitter : function(){
39691 this.split.el.setLocation(-2000,-2000);
39692 this.split.el.hide();
39698 this.split.el.show();
39700 Roo.bootstrap.layout.Split.superclass.show.call(this);
39703 beforeSlide: function(){
39704 if(Roo.isGecko){// firefox overflow auto bug workaround
39705 this.bodyEl.clip();
39707 this.tabs.bodyEl.clip();
39709 if(this.activePanel){
39710 this.activePanel.getEl().clip();
39712 if(this.activePanel.beforeSlide){
39713 this.activePanel.beforeSlide();
39719 afterSlide : function(){
39720 if(Roo.isGecko){// firefox overflow auto bug workaround
39721 this.bodyEl.unclip();
39723 this.tabs.bodyEl.unclip();
39725 if(this.activePanel){
39726 this.activePanel.getEl().unclip();
39727 if(this.activePanel.afterSlide){
39728 this.activePanel.afterSlide();
39734 initAutoHide : function(){
39735 if(this.autoHide !== false){
39736 if(!this.autoHideHd){
39737 var st = new Roo.util.DelayedTask(this.slideIn, this);
39738 this.autoHideHd = {
39739 "mouseout": function(e){
39740 if(!e.within(this.el, true)){
39744 "mouseover" : function(e){
39750 this.el.on(this.autoHideHd);
39754 clearAutoHide : function(){
39755 if(this.autoHide !== false){
39756 this.el.un("mouseout", this.autoHideHd.mouseout);
39757 this.el.un("mouseover", this.autoHideHd.mouseover);
39761 clearMonitor : function(){
39762 Roo.get(document).un("click", this.slideInIf, this);
39765 // these names are backwards but not changed for compat
39766 slideOut : function(){
39767 if(this.isSlid || this.el.hasActiveFx()){
39770 this.isSlid = true;
39771 if(this.collapseBtn){
39772 this.collapseBtn.hide();
39774 this.closeBtnState = this.closeBtn.getStyle('display');
39775 this.closeBtn.hide();
39777 this.stickBtn.show();
39780 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39781 this.beforeSlide();
39782 this.el.setStyle("z-index", 10001);
39783 this.el.slideIn(this.getSlideAnchor(), {
39784 callback: function(){
39786 this.initAutoHide();
39787 Roo.get(document).on("click", this.slideInIf, this);
39788 this.fireEvent("slideshow", this);
39795 afterSlideIn : function(){
39796 this.clearAutoHide();
39797 this.isSlid = false;
39798 this.clearMonitor();
39799 this.el.setStyle("z-index", "");
39800 if(this.collapseBtn){
39801 this.collapseBtn.show();
39803 this.closeBtn.setStyle('display', this.closeBtnState);
39805 this.stickBtn.hide();
39807 this.fireEvent("slidehide", this);
39810 slideIn : function(cb){
39811 if(!this.isSlid || this.el.hasActiveFx()){
39815 this.isSlid = false;
39816 this.beforeSlide();
39817 this.el.slideOut(this.getSlideAnchor(), {
39818 callback: function(){
39819 this.el.setLeftTop(-10000, -10000);
39821 this.afterSlideIn();
39829 slideInIf : function(e){
39830 if(!e.within(this.el)){
39835 animateCollapse : function(){
39836 this.beforeSlide();
39837 this.el.setStyle("z-index", 20000);
39838 var anchor = this.getSlideAnchor();
39839 this.el.slideOut(anchor, {
39840 callback : function(){
39841 this.el.setStyle("z-index", "");
39842 this.collapsedEl.slideIn(anchor, {duration:.3});
39844 this.el.setLocation(-10000,-10000);
39846 this.fireEvent("collapsed", this);
39853 animateExpand : function(){
39854 this.beforeSlide();
39855 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39856 this.el.setStyle("z-index", 20000);
39857 this.collapsedEl.hide({
39860 this.el.slideIn(this.getSlideAnchor(), {
39861 callback : function(){
39862 this.el.setStyle("z-index", "");
39865 this.split.el.show();
39867 this.fireEvent("invalidated", this);
39868 this.fireEvent("expanded", this);
39896 getAnchor : function(){
39897 return this.anchors[this.position];
39900 getCollapseAnchor : function(){
39901 return this.canchors[this.position];
39904 getSlideAnchor : function(){
39905 return this.sanchors[this.position];
39908 getAlignAdj : function(){
39909 var cm = this.cmargins;
39910 switch(this.position){
39926 getExpandAdj : function(){
39927 var c = this.collapsedEl, cm = this.cmargins;
39928 switch(this.position){
39930 return [-(cm.right+c.getWidth()+cm.left), 0];
39933 return [cm.right+c.getWidth()+cm.left, 0];
39936 return [0, -(cm.top+cm.bottom+c.getHeight())];
39939 return [0, cm.top+cm.bottom+c.getHeight()];
39945 * Ext JS Library 1.1.1
39946 * Copyright(c) 2006-2007, Ext JS, LLC.
39948 * Originally Released Under LGPL - original licence link has changed is not relivant.
39951 * <script type="text/javascript">
39954 * These classes are private internal classes
39956 Roo.bootstrap.layout.Center = function(config){
39957 config.region = "center";
39958 Roo.bootstrap.layout.Region.call(this, config);
39959 this.visible = true;
39960 this.minWidth = config.minWidth || 20;
39961 this.minHeight = config.minHeight || 20;
39964 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39966 // center panel can't be hidden
39970 // center panel can't be hidden
39973 getMinWidth: function(){
39974 return this.minWidth;
39977 getMinHeight: function(){
39978 return this.minHeight;
39992 Roo.bootstrap.layout.North = function(config)
39994 config.region = 'north';
39995 config.cursor = 'n-resize';
39997 Roo.bootstrap.layout.Split.call(this, config);
40001 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40002 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40003 this.split.el.addClass("roo-layout-split-v");
40005 //var size = config.initialSize || config.height;
40006 //if(this.el && typeof size != "undefined"){
40007 // this.el.setHeight(size);
40010 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40012 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40015 onRender : function(ctr, pos)
40017 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40018 var size = this.config.initialSize || this.config.height;
40019 if(this.el && typeof size != "undefined"){
40020 this.el.setHeight(size);
40025 getBox : function(){
40026 if(this.collapsed){
40027 return this.collapsedEl.getBox();
40029 var box = this.el.getBox();
40031 box.height += this.split.el.getHeight();
40036 updateBox : function(box){
40037 if(this.split && !this.collapsed){
40038 box.height -= this.split.el.getHeight();
40039 this.split.el.setLeft(box.x);
40040 this.split.el.setTop(box.y+box.height);
40041 this.split.el.setWidth(box.width);
40043 if(this.collapsed){
40044 this.updateBody(box.width, null);
40046 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40054 Roo.bootstrap.layout.South = function(config){
40055 config.region = 'south';
40056 config.cursor = 's-resize';
40057 Roo.bootstrap.layout.Split.call(this, config);
40059 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40060 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40061 this.split.el.addClass("roo-layout-split-v");
40066 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40067 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40069 onRender : function(ctr, pos)
40071 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40072 var size = this.config.initialSize || this.config.height;
40073 if(this.el && typeof size != "undefined"){
40074 this.el.setHeight(size);
40079 getBox : function(){
40080 if(this.collapsed){
40081 return this.collapsedEl.getBox();
40083 var box = this.el.getBox();
40085 var sh = this.split.el.getHeight();
40092 updateBox : function(box){
40093 if(this.split && !this.collapsed){
40094 var sh = this.split.el.getHeight();
40097 this.split.el.setLeft(box.x);
40098 this.split.el.setTop(box.y-sh);
40099 this.split.el.setWidth(box.width);
40101 if(this.collapsed){
40102 this.updateBody(box.width, null);
40104 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40108 Roo.bootstrap.layout.East = function(config){
40109 config.region = "east";
40110 config.cursor = "e-resize";
40111 Roo.bootstrap.layout.Split.call(this, config);
40113 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40114 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40115 this.split.el.addClass("roo-layout-split-h");
40119 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40120 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40122 onRender : function(ctr, pos)
40124 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40125 var size = this.config.initialSize || this.config.width;
40126 if(this.el && typeof size != "undefined"){
40127 this.el.setWidth(size);
40132 getBox : function(){
40133 if(this.collapsed){
40134 return this.collapsedEl.getBox();
40136 var box = this.el.getBox();
40138 var sw = this.split.el.getWidth();
40145 updateBox : function(box){
40146 if(this.split && !this.collapsed){
40147 var sw = this.split.el.getWidth();
40149 this.split.el.setLeft(box.x);
40150 this.split.el.setTop(box.y);
40151 this.split.el.setHeight(box.height);
40154 if(this.collapsed){
40155 this.updateBody(null, box.height);
40157 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40161 Roo.bootstrap.layout.West = function(config){
40162 config.region = "west";
40163 config.cursor = "w-resize";
40165 Roo.bootstrap.layout.Split.call(this, config);
40167 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40168 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40169 this.split.el.addClass("roo-layout-split-h");
40173 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40174 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40176 onRender: function(ctr, pos)
40178 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40179 var size = this.config.initialSize || this.config.width;
40180 if(typeof size != "undefined"){
40181 this.el.setWidth(size);
40185 getBox : function(){
40186 if(this.collapsed){
40187 return this.collapsedEl.getBox();
40189 var box = this.el.getBox();
40190 if (box.width == 0) {
40191 box.width = this.config.width; // kludge?
40194 box.width += this.split.el.getWidth();
40199 updateBox : function(box){
40200 if(this.split && !this.collapsed){
40201 var sw = this.split.el.getWidth();
40203 this.split.el.setLeft(box.x+box.width);
40204 this.split.el.setTop(box.y);
40205 this.split.el.setHeight(box.height);
40207 if(this.collapsed){
40208 this.updateBody(null, box.height);
40210 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40214 * Ext JS Library 1.1.1
40215 * Copyright(c) 2006-2007, Ext JS, LLC.
40217 * Originally Released Under LGPL - original licence link has changed is not relivant.
40220 * <script type="text/javascript">
40223 * @class Roo.bootstrap.paenl.Content
40224 * @extends Roo.util.Observable
40226 * @children Roo.bootstrap.Component
40227 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40228 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40229 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40230 * @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
40231 * @cfg {Boolean} closable True if the panel can be closed/removed
40232 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40233 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40234 * @cfg {Toolbar} toolbar A toolbar for this panel
40235 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40236 * @cfg {String} title The title for this panel
40237 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40238 * @cfg {String} url Calls {@link #setUrl} with this value
40239 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40240 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40241 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40242 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40243 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40244 * @cfg {Boolean} badges render the badges
40245 * @cfg {String} cls extra classes to use
40246 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40249 * Create a new ContentPanel.
40250 * @param {String/Object} config A string to set only the title or a config object
40253 Roo.bootstrap.panel.Content = function( config){
40255 this.tpl = config.tpl || false;
40257 var el = config.el;
40258 var content = config.content;
40260 if(config.autoCreate){ // xtype is available if this is called from factory
40263 this.el = Roo.get(el);
40264 if(!this.el && config && config.autoCreate){
40265 if(typeof config.autoCreate == "object"){
40266 if(!config.autoCreate.id){
40267 config.autoCreate.id = config.id||el;
40269 this.el = Roo.DomHelper.append(document.body,
40270 config.autoCreate, true);
40274 cls: (config.cls || '') +
40275 (config.background ? ' bg-' + config.background : '') +
40276 " roo-layout-inactive-content",
40279 if (config.iframe) {
40283 style : 'border: 0px',
40284 src : 'about:blank'
40290 elcfg.html = config.html;
40294 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40295 if (config.iframe) {
40296 this.iframeEl = this.el.select('iframe',true).first();
40301 this.closable = false;
40302 this.loaded = false;
40303 this.active = false;
40306 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40308 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40310 this.wrapEl = this.el; //this.el.wrap();
40312 if (config.toolbar.items) {
40313 ti = config.toolbar.items ;
40314 delete config.toolbar.items ;
40318 this.toolbar.render(this.wrapEl, 'before');
40319 for(var i =0;i < ti.length;i++) {
40320 // Roo.log(['add child', items[i]]);
40321 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40323 this.toolbar.items = nitems;
40324 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40325 delete config.toolbar;
40329 // xtype created footer. - not sure if will work as we normally have to render first..
40330 if (this.footer && !this.footer.el && this.footer.xtype) {
40331 if (!this.wrapEl) {
40332 this.wrapEl = this.el.wrap();
40335 this.footer.container = this.wrapEl.createChild();
40337 this.footer = Roo.factory(this.footer, Roo);
40342 if(typeof config == "string"){
40343 this.title = config;
40345 Roo.apply(this, config);
40349 this.resizeEl = Roo.get(this.resizeEl, true);
40351 this.resizeEl = this.el;
40353 // handle view.xtype
40361 * Fires when this panel is activated.
40362 * @param {Roo.ContentPanel} this
40366 * @event deactivate
40367 * Fires when this panel is activated.
40368 * @param {Roo.ContentPanel} this
40370 "deactivate" : true,
40374 * Fires when this panel is resized if fitToFrame is true.
40375 * @param {Roo.ContentPanel} this
40376 * @param {Number} width The width after any component adjustments
40377 * @param {Number} height The height after any component adjustments
40383 * Fires when this tab is created
40384 * @param {Roo.ContentPanel} this
40390 * Fires when this content is scrolled
40391 * @param {Roo.ContentPanel} this
40392 * @param {Event} scrollEvent
40403 if(this.autoScroll && !this.iframe){
40404 this.resizeEl.setStyle("overflow", "auto");
40405 this.resizeEl.on('scroll', this.onScroll, this);
40407 // fix randome scrolling
40408 //this.el.on('scroll', function() {
40409 // Roo.log('fix random scolling');
40410 // this.scrollTo('top',0);
40413 content = content || this.content;
40415 this.setContent(content);
40417 if(config && config.url){
40418 this.setUrl(this.url, this.params, this.loadOnce);
40423 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40425 if (this.view && typeof(this.view.xtype) != 'undefined') {
40426 this.view.el = this.el.appendChild(document.createElement("div"));
40427 this.view = Roo.factory(this.view);
40428 this.view.render && this.view.render(false, '');
40432 this.fireEvent('render', this);
40435 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40445 /* Resize Element - use this to work out scroll etc. */
40448 setRegion : function(region){
40449 this.region = region;
40450 this.setActiveClass(region && !this.background);
40454 setActiveClass: function(state)
40457 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40458 this.el.setStyle('position','relative');
40460 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40461 this.el.setStyle('position', 'absolute');
40466 * Returns the toolbar for this Panel if one was configured.
40467 * @return {Roo.Toolbar}
40469 getToolbar : function(){
40470 return this.toolbar;
40473 setActiveState : function(active)
40475 this.active = active;
40476 this.setActiveClass(active);
40478 if(this.fireEvent("deactivate", this) === false){
40483 this.fireEvent("activate", this);
40487 * Updates this panel's element (not for iframe)
40488 * @param {String} content The new content
40489 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40491 setContent : function(content, loadScripts){
40496 this.el.update(content, loadScripts);
40499 ignoreResize : function(w, h){
40500 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40503 this.lastSize = {width: w, height: h};
40508 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40509 * @return {Roo.UpdateManager} The UpdateManager
40511 getUpdateManager : function(){
40515 return this.el.getUpdateManager();
40518 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40519 * Does not work with IFRAME contents
40520 * @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:
40523 url: "your-url.php",
40524 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40525 callback: yourFunction,
40526 scope: yourObject, //(optional scope)
40529 text: "Loading...",
40535 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40536 * 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.
40537 * @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}
40538 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40539 * @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.
40540 * @return {Roo.ContentPanel} this
40548 var um = this.el.getUpdateManager();
40549 um.update.apply(um, arguments);
40555 * 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.
40556 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40557 * @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)
40558 * @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)
40559 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40561 setUrl : function(url, params, loadOnce){
40563 this.iframeEl.dom.src = url;
40567 if(this.refreshDelegate){
40568 this.removeListener("activate", this.refreshDelegate);
40570 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40571 this.on("activate", this.refreshDelegate);
40572 return this.el.getUpdateManager();
40575 _handleRefresh : function(url, params, loadOnce){
40576 if(!loadOnce || !this.loaded){
40577 var updater = this.el.getUpdateManager();
40578 updater.update(url, params, this._setLoaded.createDelegate(this));
40582 _setLoaded : function(){
40583 this.loaded = true;
40587 * Returns this panel's id
40590 getId : function(){
40595 * Returns this panel's element - used by regiosn to add.
40596 * @return {Roo.Element}
40598 getEl : function(){
40599 return this.wrapEl || this.el;
40604 adjustForComponents : function(width, height)
40606 //Roo.log('adjustForComponents ');
40607 if(this.resizeEl != this.el){
40608 width -= this.el.getFrameWidth('lr');
40609 height -= this.el.getFrameWidth('tb');
40612 var te = this.toolbar.getEl();
40613 te.setWidth(width);
40614 height -= te.getHeight();
40617 var te = this.footer.getEl();
40618 te.setWidth(width);
40619 height -= te.getHeight();
40623 if(this.adjustments){
40624 width += this.adjustments[0];
40625 height += this.adjustments[1];
40627 return {"width": width, "height": height};
40630 setSize : function(width, height){
40631 if(this.fitToFrame && !this.ignoreResize(width, height)){
40632 if(this.fitContainer && this.resizeEl != this.el){
40633 this.el.setSize(width, height);
40635 var size = this.adjustForComponents(width, height);
40637 this.iframeEl.setSize(width,height);
40640 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40641 this.fireEvent('resize', this, size.width, size.height);
40648 * Returns this panel's title
40651 getTitle : function(){
40653 if (typeof(this.title) != 'object') {
40658 for (var k in this.title) {
40659 if (!this.title.hasOwnProperty(k)) {
40663 if (k.indexOf('-') >= 0) {
40664 var s = k.split('-');
40665 for (var i = 0; i<s.length; i++) {
40666 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40669 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40676 * Set this panel's title
40677 * @param {String} title
40679 setTitle : function(title){
40680 this.title = title;
40682 this.region.updatePanelTitle(this, title);
40687 * Returns true is this panel was configured to be closable
40688 * @return {Boolean}
40690 isClosable : function(){
40691 return this.closable;
40694 beforeSlide : function(){
40696 this.resizeEl.clip();
40699 afterSlide : function(){
40701 this.resizeEl.unclip();
40705 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40706 * Will fail silently if the {@link #setUrl} method has not been called.
40707 * This does not activate the panel, just updates its content.
40709 refresh : function(){
40710 if(this.refreshDelegate){
40711 this.loaded = false;
40712 this.refreshDelegate();
40717 * Destroys this panel
40719 destroy : function(){
40720 this.el.removeAllListeners();
40721 var tempEl = document.createElement("span");
40722 tempEl.appendChild(this.el.dom);
40723 tempEl.innerHTML = "";
40729 * form - if the content panel contains a form - this is a reference to it.
40730 * @type {Roo.form.Form}
40734 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40735 * This contains a reference to it.
40741 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40751 * @param {Object} cfg Xtype definition of item to add.
40755 getChildContainer: function () {
40756 return this.getEl();
40760 onScroll : function(e)
40762 this.fireEvent('scroll', this, e);
40767 var ret = new Roo.factory(cfg);
40772 if (cfg.xtype.match(/^Form$/)) {
40775 //if (this.footer) {
40776 // el = this.footer.container.insertSibling(false, 'before');
40778 el = this.el.createChild();
40781 this.form = new Roo.form.Form(cfg);
40784 if ( this.form.allItems.length) {
40785 this.form.render(el.dom);
40789 // should only have one of theses..
40790 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40791 // views.. should not be just added - used named prop 'view''
40793 cfg.el = this.el.appendChild(document.createElement("div"));
40796 var ret = new Roo.factory(cfg);
40798 ret.render && ret.render(false, ''); // render blank..
40808 * @class Roo.bootstrap.panel.Grid
40809 * @extends Roo.bootstrap.panel.Content
40811 * Create a new GridPanel.
40812 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40813 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40814 * @param {Object} config A the config object
40820 Roo.bootstrap.panel.Grid = function(config)
40824 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40825 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40827 config.el = this.wrapper;
40828 //this.el = this.wrapper;
40830 if (config.container) {
40831 // ctor'ed from a Border/panel.grid
40834 this.wrapper.setStyle("overflow", "hidden");
40835 this.wrapper.addClass('roo-grid-container');
40840 if(config.toolbar){
40841 var tool_el = this.wrapper.createChild();
40842 this.toolbar = Roo.factory(config.toolbar);
40844 if (config.toolbar.items) {
40845 ti = config.toolbar.items ;
40846 delete config.toolbar.items ;
40850 this.toolbar.render(tool_el);
40851 for(var i =0;i < ti.length;i++) {
40852 // Roo.log(['add child', items[i]]);
40853 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40855 this.toolbar.items = nitems;
40857 delete config.toolbar;
40860 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40861 config.grid.scrollBody = true;;
40862 config.grid.monitorWindowResize = false; // turn off autosizing
40863 config.grid.autoHeight = false;
40864 config.grid.autoWidth = false;
40866 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40868 if (config.background) {
40869 // render grid on panel activation (if panel background)
40870 this.on('activate', function(gp) {
40871 if (!gp.grid.rendered) {
40872 gp.grid.render(this.wrapper);
40873 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40878 this.grid.render(this.wrapper);
40879 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40882 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40883 // ??? needed ??? config.el = this.wrapper;
40888 // xtype created footer. - not sure if will work as we normally have to render first..
40889 if (this.footer && !this.footer.el && this.footer.xtype) {
40891 var ctr = this.grid.getView().getFooterPanel(true);
40892 this.footer.dataSource = this.grid.dataSource;
40893 this.footer = Roo.factory(this.footer, Roo);
40894 this.footer.render(ctr);
40904 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40905 getId : function(){
40906 return this.grid.id;
40910 * Returns the grid for this panel
40911 * @return {Roo.bootstrap.Table}
40913 getGrid : function(){
40917 setSize : function(width, height){
40918 if(!this.ignoreResize(width, height)){
40919 var grid = this.grid;
40920 var size = this.adjustForComponents(width, height);
40921 // tfoot is not a footer?
40924 var gridel = grid.getGridEl();
40925 gridel.setSize(size.width, size.height);
40927 var tbd = grid.getGridEl().select('tbody', true).first();
40928 var thd = grid.getGridEl().select('thead',true).first();
40929 var tbf= grid.getGridEl().select('tfoot', true).first();
40932 size.height -= tbf.getHeight();
40935 size.height -= thd.getHeight();
40938 tbd.setSize(size.width, size.height );
40939 // this is for the account management tab -seems to work there.
40940 var thd = grid.getGridEl().select('thead',true).first();
40942 // tbd.setSize(size.width, size.height - thd.getHeight());
40951 beforeSlide : function(){
40952 this.grid.getView().scroller.clip();
40955 afterSlide : function(){
40956 this.grid.getView().scroller.unclip();
40959 destroy : function(){
40960 this.grid.destroy();
40962 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40967 * @class Roo.bootstrap.panel.Nest
40968 * @extends Roo.bootstrap.panel.Content
40970 * Create a new Panel, that can contain a layout.Border.
40973 * @param {String/Object} config A string to set only the title or a config object
40975 Roo.bootstrap.panel.Nest = function(config)
40977 // construct with only one argument..
40978 /* FIXME - implement nicer consturctors
40979 if (layout.layout) {
40981 layout = config.layout;
40982 delete config.layout;
40984 if (layout.xtype && !layout.getEl) {
40985 // then layout needs constructing..
40986 layout = Roo.factory(layout, Roo);
40990 config.el = config.layout.getEl();
40992 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40994 config.layout.monitorWindowResize = false; // turn off autosizing
40995 this.layout = config.layout;
40996 this.layout.getEl().addClass("roo-layout-nested-layout");
40997 this.layout.parent = this;
41004 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41006 * @cfg {Roo.BorderLayout} layout The layout for this panel
41010 setSize : function(width, height){
41011 if(!this.ignoreResize(width, height)){
41012 var size = this.adjustForComponents(width, height);
41013 var el = this.layout.getEl();
41014 if (size.height < 1) {
41015 el.setWidth(size.width);
41017 el.setSize(size.width, size.height);
41019 var touch = el.dom.offsetWidth;
41020 this.layout.layout();
41021 // ie requires a double layout on the first pass
41022 if(Roo.isIE && !this.initialized){
41023 this.initialized = true;
41024 this.layout.layout();
41029 // activate all subpanels if not currently active..
41031 setActiveState : function(active){
41032 this.active = active;
41033 this.setActiveClass(active);
41036 this.fireEvent("deactivate", this);
41040 this.fireEvent("activate", this);
41041 // not sure if this should happen before or after..
41042 if (!this.layout) {
41043 return; // should not happen..
41046 for (var r in this.layout.regions) {
41047 reg = this.layout.getRegion(r);
41048 if (reg.getActivePanel()) {
41049 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41050 reg.setActivePanel(reg.getActivePanel());
41053 if (!reg.panels.length) {
41056 reg.showPanel(reg.getPanel(0));
41065 * Returns the nested BorderLayout for this panel
41066 * @return {Roo.BorderLayout}
41068 getLayout : function(){
41069 return this.layout;
41073 * Adds a xtype elements to the layout of the nested panel
41077 xtype : 'ContentPanel',
41084 xtype : 'NestedLayoutPanel',
41090 items : [ ... list of content panels or nested layout panels.. ]
41094 * @param {Object} cfg Xtype definition of item to add.
41096 addxtype : function(cfg) {
41097 return this.layout.addxtype(cfg);
41102 * Ext JS Library 1.1.1
41103 * Copyright(c) 2006-2007, Ext JS, LLC.
41105 * Originally Released Under LGPL - original licence link has changed is not relivant.
41108 * <script type="text/javascript">
41111 * @class Roo.TabPanel
41112 * @extends Roo.util.Observable
41113 * A lightweight tab container.
41117 // basic tabs 1, built from existing content
41118 var tabs = new Roo.TabPanel("tabs1");
41119 tabs.addTab("script", "View Script");
41120 tabs.addTab("markup", "View Markup");
41121 tabs.activate("script");
41123 // more advanced tabs, built from javascript
41124 var jtabs = new Roo.TabPanel("jtabs");
41125 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41127 // set up the UpdateManager
41128 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41129 var updater = tab2.getUpdateManager();
41130 updater.setDefaultUrl("ajax1.htm");
41131 tab2.on('activate', updater.refresh, updater, true);
41133 // Use setUrl for Ajax loading
41134 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41135 tab3.setUrl("ajax2.htm", null, true);
41138 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41141 jtabs.activate("jtabs-1");
41144 * Create a new TabPanel.
41145 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41146 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41148 Roo.bootstrap.panel.Tabs = function(config){
41150 * The container element for this TabPanel.
41151 * @type Roo.Element
41153 this.el = Roo.get(config.el);
41156 if(typeof config == "boolean"){
41157 this.tabPosition = config ? "bottom" : "top";
41159 Roo.apply(this, config);
41163 if(this.tabPosition == "bottom"){
41164 // if tabs are at the bottom = create the body first.
41165 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41166 this.el.addClass("roo-tabs-bottom");
41168 // next create the tabs holders
41170 if (this.tabPosition == "west"){
41172 var reg = this.region; // fake it..
41174 if (!reg.mgr.parent) {
41177 reg = reg.mgr.parent.region;
41179 Roo.log("got nest?");
41181 if (reg.mgr.getRegion('west')) {
41182 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41183 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41184 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41185 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41186 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41194 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41195 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41196 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41197 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41202 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41205 // finally - if tabs are at the top, then create the body last..
41206 if(this.tabPosition != "bottom"){
41207 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41208 * @type Roo.Element
41210 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41211 this.el.addClass("roo-tabs-top");
41215 this.bodyEl.setStyle("position", "relative");
41217 this.active = null;
41218 this.activateDelegate = this.activate.createDelegate(this);
41223 * Fires when the active tab changes
41224 * @param {Roo.TabPanel} this
41225 * @param {Roo.TabPanelItem} activePanel The new active tab
41229 * @event beforetabchange
41230 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41231 * @param {Roo.TabPanel} this
41232 * @param {Object} e Set cancel to true on this object to cancel the tab change
41233 * @param {Roo.TabPanelItem} tab The tab being changed to
41235 "beforetabchange" : true
41238 Roo.EventManager.onWindowResize(this.onResize, this);
41239 this.cpad = this.el.getPadding("lr");
41240 this.hiddenCount = 0;
41243 // toolbar on the tabbar support...
41244 if (this.toolbar) {
41245 alert("no toolbar support yet");
41246 this.toolbar = false;
41248 var tcfg = this.toolbar;
41249 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41250 this.toolbar = new Roo.Toolbar(tcfg);
41251 if (Roo.isSafari) {
41252 var tbl = tcfg.container.child('table', true);
41253 tbl.setAttribute('width', '100%');
41261 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41264 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41266 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41268 tabPosition : "top",
41270 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41272 currentTabWidth : 0,
41274 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41278 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41282 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41284 preferredTabWidth : 175,
41286 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41288 resizeTabs : false,
41290 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41292 monitorResize : true,
41294 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41296 toolbar : false, // set by caller..
41298 region : false, /// set by caller
41300 disableTooltips : true, // not used yet...
41303 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41304 * @param {String} id The id of the div to use <b>or create</b>
41305 * @param {String} text The text for the tab
41306 * @param {String} content (optional) Content to put in the TabPanelItem body
41307 * @param {Boolean} closable (optional) True to create a close icon on the tab
41308 * @return {Roo.TabPanelItem} The created TabPanelItem
41310 addTab : function(id, text, content, closable, tpl)
41312 var item = new Roo.bootstrap.panel.TabItem({
41316 closable : closable,
41319 this.addTabItem(item);
41321 item.setContent(content);
41327 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41328 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41329 * @return {Roo.TabPanelItem}
41331 getTab : function(id){
41332 return this.items[id];
41336 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41337 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41339 hideTab : function(id){
41340 var t = this.items[id];
41343 this.hiddenCount++;
41344 this.autoSizeTabs();
41349 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41350 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41352 unhideTab : function(id){
41353 var t = this.items[id];
41355 t.setHidden(false);
41356 this.hiddenCount--;
41357 this.autoSizeTabs();
41362 * Adds an existing {@link Roo.TabPanelItem}.
41363 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41365 addTabItem : function(item)
41367 this.items[item.id] = item;
41368 this.items.push(item);
41369 this.autoSizeTabs();
41370 // if(this.resizeTabs){
41371 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41372 // this.autoSizeTabs();
41374 // item.autoSize();
41379 * Removes a {@link Roo.TabPanelItem}.
41380 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41382 removeTab : function(id){
41383 var items = this.items;
41384 var tab = items[id];
41385 if(!tab) { return; }
41386 var index = items.indexOf(tab);
41387 if(this.active == tab && items.length > 1){
41388 var newTab = this.getNextAvailable(index);
41393 this.stripEl.dom.removeChild(tab.pnode.dom);
41394 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41395 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41397 items.splice(index, 1);
41398 delete this.items[tab.id];
41399 tab.fireEvent("close", tab);
41400 tab.purgeListeners();
41401 this.autoSizeTabs();
41404 getNextAvailable : function(start){
41405 var items = this.items;
41407 // look for a next tab that will slide over to
41408 // replace the one being removed
41409 while(index < items.length){
41410 var item = items[++index];
41411 if(item && !item.isHidden()){
41415 // if one isn't found select the previous tab (on the left)
41418 var item = items[--index];
41419 if(item && !item.isHidden()){
41427 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41428 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41430 disableTab : function(id){
41431 var tab = this.items[id];
41432 if(tab && this.active != tab){
41438 * Enables a {@link Roo.TabPanelItem} that is disabled.
41439 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41441 enableTab : function(id){
41442 var tab = this.items[id];
41447 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41448 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41449 * @return {Roo.TabPanelItem} The TabPanelItem.
41451 activate : function(id)
41453 //Roo.log('activite:' + id);
41455 var tab = this.items[id];
41459 if(tab == this.active || tab.disabled){
41463 this.fireEvent("beforetabchange", this, e, tab);
41464 if(e.cancel !== true && !tab.disabled){
41466 this.active.hide();
41468 this.active = this.items[id];
41469 this.active.show();
41470 this.fireEvent("tabchange", this, this.active);
41476 * Gets the active {@link Roo.TabPanelItem}.
41477 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41479 getActiveTab : function(){
41480 return this.active;
41484 * Updates the tab body element to fit the height of the container element
41485 * for overflow scrolling
41486 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41488 syncHeight : function(targetHeight){
41489 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41490 var bm = this.bodyEl.getMargins();
41491 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41492 this.bodyEl.setHeight(newHeight);
41496 onResize : function(){
41497 if(this.monitorResize){
41498 this.autoSizeTabs();
41503 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41505 beginUpdate : function(){
41506 this.updating = true;
41510 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41512 endUpdate : function(){
41513 this.updating = false;
41514 this.autoSizeTabs();
41518 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41520 autoSizeTabs : function()
41522 var count = this.items.length;
41523 var vcount = count - this.hiddenCount;
41526 this.stripEl.hide();
41528 this.stripEl.show();
41531 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41536 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41537 var availWidth = Math.floor(w / vcount);
41538 var b = this.stripBody;
41539 if(b.getWidth() > w){
41540 var tabs = this.items;
41541 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41542 if(availWidth < this.minTabWidth){
41543 /*if(!this.sleft){ // incomplete scrolling code
41544 this.createScrollButtons();
41547 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41550 if(this.currentTabWidth < this.preferredTabWidth){
41551 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41557 * Returns the number of tabs in this TabPanel.
41560 getCount : function(){
41561 return this.items.length;
41565 * Resizes all the tabs to the passed width
41566 * @param {Number} The new width
41568 setTabWidth : function(width){
41569 this.currentTabWidth = width;
41570 for(var i = 0, len = this.items.length; i < len; i++) {
41571 if(!this.items[i].isHidden()) {
41572 this.items[i].setWidth(width);
41578 * Destroys this TabPanel
41579 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41581 destroy : function(removeEl){
41582 Roo.EventManager.removeResizeListener(this.onResize, this);
41583 for(var i = 0, len = this.items.length; i < len; i++){
41584 this.items[i].purgeListeners();
41586 if(removeEl === true){
41587 this.el.update("");
41592 createStrip : function(container)
41594 var strip = document.createElement("nav");
41595 strip.className = Roo.bootstrap.version == 4 ?
41596 "navbar-light bg-light" :
41597 "navbar navbar-default"; //"x-tabs-wrap";
41598 container.appendChild(strip);
41602 createStripList : function(strip)
41604 // div wrapper for retard IE
41605 // returns the "tr" element.
41606 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41607 //'<div class="x-tabs-strip-wrap">'+
41608 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41609 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41610 return strip.firstChild; //.firstChild.firstChild.firstChild;
41612 createBody : function(container)
41614 var body = document.createElement("div");
41615 Roo.id(body, "tab-body");
41616 //Roo.fly(body).addClass("x-tabs-body");
41617 Roo.fly(body).addClass("tab-content");
41618 container.appendChild(body);
41621 createItemBody :function(bodyEl, id){
41622 var body = Roo.getDom(id);
41624 body = document.createElement("div");
41627 //Roo.fly(body).addClass("x-tabs-item-body");
41628 Roo.fly(body).addClass("tab-pane");
41629 bodyEl.insertBefore(body, bodyEl.firstChild);
41633 createStripElements : function(stripEl, text, closable, tpl)
41635 var td = document.createElement("li"); // was td..
41636 td.className = 'nav-item';
41638 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41641 stripEl.appendChild(td);
41643 td.className = "x-tabs-closable";
41644 if(!this.closeTpl){
41645 this.closeTpl = new Roo.Template(
41646 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41647 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41648 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41651 var el = this.closeTpl.overwrite(td, {"text": text});
41652 var close = el.getElementsByTagName("div")[0];
41653 var inner = el.getElementsByTagName("em")[0];
41654 return {"el": el, "close": close, "inner": inner};
41657 // not sure what this is..
41658 // if(!this.tabTpl){
41659 //this.tabTpl = new Roo.Template(
41660 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41661 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41663 // this.tabTpl = new Roo.Template(
41664 // '<a href="#">' +
41665 // '<span unselectable="on"' +
41666 // (this.disableTooltips ? '' : ' title="{text}"') +
41667 // ' >{text}</span></a>'
41673 var template = tpl || this.tabTpl || false;
41676 template = new Roo.Template(
41677 Roo.bootstrap.version == 4 ?
41679 '<a class="nav-link" href="#" unselectable="on"' +
41680 (this.disableTooltips ? '' : ' title="{text}"') +
41683 '<a class="nav-link" href="#">' +
41684 '<span unselectable="on"' +
41685 (this.disableTooltips ? '' : ' title="{text}"') +
41686 ' >{text}</span></a>'
41691 switch (typeof(template)) {
41695 template = new Roo.Template(template);
41701 var el = template.overwrite(td, {"text": text});
41703 var inner = el.getElementsByTagName("span")[0];
41705 return {"el": el, "inner": inner};
41713 * @class Roo.TabPanelItem
41714 * @extends Roo.util.Observable
41715 * Represents an individual item (tab plus body) in a TabPanel.
41716 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41717 * @param {String} id The id of this TabPanelItem
41718 * @param {String} text The text for the tab of this TabPanelItem
41719 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41721 Roo.bootstrap.panel.TabItem = function(config){
41723 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41724 * @type Roo.TabPanel
41726 this.tabPanel = config.panel;
41728 * The id for this TabPanelItem
41731 this.id = config.id;
41733 this.disabled = false;
41735 this.text = config.text;
41737 this.loaded = false;
41738 this.closable = config.closable;
41741 * The body element for this TabPanelItem.
41742 * @type Roo.Element
41744 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41745 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41746 this.bodyEl.setStyle("display", "block");
41747 this.bodyEl.setStyle("zoom", "1");
41748 //this.hideAction();
41750 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41752 this.el = Roo.get(els.el);
41753 this.inner = Roo.get(els.inner, true);
41754 this.textEl = Roo.bootstrap.version == 4 ?
41755 this.el : Roo.get(this.el.dom.firstChild, true);
41757 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41758 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41761 // this.el.on("mousedown", this.onTabMouseDown, this);
41762 this.el.on("click", this.onTabClick, this);
41764 if(config.closable){
41765 var c = Roo.get(els.close, true);
41766 c.dom.title = this.closeText;
41767 c.addClassOnOver("close-over");
41768 c.on("click", this.closeClick, this);
41774 * Fires when this tab becomes the active tab.
41775 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41776 * @param {Roo.TabPanelItem} this
41780 * @event beforeclose
41781 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41782 * @param {Roo.TabPanelItem} this
41783 * @param {Object} e Set cancel to true on this object to cancel the close.
41785 "beforeclose": true,
41788 * Fires when this tab is closed.
41789 * @param {Roo.TabPanelItem} this
41793 * @event deactivate
41794 * Fires when this tab is no longer the active tab.
41795 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41796 * @param {Roo.TabPanelItem} this
41798 "deactivate" : true
41800 this.hidden = false;
41802 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41807 purgeListeners : function(){
41808 Roo.util.Observable.prototype.purgeListeners.call(this);
41809 this.el.removeAllListeners();
41812 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41815 this.status_node.addClass("active");
41818 this.tabPanel.stripWrap.repaint();
41820 this.fireEvent("activate", this.tabPanel, this);
41824 * Returns true if this tab is the active tab.
41825 * @return {Boolean}
41827 isActive : function(){
41828 return this.tabPanel.getActiveTab() == this;
41832 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41835 this.status_node.removeClass("active");
41837 this.fireEvent("deactivate", this.tabPanel, this);
41840 hideAction : function(){
41841 this.bodyEl.hide();
41842 this.bodyEl.setStyle("position", "absolute");
41843 this.bodyEl.setLeft("-20000px");
41844 this.bodyEl.setTop("-20000px");
41847 showAction : function(){
41848 this.bodyEl.setStyle("position", "relative");
41849 this.bodyEl.setTop("");
41850 this.bodyEl.setLeft("");
41851 this.bodyEl.show();
41855 * Set the tooltip for the tab.
41856 * @param {String} tooltip The tab's tooltip
41858 setTooltip : function(text){
41859 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41860 this.textEl.dom.qtip = text;
41861 this.textEl.dom.removeAttribute('title');
41863 this.textEl.dom.title = text;
41867 onTabClick : function(e){
41868 e.preventDefault();
41869 this.tabPanel.activate(this.id);
41872 onTabMouseDown : function(e){
41873 e.preventDefault();
41874 this.tabPanel.activate(this.id);
41877 getWidth : function(){
41878 return this.inner.getWidth();
41881 setWidth : function(width){
41882 var iwidth = width - this.linode.getPadding("lr");
41883 this.inner.setWidth(iwidth);
41884 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41885 this.linode.setWidth(width);
41889 * Show or hide the tab
41890 * @param {Boolean} hidden True to hide or false to show.
41892 setHidden : function(hidden){
41893 this.hidden = hidden;
41894 this.linode.setStyle("display", hidden ? "none" : "");
41898 * Returns true if this tab is "hidden"
41899 * @return {Boolean}
41901 isHidden : function(){
41902 return this.hidden;
41906 * Returns the text for this tab
41909 getText : function(){
41913 autoSize : function(){
41914 //this.el.beginMeasure();
41915 this.textEl.setWidth(1);
41917 * #2804 [new] Tabs in Roojs
41918 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41920 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41921 //this.el.endMeasure();
41925 * Sets the text for the tab (Note: this also sets the tooltip text)
41926 * @param {String} text The tab's text and tooltip
41928 setText : function(text){
41930 this.textEl.update(text);
41931 this.setTooltip(text);
41932 //if(!this.tabPanel.resizeTabs){
41933 // this.autoSize();
41937 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41939 activate : function(){
41940 this.tabPanel.activate(this.id);
41944 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41946 disable : function(){
41947 if(this.tabPanel.active != this){
41948 this.disabled = true;
41949 this.status_node.addClass("disabled");
41954 * Enables this TabPanelItem if it was previously disabled.
41956 enable : function(){
41957 this.disabled = false;
41958 this.status_node.removeClass("disabled");
41962 * Sets the content for this TabPanelItem.
41963 * @param {String} content The content
41964 * @param {Boolean} loadScripts true to look for and load scripts
41966 setContent : function(content, loadScripts){
41967 this.bodyEl.update(content, loadScripts);
41971 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41972 * @return {Roo.UpdateManager} The UpdateManager
41974 getUpdateManager : function(){
41975 return this.bodyEl.getUpdateManager();
41979 * Set a URL to be used to load the content for this TabPanelItem.
41980 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41981 * @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)
41982 * @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)
41983 * @return {Roo.UpdateManager} The UpdateManager
41985 setUrl : function(url, params, loadOnce){
41986 if(this.refreshDelegate){
41987 this.un('activate', this.refreshDelegate);
41989 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41990 this.on("activate", this.refreshDelegate);
41991 return this.bodyEl.getUpdateManager();
41995 _handleRefresh : function(url, params, loadOnce){
41996 if(!loadOnce || !this.loaded){
41997 var updater = this.bodyEl.getUpdateManager();
41998 updater.update(url, params, this._setLoaded.createDelegate(this));
42003 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42004 * Will fail silently if the setUrl method has not been called.
42005 * This does not activate the panel, just updates its content.
42007 refresh : function(){
42008 if(this.refreshDelegate){
42009 this.loaded = false;
42010 this.refreshDelegate();
42015 _setLoaded : function(){
42016 this.loaded = true;
42020 closeClick : function(e){
42023 this.fireEvent("beforeclose", this, o);
42024 if(o.cancel !== true){
42025 this.tabPanel.removeTab(this.id);
42029 * The text displayed in the tooltip for the close icon.
42032 closeText : "Close this tab"
42035 * This script refer to:
42036 * Title: International Telephone Input
42037 * Author: Jack O'Connor
42038 * Code version: v12.1.12
42039 * Availability: https://github.com/jackocnr/intl-tel-input.git
42042 Roo.bootstrap.form.PhoneInputData = function() {
42045 "Afghanistan (افغانستان)",
42050 "Albania (Shqipëri)",
42055 "Algeria (الجزائر)",
42080 "Antigua and Barbuda",
42090 "Armenia (Հայաստան)",
42106 "Austria (Österreich)",
42111 "Azerbaijan (Azərbaycan)",
42121 "Bahrain (البحرين)",
42126 "Bangladesh (বাংলাদেশ)",
42136 "Belarus (Беларусь)",
42141 "Belgium (België)",
42171 "Bosnia and Herzegovina (Босна и Херцеговина)",
42186 "British Indian Ocean Territory",
42191 "British Virgin Islands",
42201 "Bulgaria (България)",
42211 "Burundi (Uburundi)",
42216 "Cambodia (កម្ពុជា)",
42221 "Cameroon (Cameroun)",
42230 ["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"]
42233 "Cape Verde (Kabu Verdi)",
42238 "Caribbean Netherlands",
42249 "Central African Republic (République centrafricaine)",
42269 "Christmas Island",
42275 "Cocos (Keeling) Islands",
42286 "Comoros (جزر القمر)",
42291 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42296 "Congo (Republic) (Congo-Brazzaville)",
42316 "Croatia (Hrvatska)",
42337 "Czech Republic (Česká republika)",
42342 "Denmark (Danmark)",
42357 "Dominican Republic (República Dominicana)",
42361 ["809", "829", "849"]
42379 "Equatorial Guinea (Guinea Ecuatorial)",
42399 "Falkland Islands (Islas Malvinas)",
42404 "Faroe Islands (Føroyar)",
42425 "French Guiana (Guyane française)",
42430 "French Polynesia (Polynésie française)",
42445 "Georgia (საქართველო)",
42450 "Germany (Deutschland)",
42470 "Greenland (Kalaallit Nunaat)",
42507 "Guinea-Bissau (Guiné Bissau)",
42532 "Hungary (Magyarország)",
42537 "Iceland (Ísland)",
42557 "Iraq (العراق)",
42573 "Israel (ישראל)",
42600 "Jordan (الأردن)",
42605 "Kazakhstan (Казахстан)",
42626 "Kuwait (الكويت)",
42631 "Kyrgyzstan (Кыргызстан)",
42641 "Latvia (Latvija)",
42646 "Lebanon (لبنان)",
42661 "Libya (ليبيا)",
42671 "Lithuania (Lietuva)",
42686 "Macedonia (FYROM) (Македонија)",
42691 "Madagascar (Madagasikara)",
42721 "Marshall Islands",
42731 "Mauritania (موريتانيا)",
42736 "Mauritius (Moris)",
42757 "Moldova (Republica Moldova)",
42767 "Mongolia (Монгол)",
42772 "Montenegro (Crna Gora)",
42782 "Morocco (المغرب)",
42788 "Mozambique (Moçambique)",
42793 "Myanmar (Burma) (မြန်မာ)",
42798 "Namibia (Namibië)",
42813 "Netherlands (Nederland)",
42818 "New Caledonia (Nouvelle-Calédonie)",
42853 "North Korea (조선 민주주의 인민 공화국)",
42858 "Northern Mariana Islands",
42874 "Pakistan (پاکستان)",
42884 "Palestine (فلسطين)",
42894 "Papua New Guinea",
42936 "Réunion (La Réunion)",
42942 "Romania (România)",
42958 "Saint Barthélemy",
42969 "Saint Kitts and Nevis",
42979 "Saint Martin (Saint-Martin (partie française))",
42985 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42990 "Saint Vincent and the Grenadines",
43005 "São Tomé and Príncipe (São Tomé e Príncipe)",
43010 "Saudi Arabia (المملكة العربية السعودية)",
43015 "Senegal (Sénégal)",
43045 "Slovakia (Slovensko)",
43050 "Slovenia (Slovenija)",
43060 "Somalia (Soomaaliya)",
43070 "South Korea (대한민국)",
43075 "South Sudan (جنوب السودان)",
43085 "Sri Lanka (ශ්රී ලංකාව)",
43090 "Sudan (السودان)",
43100 "Svalbard and Jan Mayen",
43111 "Sweden (Sverige)",
43116 "Switzerland (Schweiz)",
43121 "Syria (سوريا)",
43166 "Trinidad and Tobago",
43171 "Tunisia (تونس)",
43176 "Turkey (Türkiye)",
43186 "Turks and Caicos Islands",
43196 "U.S. Virgin Islands",
43206 "Ukraine (Україна)",
43211 "United Arab Emirates (الإمارات العربية المتحدة)",
43233 "Uzbekistan (Oʻzbekiston)",
43243 "Vatican City (Città del Vaticano)",
43254 "Vietnam (Việt Nam)",
43259 "Wallis and Futuna (Wallis-et-Futuna)",
43264 "Western Sahara (الصحراء الغربية)",
43270 "Yemen (اليمن)",
43294 * This script refer to:
43295 * Title: International Telephone Input
43296 * Author: Jack O'Connor
43297 * Code version: v12.1.12
43298 * Availability: https://github.com/jackocnr/intl-tel-input.git
43302 * @class Roo.bootstrap.form.PhoneInput
43303 * @extends Roo.bootstrap.form.TriggerField
43304 * An input with International dial-code selection
43306 * @cfg {String} defaultDialCode default '+852'
43307 * @cfg {Array} preferedCountries default []
43310 * Create a new PhoneInput.
43311 * @param {Object} config Configuration options
43314 Roo.bootstrap.form.PhoneInput = function(config) {
43315 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43318 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43320 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43322 listWidth: undefined,
43324 selectedClass: 'active',
43326 invalidClass : "has-warning",
43328 validClass: 'has-success',
43330 allowed: '0123456789',
43335 * @cfg {String} defaultDialCode The default dial code when initializing the input
43337 defaultDialCode: '+852',
43340 * @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
43342 preferedCountries: false,
43344 getAutoCreate : function()
43346 var data = Roo.bootstrap.form.PhoneInputData();
43347 var align = this.labelAlign || this.parentLabelAlign();
43350 this.allCountries = [];
43351 this.dialCodeMapping = [];
43353 for (var i = 0; i < data.length; i++) {
43355 this.allCountries[i] = {
43359 priority: c[3] || 0,
43360 areaCodes: c[4] || null
43362 this.dialCodeMapping[c[2]] = {
43365 priority: c[3] || 0,
43366 areaCodes: c[4] || null
43378 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43379 maxlength: this.max_length,
43380 cls : 'form-control tel-input',
43381 autocomplete: 'new-password'
43384 var hiddenInput = {
43387 cls: 'hidden-tel-input'
43391 hiddenInput.name = this.name;
43394 if (this.disabled) {
43395 input.disabled = true;
43398 var flag_container = {
43415 cls: this.hasFeedback ? 'has-feedback' : '',
43421 cls: 'dial-code-holder',
43428 cls: 'roo-select2-container input-group',
43435 if (this.fieldLabel.length) {
43438 tooltip: 'This field is required'
43444 cls: 'control-label',
43450 html: this.fieldLabel
43453 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43459 if(this.indicatorpos == 'right') {
43460 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43467 if(align == 'left') {
43475 if(this.labelWidth > 12){
43476 label.style = "width: " + this.labelWidth + 'px';
43478 if(this.labelWidth < 13 && this.labelmd == 0){
43479 this.labelmd = this.labelWidth;
43481 if(this.labellg > 0){
43482 label.cls += ' col-lg-' + this.labellg;
43483 input.cls += ' col-lg-' + (12 - this.labellg);
43485 if(this.labelmd > 0){
43486 label.cls += ' col-md-' + this.labelmd;
43487 container.cls += ' col-md-' + (12 - this.labelmd);
43489 if(this.labelsm > 0){
43490 label.cls += ' col-sm-' + this.labelsm;
43491 container.cls += ' col-sm-' + (12 - this.labelsm);
43493 if(this.labelxs > 0){
43494 label.cls += ' col-xs-' + this.labelxs;
43495 container.cls += ' col-xs-' + (12 - this.labelxs);
43505 var settings = this;
43507 ['xs','sm','md','lg'].map(function(size){
43508 if (settings[size]) {
43509 cfg.cls += ' col-' + size + '-' + settings[size];
43513 this.store = new Roo.data.Store({
43514 proxy : new Roo.data.MemoryProxy({}),
43515 reader : new Roo.data.JsonReader({
43526 'name' : 'dialCode',
43530 'name' : 'priority',
43534 'name' : 'areaCodes',
43541 if(!this.preferedCountries) {
43542 this.preferedCountries = [
43549 var p = this.preferedCountries.reverse();
43552 for (var i = 0; i < p.length; i++) {
43553 for (var j = 0; j < this.allCountries.length; j++) {
43554 if(this.allCountries[j].iso2 == p[i]) {
43555 var t = this.allCountries[j];
43556 this.allCountries.splice(j,1);
43557 this.allCountries.unshift(t);
43563 this.store.proxy.data = {
43565 data: this.allCountries
43571 initEvents : function()
43574 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43576 this.indicator = this.indicatorEl();
43577 this.flag = this.flagEl();
43578 this.dialCodeHolder = this.dialCodeHolderEl();
43580 this.trigger = this.el.select('div.flag-box',true).first();
43581 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43586 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43587 _this.list.setWidth(lw);
43590 this.list.on('mouseover', this.onViewOver, this);
43591 this.list.on('mousemove', this.onViewMove, this);
43592 this.inputEl().on("keyup", this.onKeyUp, this);
43593 this.inputEl().on("keypress", this.onKeyPress, this);
43595 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43597 this.view = new Roo.View(this.list, this.tpl, {
43598 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43601 this.view.on('click', this.onViewClick, this);
43602 this.setValue(this.defaultDialCode);
43605 onTriggerClick : function(e)
43607 Roo.log('trigger click');
43612 if(this.isExpanded()){
43614 this.hasFocus = false;
43616 this.store.load({});
43617 this.hasFocus = true;
43622 isExpanded : function()
43624 return this.list.isVisible();
43627 collapse : function()
43629 if(!this.isExpanded()){
43633 Roo.get(document).un('mousedown', this.collapseIf, this);
43634 Roo.get(document).un('mousewheel', this.collapseIf, this);
43635 this.fireEvent('collapse', this);
43639 expand : function()
43643 if(this.isExpanded() || !this.hasFocus){
43647 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43648 this.list.setWidth(lw);
43651 this.restrictHeight();
43653 Roo.get(document).on('mousedown', this.collapseIf, this);
43654 Roo.get(document).on('mousewheel', this.collapseIf, this);
43656 this.fireEvent('expand', this);
43659 restrictHeight : function()
43661 this.list.alignTo(this.inputEl(), this.listAlign);
43662 this.list.alignTo(this.inputEl(), this.listAlign);
43665 onViewOver : function(e, t)
43667 if(this.inKeyMode){
43670 var item = this.view.findItemFromChild(t);
43673 var index = this.view.indexOf(item);
43674 this.select(index, false);
43679 onViewClick : function(view, doFocus, el, e)
43681 var index = this.view.getSelectedIndexes()[0];
43683 var r = this.store.getAt(index);
43686 this.onSelect(r, index);
43688 if(doFocus !== false && !this.blockFocus){
43689 this.inputEl().focus();
43693 onViewMove : function(e, t)
43695 this.inKeyMode = false;
43698 select : function(index, scrollIntoView)
43700 this.selectedIndex = index;
43701 this.view.select(index);
43702 if(scrollIntoView !== false){
43703 var el = this.view.getNode(index);
43705 this.list.scrollChildIntoView(el, false);
43710 createList : function()
43712 this.list = Roo.get(document.body).createChild({
43714 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43715 style: 'display:none'
43718 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43721 collapseIf : function(e)
43723 var in_combo = e.within(this.el);
43724 var in_list = e.within(this.list);
43725 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43727 if (in_combo || in_list || is_list) {
43733 onSelect : function(record, index)
43735 if(this.fireEvent('beforeselect', this, record, index) !== false){
43737 this.setFlagClass(record.data.iso2);
43738 this.setDialCode(record.data.dialCode);
43739 this.hasFocus = false;
43741 this.fireEvent('select', this, record, index);
43745 flagEl : function()
43747 var flag = this.el.select('div.flag',true).first();
43754 dialCodeHolderEl : function()
43756 var d = this.el.select('input.dial-code-holder',true).first();
43763 setDialCode : function(v)
43765 this.dialCodeHolder.dom.value = '+'+v;
43768 setFlagClass : function(n)
43770 this.flag.dom.className = 'flag '+n;
43773 getValue : function()
43775 var v = this.inputEl().getValue();
43776 if(this.dialCodeHolder) {
43777 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43782 setValue : function(v)
43784 var d = this.getDialCode(v);
43786 //invalid dial code
43787 if(v.length == 0 || !d || d.length == 0) {
43789 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43790 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43796 this.setFlagClass(this.dialCodeMapping[d].iso2);
43797 this.setDialCode(d);
43798 this.inputEl().dom.value = v.replace('+'+d,'');
43799 this.hiddenEl().dom.value = this.getValue();
43804 getDialCode : function(v)
43808 if (v.length == 0) {
43809 return this.dialCodeHolder.dom.value;
43813 if (v.charAt(0) != "+") {
43816 var numericChars = "";
43817 for (var i = 1; i < v.length; i++) {
43818 var c = v.charAt(i);
43821 if (this.dialCodeMapping[numericChars]) {
43822 dialCode = v.substr(1, i);
43824 if (numericChars.length == 4) {
43834 this.setValue(this.defaultDialCode);
43838 hiddenEl : function()
43840 return this.el.select('input.hidden-tel-input',true).first();
43843 // after setting val
43844 onKeyUp : function(e){
43845 this.setValue(this.getValue());
43848 onKeyPress : function(e){
43849 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43856 * @class Roo.bootstrap.form.MoneyField
43857 * @extends Roo.bootstrap.form.ComboBox
43858 * Bootstrap MoneyField class
43861 * Create a new MoneyField.
43862 * @param {Object} config Configuration options
43865 Roo.bootstrap.form.MoneyField = function(config) {
43867 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43871 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43874 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43876 allowDecimals : true,
43878 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43880 decimalSeparator : ".",
43882 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43884 decimalPrecision : 0,
43886 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43888 allowNegative : true,
43890 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43894 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43896 minValue : Number.NEGATIVE_INFINITY,
43898 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43900 maxValue : Number.MAX_VALUE,
43902 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43904 minText : "The minimum value for this field is {0}",
43906 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43908 maxText : "The maximum value for this field is {0}",
43910 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43911 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43913 nanText : "{0} is not a valid number",
43915 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43919 * @cfg {String} defaults currency of the MoneyField
43920 * value should be in lkey
43922 defaultCurrency : false,
43924 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43926 thousandsDelimiter : false,
43928 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43937 * @cfg {Roo.data.Store} store Store to lookup currency??
43941 getAutoCreate : function()
43943 var align = this.labelAlign || this.parentLabelAlign();
43955 cls : 'form-control roo-money-amount-input',
43956 autocomplete: 'new-password'
43959 var hiddenInput = {
43963 cls: 'hidden-number-input'
43966 if(this.max_length) {
43967 input.maxlength = this.max_length;
43971 hiddenInput.name = this.name;
43974 if (this.disabled) {
43975 input.disabled = true;
43978 var clg = 12 - this.inputlg;
43979 var cmd = 12 - this.inputmd;
43980 var csm = 12 - this.inputsm;
43981 var cxs = 12 - this.inputxs;
43985 cls : 'row roo-money-field',
43989 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43993 cls: 'roo-select2-container input-group',
43997 cls : 'form-control roo-money-currency-input',
43998 autocomplete: 'new-password',
44000 name : this.currencyName
44004 cls : 'input-group-addon',
44018 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44022 cls: this.hasFeedback ? 'has-feedback' : '',
44033 if (this.fieldLabel.length) {
44036 tooltip: 'This field is required'
44042 cls: 'control-label',
44048 html: this.fieldLabel
44051 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44057 if(this.indicatorpos == 'right') {
44058 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44065 if(align == 'left') {
44073 if(this.labelWidth > 12){
44074 label.style = "width: " + this.labelWidth + 'px';
44076 if(this.labelWidth < 13 && this.labelmd == 0){
44077 this.labelmd = this.labelWidth;
44079 if(this.labellg > 0){
44080 label.cls += ' col-lg-' + this.labellg;
44081 input.cls += ' col-lg-' + (12 - this.labellg);
44083 if(this.labelmd > 0){
44084 label.cls += ' col-md-' + this.labelmd;
44085 container.cls += ' col-md-' + (12 - this.labelmd);
44087 if(this.labelsm > 0){
44088 label.cls += ' col-sm-' + this.labelsm;
44089 container.cls += ' col-sm-' + (12 - this.labelsm);
44091 if(this.labelxs > 0){
44092 label.cls += ' col-xs-' + this.labelxs;
44093 container.cls += ' col-xs-' + (12 - this.labelxs);
44104 var settings = this;
44106 ['xs','sm','md','lg'].map(function(size){
44107 if (settings[size]) {
44108 cfg.cls += ' col-' + size + '-' + settings[size];
44115 initEvents : function()
44117 this.indicator = this.indicatorEl();
44119 this.initCurrencyEvent();
44121 this.initNumberEvent();
44124 initCurrencyEvent : function()
44127 throw "can not find store for combo";
44130 this.store = Roo.factory(this.store, Roo.data);
44131 this.store.parent = this;
44135 this.triggerEl = this.el.select('.input-group-addon', true).first();
44137 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44142 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44143 _this.list.setWidth(lw);
44146 this.list.on('mouseover', this.onViewOver, this);
44147 this.list.on('mousemove', this.onViewMove, this);
44148 this.list.on('scroll', this.onViewScroll, this);
44151 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44154 this.view = new Roo.View(this.list, this.tpl, {
44155 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44158 this.view.on('click', this.onViewClick, this);
44160 this.store.on('beforeload', this.onBeforeLoad, this);
44161 this.store.on('load', this.onLoad, this);
44162 this.store.on('loadexception', this.onLoadException, this);
44164 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44165 "up" : function(e){
44166 this.inKeyMode = true;
44170 "down" : function(e){
44171 if(!this.isExpanded()){
44172 this.onTriggerClick();
44174 this.inKeyMode = true;
44179 "enter" : function(e){
44182 if(this.fireEvent("specialkey", this, e)){
44183 this.onViewClick(false);
44189 "esc" : function(e){
44193 "tab" : function(e){
44196 if(this.fireEvent("specialkey", this, e)){
44197 this.onViewClick(false);
44205 doRelay : function(foo, bar, hname){
44206 if(hname == 'down' || this.scope.isExpanded()){
44207 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44215 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44219 initNumberEvent : function(e)
44221 this.inputEl().on("keydown" , this.fireKey, this);
44222 this.inputEl().on("focus", this.onFocus, this);
44223 this.inputEl().on("blur", this.onBlur, this);
44225 this.inputEl().relayEvent('keyup', this);
44227 if(this.indicator){
44228 this.indicator.addClass('invisible');
44231 this.originalValue = this.getValue();
44233 if(this.validationEvent == 'keyup'){
44234 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44235 this.inputEl().on('keyup', this.filterValidation, this);
44237 else if(this.validationEvent !== false){
44238 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44241 if(this.selectOnFocus){
44242 this.on("focus", this.preFocus, this);
44245 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44246 this.inputEl().on("keypress", this.filterKeys, this);
44248 this.inputEl().relayEvent('keypress', this);
44251 var allowed = "0123456789";
44253 if(this.allowDecimals){
44254 allowed += this.decimalSeparator;
44257 if(this.allowNegative){
44261 if(this.thousandsDelimiter) {
44265 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44267 var keyPress = function(e){
44269 var k = e.getKey();
44271 var c = e.getCharCode();
44274 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44275 allowed.indexOf(String.fromCharCode(c)) === -1
44281 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44285 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44290 this.inputEl().on("keypress", keyPress, this);
44294 onTriggerClick : function(e)
44301 this.loadNext = false;
44303 if(this.isExpanded()){
44308 this.hasFocus = true;
44310 if(this.triggerAction == 'all') {
44311 this.doQuery(this.allQuery, true);
44315 this.doQuery(this.getRawValue());
44318 getCurrency : function()
44320 var v = this.currencyEl().getValue();
44325 restrictHeight : function()
44327 this.list.alignTo(this.currencyEl(), this.listAlign);
44328 this.list.alignTo(this.currencyEl(), this.listAlign);
44331 onViewClick : function(view, doFocus, el, e)
44333 var index = this.view.getSelectedIndexes()[0];
44335 var r = this.store.getAt(index);
44338 this.onSelect(r, index);
44342 onSelect : function(record, index){
44344 if(this.fireEvent('beforeselect', this, record, index) !== false){
44346 this.setFromCurrencyData(index > -1 ? record.data : false);
44350 this.fireEvent('select', this, record, index);
44354 setFromCurrencyData : function(o)
44358 this.lastCurrency = o;
44360 if (this.currencyField) {
44361 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44363 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44366 this.lastSelectionText = currency;
44368 //setting default currency
44369 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44370 this.setCurrency(this.defaultCurrency);
44374 this.setCurrency(currency);
44377 setFromData : function(o)
44381 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44383 this.setFromCurrencyData(c);
44388 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44390 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44393 this.setValue(value);
44397 setCurrency : function(v)
44399 this.currencyValue = v;
44402 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44407 setValue : function(v)
44409 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44415 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44417 this.inputEl().dom.value = (v == '') ? '' :
44418 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44420 if(!this.allowZero && v === '0') {
44421 this.hiddenEl().dom.value = '';
44422 this.inputEl().dom.value = '';
44429 getRawValue : function()
44431 var v = this.inputEl().getValue();
44436 getValue : function()
44438 return this.fixPrecision(this.parseValue(this.getRawValue()));
44441 parseValue : function(value)
44443 if(this.thousandsDelimiter) {
44445 r = new RegExp(",", "g");
44446 value = value.replace(r, "");
44449 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44450 return isNaN(value) ? '' : value;
44454 fixPrecision : function(value)
44456 if(this.thousandsDelimiter) {
44458 r = new RegExp(",", "g");
44459 value = value.replace(r, "");
44462 var nan = isNaN(value);
44464 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44465 return nan ? '' : value;
44467 return parseFloat(value).toFixed(this.decimalPrecision);
44470 decimalPrecisionFcn : function(v)
44472 return Math.floor(v);
44475 validateValue : function(value)
44477 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44481 var num = this.parseValue(value);
44484 this.markInvalid(String.format(this.nanText, value));
44488 if(num < this.minValue){
44489 this.markInvalid(String.format(this.minText, this.minValue));
44493 if(num > this.maxValue){
44494 this.markInvalid(String.format(this.maxText, this.maxValue));
44501 validate : function()
44503 if(this.disabled || this.allowBlank){
44508 var currency = this.getCurrency();
44510 if(this.validateValue(this.getRawValue()) && currency.length){
44515 this.markInvalid();
44519 getName: function()
44524 beforeBlur : function()
44530 var v = this.parseValue(this.getRawValue());
44537 onBlur : function()
44541 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44542 //this.el.removeClass(this.focusClass);
44545 this.hasFocus = false;
44547 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44551 var v = this.getValue();
44553 if(String(v) !== String(this.startValue)){
44554 this.fireEvent('change', this, v, this.startValue);
44557 this.fireEvent("blur", this);
44560 inputEl : function()
44562 return this.el.select('.roo-money-amount-input', true).first();
44565 currencyEl : function()
44567 return this.el.select('.roo-money-currency-input', true).first();
44570 hiddenEl : function()
44572 return this.el.select('input.hidden-number-input',true).first();
44576 * @class Roo.bootstrap.BezierSignature
44577 * @extends Roo.bootstrap.Component
44578 * Bootstrap BezierSignature class
44579 * This script refer to:
44580 * Title: Signature Pad
44582 * Availability: https://github.com/szimek/signature_pad
44585 * Create a new BezierSignature
44586 * @param {Object} config The config object
44589 Roo.bootstrap.BezierSignature = function(config){
44590 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44596 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44603 mouse_btn_down: true,
44606 * @cfg {int} canvas height
44608 canvas_height: '200px',
44611 * @cfg {float|function} Radius of a single dot.
44616 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44621 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44626 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44631 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44636 * @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.
44638 bg_color: 'rgba(0, 0, 0, 0)',
44641 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44643 dot_color: 'black',
44646 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44648 velocity_filter_weight: 0.7,
44651 * @cfg {function} Callback when stroke begin.
44656 * @cfg {function} Callback when stroke end.
44660 getAutoCreate : function()
44662 var cls = 'roo-signature column';
44665 cls += ' ' + this.cls;
44675 for(var i = 0; i < col_sizes.length; i++) {
44676 if(this[col_sizes[i]]) {
44677 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44687 cls: 'roo-signature-body',
44691 cls: 'roo-signature-body-canvas',
44692 height: this.canvas_height,
44693 width: this.canvas_width
44700 style: 'display: none'
44708 initEvents: function()
44710 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44712 var canvas = this.canvasEl();
44714 // mouse && touch event swapping...
44715 canvas.dom.style.touchAction = 'none';
44716 canvas.dom.style.msTouchAction = 'none';
44718 this.mouse_btn_down = false;
44719 canvas.on('mousedown', this._handleMouseDown, this);
44720 canvas.on('mousemove', this._handleMouseMove, this);
44721 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44723 if (window.PointerEvent) {
44724 canvas.on('pointerdown', this._handleMouseDown, this);
44725 canvas.on('pointermove', this._handleMouseMove, this);
44726 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44729 if ('ontouchstart' in window) {
44730 canvas.on('touchstart', this._handleTouchStart, this);
44731 canvas.on('touchmove', this._handleTouchMove, this);
44732 canvas.on('touchend', this._handleTouchEnd, this);
44735 Roo.EventManager.onWindowResize(this.resize, this, true);
44737 // file input event
44738 this.fileEl().on('change', this.uploadImage, this);
44745 resize: function(){
44747 var canvas = this.canvasEl().dom;
44748 var ctx = this.canvasElCtx();
44749 var img_data = false;
44751 if(canvas.width > 0) {
44752 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44754 // setting canvas width will clean img data
44757 var style = window.getComputedStyle ?
44758 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44760 var padding_left = parseInt(style.paddingLeft) || 0;
44761 var padding_right = parseInt(style.paddingRight) || 0;
44763 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44766 ctx.putImageData(img_data, 0, 0);
44770 _handleMouseDown: function(e)
44772 if (e.browserEvent.which === 1) {
44773 this.mouse_btn_down = true;
44774 this.strokeBegin(e);
44778 _handleMouseMove: function (e)
44780 if (this.mouse_btn_down) {
44781 this.strokeMoveUpdate(e);
44785 _handleMouseUp: function (e)
44787 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44788 this.mouse_btn_down = false;
44793 _handleTouchStart: function (e) {
44795 e.preventDefault();
44796 if (e.browserEvent.targetTouches.length === 1) {
44797 // var touch = e.browserEvent.changedTouches[0];
44798 // this.strokeBegin(touch);
44800 this.strokeBegin(e); // assume e catching the correct xy...
44804 _handleTouchMove: function (e) {
44805 e.preventDefault();
44806 // var touch = event.targetTouches[0];
44807 // _this._strokeMoveUpdate(touch);
44808 this.strokeMoveUpdate(e);
44811 _handleTouchEnd: function (e) {
44812 var wasCanvasTouched = e.target === this.canvasEl().dom;
44813 if (wasCanvasTouched) {
44814 e.preventDefault();
44815 // var touch = event.changedTouches[0];
44816 // _this._strokeEnd(touch);
44821 reset: function () {
44822 this._lastPoints = [];
44823 this._lastVelocity = 0;
44824 this._lastWidth = (this.min_width + this.max_width) / 2;
44825 this.canvasElCtx().fillStyle = this.dot_color;
44828 strokeMoveUpdate: function(e)
44830 this.strokeUpdate(e);
44832 if (this.throttle) {
44833 this.throttleStroke(this.strokeUpdate, this.throttle);
44836 this.strokeUpdate(e);
44840 strokeBegin: function(e)
44842 var newPointGroup = {
44843 color: this.dot_color,
44847 if (typeof this.onBegin === 'function') {
44851 this.curve_data.push(newPointGroup);
44853 this.strokeUpdate(e);
44856 strokeUpdate: function(e)
44858 var rect = this.canvasEl().dom.getBoundingClientRect();
44859 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44860 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44861 var lastPoints = lastPointGroup.points;
44862 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44863 var isLastPointTooClose = lastPoint
44864 ? point.distanceTo(lastPoint) <= this.min_distance
44866 var color = lastPointGroup.color;
44867 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44868 var curve = this.addPoint(point);
44870 this.drawDot({color: color, point: point});
44873 this.drawCurve({color: color, curve: curve});
44883 strokeEnd: function(e)
44885 this.strokeUpdate(e);
44886 if (typeof this.onEnd === 'function') {
44891 addPoint: function (point) {
44892 var _lastPoints = this._lastPoints;
44893 _lastPoints.push(point);
44894 if (_lastPoints.length > 2) {
44895 if (_lastPoints.length === 3) {
44896 _lastPoints.unshift(_lastPoints[0]);
44898 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44899 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44900 _lastPoints.shift();
44906 calculateCurveWidths: function (startPoint, endPoint) {
44907 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44908 (1 - this.velocity_filter_weight) * this._lastVelocity;
44910 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44913 start: this._lastWidth
44916 this._lastVelocity = velocity;
44917 this._lastWidth = newWidth;
44921 drawDot: function (_a) {
44922 var color = _a.color, point = _a.point;
44923 var ctx = this.canvasElCtx();
44924 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44926 this.drawCurveSegment(point.x, point.y, width);
44928 ctx.fillStyle = color;
44932 drawCurve: function (_a) {
44933 var color = _a.color, curve = _a.curve;
44934 var ctx = this.canvasElCtx();
44935 var widthDelta = curve.endWidth - curve.startWidth;
44936 var drawSteps = Math.floor(curve.length()) * 2;
44938 ctx.fillStyle = color;
44939 for (var i = 0; i < drawSteps; i += 1) {
44940 var t = i / drawSteps;
44946 var x = uuu * curve.startPoint.x;
44947 x += 3 * uu * t * curve.control1.x;
44948 x += 3 * u * tt * curve.control2.x;
44949 x += ttt * curve.endPoint.x;
44950 var y = uuu * curve.startPoint.y;
44951 y += 3 * uu * t * curve.control1.y;
44952 y += 3 * u * tt * curve.control2.y;
44953 y += ttt * curve.endPoint.y;
44954 var width = curve.startWidth + ttt * widthDelta;
44955 this.drawCurveSegment(x, y, width);
44961 drawCurveSegment: function (x, y, width) {
44962 var ctx = this.canvasElCtx();
44964 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44965 this.is_empty = false;
44970 var ctx = this.canvasElCtx();
44971 var canvas = this.canvasEl().dom;
44972 ctx.fillStyle = this.bg_color;
44973 ctx.clearRect(0, 0, canvas.width, canvas.height);
44974 ctx.fillRect(0, 0, canvas.width, canvas.height);
44975 this.curve_data = [];
44977 this.is_empty = true;
44982 return this.el.select('input',true).first();
44985 canvasEl: function()
44987 return this.el.select('canvas',true).first();
44990 canvasElCtx: function()
44992 return this.el.select('canvas',true).first().dom.getContext('2d');
44995 getImage: function(type)
44997 if(this.is_empty) {
45002 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45005 drawFromImage: function(img_src)
45007 var img = new Image();
45009 img.onload = function(){
45010 this.canvasElCtx().drawImage(img, 0, 0);
45015 this.is_empty = false;
45018 selectImage: function()
45020 this.fileEl().dom.click();
45023 uploadImage: function(e)
45025 var reader = new FileReader();
45027 reader.onload = function(e){
45028 var img = new Image();
45029 img.onload = function(){
45031 this.canvasElCtx().drawImage(img, 0, 0);
45033 img.src = e.target.result;
45036 reader.readAsDataURL(e.target.files[0]);
45039 // Bezier Point Constructor
45040 Point: (function () {
45041 function Point(x, y, time) {
45044 this.time = time || Date.now();
45046 Point.prototype.distanceTo = function (start) {
45047 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45049 Point.prototype.equals = function (other) {
45050 return this.x === other.x && this.y === other.y && this.time === other.time;
45052 Point.prototype.velocityFrom = function (start) {
45053 return this.time !== start.time
45054 ? this.distanceTo(start) / (this.time - start.time)
45061 // Bezier Constructor
45062 Bezier: (function () {
45063 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45064 this.startPoint = startPoint;
45065 this.control2 = control2;
45066 this.control1 = control1;
45067 this.endPoint = endPoint;
45068 this.startWidth = startWidth;
45069 this.endWidth = endWidth;
45071 Bezier.fromPoints = function (points, widths, scope) {
45072 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45073 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45074 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45076 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45077 var dx1 = s1.x - s2.x;
45078 var dy1 = s1.y - s2.y;
45079 var dx2 = s2.x - s3.x;
45080 var dy2 = s2.y - s3.y;
45081 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45082 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45083 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45084 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45085 var dxm = m1.x - m2.x;
45086 var dym = m1.y - m2.y;
45087 var k = l2 / (l1 + l2);
45088 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45089 var tx = s2.x - cm.x;
45090 var ty = s2.y - cm.y;
45092 c1: new scope.Point(m1.x + tx, m1.y + ty),
45093 c2: new scope.Point(m2.x + tx, m2.y + ty)
45096 Bezier.prototype.length = function () {
45101 for (var i = 0; i <= steps; i += 1) {
45103 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45104 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45106 var xdiff = cx - px;
45107 var ydiff = cy - py;
45108 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45115 Bezier.prototype.point = function (t, start, c1, c2, end) {
45116 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45117 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45118 + (3.0 * c2 * (1.0 - t) * t * t)
45119 + (end * t * t * t);
45124 throttleStroke: function(fn, wait) {
45125 if (wait === void 0) { wait = 250; }
45127 var timeout = null;
45131 var later = function () {
45132 previous = Date.now();
45134 result = fn.apply(storedContext, storedArgs);
45136 storedContext = null;
45140 return function wrapper() {
45142 for (var _i = 0; _i < arguments.length; _i++) {
45143 args[_i] = arguments[_i];
45145 var now = Date.now();
45146 var remaining = wait - (now - previous);
45147 storedContext = this;
45149 if (remaining <= 0 || remaining > wait) {
45151 clearTimeout(timeout);
45155 result = fn.apply(storedContext, storedArgs);
45157 storedContext = null;
45161 else if (!timeout) {
45162 timeout = window.setTimeout(later, remaining);
45172 // old names for form elements
45173 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45174 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45175 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45176 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45177 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45178 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45179 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45180 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45181 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45182 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45183 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45184 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45185 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45186 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45187 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45188 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45189 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45190 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45191 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45192 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45193 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45194 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45195 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45196 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45197 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45198 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45200 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45201 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45203 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45204 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45206 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45207 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45208 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45209 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator