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 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21752 * @param {string} (left|right|top|bottom) position
21754 show : function (on_el, placement)
21756 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21757 on_el = on_el || false; // default to false
21760 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21761 on_el = this.parent().el;
21762 } else if (this.over) {
21763 on_el = Roo.get(this.over);
21768 this.alignEl = Roo.get( on_el );
21771 this.render(document.body);
21777 if (this.title === false) {
21778 this.headerEl.hide();
21783 this.el.dom.style.display = 'block';
21786 if (this.alignEl) {
21787 this.updatePosition(this.placement, true);
21790 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21791 var es = this.el.getSize();
21792 var x = Roo.lib.Dom.getViewWidth()/2;
21793 var y = Roo.lib.Dom.getViewHeight()/2;
21794 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21799 //var arrow = this.el.select('.arrow',true).first();
21800 //arrow.set(align[2],
21802 this.el.addClass('in');
21806 this.hoverState = 'in';
21809 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21810 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21811 this.maskEl.dom.style.display = 'block';
21812 this.maskEl.addClass('show');
21814 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21816 this.fireEvent('show', this);
21820 * fire this manually after loading a grid in the table for example
21821 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21822 * @param {Boolean} try and move it if we cant get right position.
21824 updatePosition : function(placement, try_move)
21826 // allow for calling with no parameters
21827 placement = placement ? placement : this.placement;
21828 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21830 this.el.removeClass([
21831 'fade','top','bottom', 'left', 'right','in',
21832 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21834 this.el.addClass(placement + ' bs-popover-' + placement);
21836 if (!this.alignEl ) {
21840 switch (placement) {
21842 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21843 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21844 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21845 //normal display... or moved up/down.
21846 this.el.setXY(offset);
21847 var xy = this.alignEl.getAnchorXY('tr', false);
21849 this.arrowEl.setXY(xy);
21852 // continue through...
21853 return this.updatePosition('left', false);
21857 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21858 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21859 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21860 //normal display... or moved up/down.
21861 this.el.setXY(offset);
21862 var xy = this.alignEl.getAnchorXY('tl', false);
21863 xy[0]-=10;xy[1]+=5; // << fix me
21864 this.arrowEl.setXY(xy);
21868 return this.updatePosition('right', false);
21871 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21872 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21873 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21874 //normal display... or moved up/down.
21875 this.el.setXY(offset);
21876 var xy = this.alignEl.getAnchorXY('t', false);
21877 xy[1]-=10; // << fix me
21878 this.arrowEl.setXY(xy);
21882 return this.updatePosition('bottom', false);
21885 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21886 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21887 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21888 //normal display... or moved up/down.
21889 this.el.setXY(offset);
21890 var xy = this.alignEl.getAnchorXY('b', false);
21891 xy[1]+=2; // << fix me
21892 this.arrowEl.setXY(xy);
21896 return this.updatePosition('top', false);
21907 this.el.setXY([0,0]);
21908 this.el.removeClass('in');
21910 this.hoverState = null;
21911 this.maskEl.hide(); // always..
21912 this.fireEvent('hide', this);
21918 Roo.apply(Roo.bootstrap.Popover, {
21921 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21922 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21923 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21924 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21929 clickHander : false,
21933 onMouseDown : function(e)
21935 if (this.popups.length && !e.getTarget(".roo-popover")) {
21936 /// what is nothing is showing..
21945 register : function(popup)
21947 if (!Roo.bootstrap.Popover.clickHandler) {
21948 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21950 // hide other popups.
21951 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21952 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21953 this.hideAll(); //<< why?
21954 //this.popups.push(popup);
21956 hideAll : function()
21958 this.popups.forEach(function(p) {
21962 onShow : function() {
21963 Roo.bootstrap.Popover.popups.push(this);
21965 onHide : function() {
21966 Roo.bootstrap.Popover.popups.remove(this);
21971 * @class Roo.bootstrap.PopoverNav
21972 * @extends Roo.bootstrap.nav.Simplebar
21973 * @parent Roo.bootstrap.Popover
21974 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21976 * Bootstrap Popover header navigation class
21977 * FIXME? should this go under nav?
21981 * Create a new Popover Header Navigation
21982 * @param {Object} config The config object
21985 Roo.bootstrap.PopoverNav = function(config){
21986 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21989 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
21992 container_method : 'getPopoverHeader'
22010 * @class Roo.bootstrap.Progress
22011 * @extends Roo.bootstrap.Component
22012 * @children Roo.bootstrap.ProgressBar
22013 * Bootstrap Progress class
22014 * @cfg {Boolean} striped striped of the progress bar
22015 * @cfg {Boolean} active animated of the progress bar
22019 * Create a new Progress
22020 * @param {Object} config The config object
22023 Roo.bootstrap.Progress = function(config){
22024 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22027 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22032 getAutoCreate : function(){
22040 cfg.cls += ' progress-striped';
22044 cfg.cls += ' active';
22063 * @class Roo.bootstrap.ProgressBar
22064 * @extends Roo.bootstrap.Component
22065 * Bootstrap ProgressBar class
22066 * @cfg {Number} aria_valuenow aria-value now
22067 * @cfg {Number} aria_valuemin aria-value min
22068 * @cfg {Number} aria_valuemax aria-value max
22069 * @cfg {String} label label for the progress bar
22070 * @cfg {String} panel (success | info | warning | danger )
22071 * @cfg {String} role role of the progress bar
22072 * @cfg {String} sr_only text
22076 * Create a new ProgressBar
22077 * @param {Object} config The config object
22080 Roo.bootstrap.ProgressBar = function(config){
22081 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22084 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22088 aria_valuemax : 100,
22094 getAutoCreate : function()
22099 cls: 'progress-bar',
22100 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22112 cfg.role = this.role;
22115 if(this.aria_valuenow){
22116 cfg['aria-valuenow'] = this.aria_valuenow;
22119 if(this.aria_valuemin){
22120 cfg['aria-valuemin'] = this.aria_valuemin;
22123 if(this.aria_valuemax){
22124 cfg['aria-valuemax'] = this.aria_valuemax;
22127 if(this.label && !this.sr_only){
22128 cfg.html = this.label;
22132 cfg.cls += ' progress-bar-' + this.panel;
22138 update : function(aria_valuenow)
22140 this.aria_valuenow = aria_valuenow;
22142 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22150 * @class Roo.bootstrap.TabGroup
22151 * @extends Roo.bootstrap.Column
22152 * @children Roo.bootstrap.TabPanel
22153 * Bootstrap Column class
22154 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22155 * @cfg {Boolean} carousel true to make the group behave like a carousel
22156 * @cfg {Boolean} bullets show bullets for the panels
22157 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22158 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22159 * @cfg {Boolean} showarrow (true|false) show arrow default true
22162 * Create a new TabGroup
22163 * @param {Object} config The config object
22166 Roo.bootstrap.TabGroup = function(config){
22167 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22169 this.navId = Roo.id();
22172 Roo.bootstrap.TabGroup.register(this);
22176 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22179 transition : false,
22184 slideOnTouch : false,
22187 getAutoCreate : function()
22189 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22191 cfg.cls += ' tab-content';
22193 if (this.carousel) {
22194 cfg.cls += ' carousel slide';
22197 cls : 'carousel-inner',
22201 if(this.bullets && !Roo.isTouch){
22204 cls : 'carousel-bullets',
22208 if(this.bullets_cls){
22209 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22216 cfg.cn[0].cn.push(bullets);
22219 if(this.showarrow){
22220 cfg.cn[0].cn.push({
22222 class : 'carousel-arrow',
22226 class : 'carousel-prev',
22230 class : 'fa fa-chevron-left'
22236 class : 'carousel-next',
22240 class : 'fa fa-chevron-right'
22253 initEvents: function()
22255 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22256 // this.el.on("touchstart", this.onTouchStart, this);
22259 if(this.autoslide){
22262 this.slideFn = window.setInterval(function() {
22263 _this.showPanelNext();
22267 if(this.showarrow){
22268 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22269 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22275 // onTouchStart : function(e, el, o)
22277 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22281 // this.showPanelNext();
22285 getChildContainer : function()
22287 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22291 * register a Navigation item
22292 * @param {Roo.bootstrap.nav.Item} the navitem to add
22294 register : function(item)
22296 this.tabs.push( item);
22297 item.navId = this.navId; // not really needed..
22302 getActivePanel : function()
22305 Roo.each(this.tabs, function(t) {
22315 getPanelByName : function(n)
22318 Roo.each(this.tabs, function(t) {
22319 if (t.tabId == n) {
22327 indexOfPanel : function(p)
22330 Roo.each(this.tabs, function(t,i) {
22331 if (t.tabId == p.tabId) {
22340 * show a specific panel
22341 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22342 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22344 showPanel : function (pan)
22346 if(this.transition || typeof(pan) == 'undefined'){
22347 Roo.log("waiting for the transitionend");
22351 if (typeof(pan) == 'number') {
22352 pan = this.tabs[pan];
22355 if (typeof(pan) == 'string') {
22356 pan = this.getPanelByName(pan);
22359 var cur = this.getActivePanel();
22362 Roo.log('pan or acitve pan is undefined');
22366 if (pan.tabId == this.getActivePanel().tabId) {
22370 if (false === cur.fireEvent('beforedeactivate')) {
22374 if(this.bullets > 0 && !Roo.isTouch){
22375 this.setActiveBullet(this.indexOfPanel(pan));
22378 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22380 //class="carousel-item carousel-item-next carousel-item-left"
22382 this.transition = true;
22383 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22384 var lr = dir == 'next' ? 'left' : 'right';
22385 pan.el.addClass(dir); // or prev
22386 pan.el.addClass('carousel-item-' + dir); // or prev
22387 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22388 cur.el.addClass(lr); // or right
22389 pan.el.addClass(lr);
22390 cur.el.addClass('carousel-item-' +lr); // or right
22391 pan.el.addClass('carousel-item-' +lr);
22395 cur.el.on('transitionend', function() {
22396 Roo.log("trans end?");
22398 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22399 pan.setActive(true);
22401 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22402 cur.setActive(false);
22404 _this.transition = false;
22406 }, this, { single: true } );
22411 cur.setActive(false);
22412 pan.setActive(true);
22417 showPanelNext : function()
22419 var i = this.indexOfPanel(this.getActivePanel());
22421 if (i >= this.tabs.length - 1 && !this.autoslide) {
22425 if (i >= this.tabs.length - 1 && this.autoslide) {
22429 this.showPanel(this.tabs[i+1]);
22432 showPanelPrev : function()
22434 var i = this.indexOfPanel(this.getActivePanel());
22436 if (i < 1 && !this.autoslide) {
22440 if (i < 1 && this.autoslide) {
22441 i = this.tabs.length;
22444 this.showPanel(this.tabs[i-1]);
22448 addBullet: function()
22450 if(!this.bullets || Roo.isTouch){
22453 var ctr = this.el.select('.carousel-bullets',true).first();
22454 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22455 var bullet = ctr.createChild({
22456 cls : 'bullet bullet-' + i
22457 },ctr.dom.lastChild);
22462 bullet.on('click', (function(e, el, o, ii, t){
22464 e.preventDefault();
22466 this.showPanel(ii);
22468 if(this.autoslide && this.slideFn){
22469 clearInterval(this.slideFn);
22470 this.slideFn = window.setInterval(function() {
22471 _this.showPanelNext();
22475 }).createDelegate(this, [i, bullet], true));
22480 setActiveBullet : function(i)
22486 Roo.each(this.el.select('.bullet', true).elements, function(el){
22487 el.removeClass('selected');
22490 var bullet = this.el.select('.bullet-' + i, true).first();
22496 bullet.addClass('selected');
22507 Roo.apply(Roo.bootstrap.TabGroup, {
22511 * register a Navigation Group
22512 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22514 register : function(navgrp)
22516 this.groups[navgrp.navId] = navgrp;
22520 * fetch a Navigation Group based on the navigation ID
22521 * if one does not exist , it will get created.
22522 * @param {string} the navgroup to add
22523 * @returns {Roo.bootstrap.nav.Group} the navgroup
22525 get: function(navId) {
22526 if (typeof(this.groups[navId]) == 'undefined') {
22527 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22529 return this.groups[navId] ;
22544 * @class Roo.bootstrap.TabPanel
22545 * @extends Roo.bootstrap.Component
22546 * @children Roo.bootstrap.Component
22547 * Bootstrap TabPanel class
22548 * @cfg {Boolean} active panel active
22549 * @cfg {String} html panel content
22550 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22551 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22552 * @cfg {String} href click to link..
22553 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22557 * Create a new TabPanel
22558 * @param {Object} config The config object
22561 Roo.bootstrap.TabPanel = function(config){
22562 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22566 * Fires when the active status changes
22567 * @param {Roo.bootstrap.TabPanel} this
22568 * @param {Boolean} state the new state
22573 * @event beforedeactivate
22574 * Fires before a tab is de-activated - can be used to do validation on a form.
22575 * @param {Roo.bootstrap.TabPanel} this
22576 * @return {Boolean} false if there is an error
22579 'beforedeactivate': true
22582 this.tabId = this.tabId || Roo.id();
22586 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22593 touchSlide : false,
22594 getAutoCreate : function(){
22599 // item is needed for carousel - not sure if it has any effect otherwise
22600 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22601 html: this.html || ''
22605 cfg.cls += ' active';
22609 cfg.tabId = this.tabId;
22617 initEvents: function()
22619 var p = this.parent();
22621 this.navId = this.navId || p.navId;
22623 if (typeof(this.navId) != 'undefined') {
22624 // not really needed.. but just in case.. parent should be a NavGroup.
22625 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22629 var i = tg.tabs.length - 1;
22631 if(this.active && tg.bullets > 0 && i < tg.bullets){
22632 tg.setActiveBullet(i);
22636 this.el.on('click', this.onClick, this);
22638 if(Roo.isTouch && this.touchSlide){
22639 this.el.on("touchstart", this.onTouchStart, this);
22640 this.el.on("touchmove", this.onTouchMove, this);
22641 this.el.on("touchend", this.onTouchEnd, this);
22646 onRender : function(ct, position)
22648 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22651 setActive : function(state)
22653 Roo.log("panel - set active " + this.tabId + "=" + state);
22655 this.active = state;
22657 this.el.removeClass('active');
22659 } else if (!this.el.hasClass('active')) {
22660 this.el.addClass('active');
22663 this.fireEvent('changed', this, state);
22666 onClick : function(e)
22668 e.preventDefault();
22670 if(!this.href.length){
22674 window.location.href = this.href;
22683 onTouchStart : function(e)
22685 this.swiping = false;
22687 this.startX = e.browserEvent.touches[0].clientX;
22688 this.startY = e.browserEvent.touches[0].clientY;
22691 onTouchMove : function(e)
22693 this.swiping = true;
22695 this.endX = e.browserEvent.touches[0].clientX;
22696 this.endY = e.browserEvent.touches[0].clientY;
22699 onTouchEnd : function(e)
22706 var tabGroup = this.parent();
22708 if(this.endX > this.startX){ // swiping right
22709 tabGroup.showPanelPrev();
22713 if(this.startX > this.endX){ // swiping left
22714 tabGroup.showPanelNext();
22733 * @class Roo.bootstrap.form.DateField
22734 * @extends Roo.bootstrap.form.Input
22735 * Bootstrap DateField class
22736 * @cfg {Number} weekStart default 0
22737 * @cfg {String} viewMode default empty, (months|years)
22738 * @cfg {String} minViewMode default empty, (months|years)
22739 * @cfg {Number} startDate default -Infinity
22740 * @cfg {Number} endDate default Infinity
22741 * @cfg {Boolean} todayHighlight default false
22742 * @cfg {Boolean} todayBtn default false
22743 * @cfg {Boolean} calendarWeeks default false
22744 * @cfg {Object} daysOfWeekDisabled default empty
22745 * @cfg {Boolean} singleMode default false (true | false)
22747 * @cfg {Boolean} keyboardNavigation default true
22748 * @cfg {String} language default en
22751 * Create a new DateField
22752 * @param {Object} config The config object
22755 Roo.bootstrap.form.DateField = function(config){
22756 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22760 * Fires when this field show.
22761 * @param {Roo.bootstrap.form.DateField} this
22762 * @param {Mixed} date The date value
22767 * Fires when this field hide.
22768 * @param {Roo.bootstrap.form.DateField} this
22769 * @param {Mixed} date The date value
22774 * Fires when select a date.
22775 * @param {Roo.bootstrap.form.DateField} this
22776 * @param {Mixed} date The date value
22780 * @event beforeselect
22781 * Fires when before select a date.
22782 * @param {Roo.bootstrap.form.DateField} this
22783 * @param {Mixed} date The date value
22785 beforeselect : true
22789 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22792 * @cfg {String} format
22793 * The default date format string which can be overriden for localization support. The format must be
22794 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22798 * @cfg {String} altFormats
22799 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22800 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22802 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22810 todayHighlight : false,
22816 keyboardNavigation: true,
22818 calendarWeeks: false,
22820 startDate: -Infinity,
22824 daysOfWeekDisabled: [],
22828 singleMode : false,
22830 UTCDate: function()
22832 return new Date(Date.UTC.apply(Date, arguments));
22835 UTCToday: function()
22837 var today = new Date();
22838 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22841 getDate: function() {
22842 var d = this.getUTCDate();
22843 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22846 getUTCDate: function() {
22850 setDate: function(d) {
22851 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22854 setUTCDate: function(d) {
22856 this.setValue(this.formatDate(this.date));
22859 onRender: function(ct, position)
22862 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22864 this.language = this.language || 'en';
22865 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22866 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22868 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22869 this.format = this.format || 'm/d/y';
22870 this.isInline = false;
22871 this.isInput = true;
22872 this.component = this.el.select('.add-on', true).first() || false;
22873 this.component = (this.component && this.component.length === 0) ? false : this.component;
22874 this.hasInput = this.component && this.inputEl().length;
22876 if (typeof(this.minViewMode === 'string')) {
22877 switch (this.minViewMode) {
22879 this.minViewMode = 1;
22882 this.minViewMode = 2;
22885 this.minViewMode = 0;
22890 if (typeof(this.viewMode === 'string')) {
22891 switch (this.viewMode) {
22904 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22906 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22908 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22910 this.picker().on('mousedown', this.onMousedown, this);
22911 this.picker().on('click', this.onClick, this);
22913 this.picker().addClass('datepicker-dropdown');
22915 this.startViewMode = this.viewMode;
22917 if(this.singleMode){
22918 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22919 v.setVisibilityMode(Roo.Element.DISPLAY);
22923 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22924 v.setStyle('width', '189px');
22928 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22929 if(!this.calendarWeeks){
22934 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22935 v.attr('colspan', function(i, val){
22936 return parseInt(val) + 1;
22941 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22943 this.setStartDate(this.startDate);
22944 this.setEndDate(this.endDate);
22946 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22953 if(this.isInline) {
22958 picker : function()
22960 return this.pickerEl;
22961 // return this.el.select('.datepicker', true).first();
22964 fillDow: function()
22966 var dowCnt = this.weekStart;
22975 if(this.calendarWeeks){
22983 while (dowCnt < this.weekStart + 7) {
22987 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22991 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22994 fillMonths: function()
22997 var months = this.picker().select('>.datepicker-months td', true).first();
22999 months.dom.innerHTML = '';
23005 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23008 months.createChild(month);
23015 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;
23017 if (this.date < this.startDate) {
23018 this.viewDate = new Date(this.startDate);
23019 } else if (this.date > this.endDate) {
23020 this.viewDate = new Date(this.endDate);
23022 this.viewDate = new Date(this.date);
23030 var d = new Date(this.viewDate),
23031 year = d.getUTCFullYear(),
23032 month = d.getUTCMonth(),
23033 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23034 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23035 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23036 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23037 currentDate = this.date && this.date.valueOf(),
23038 today = this.UTCToday();
23040 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23042 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23044 // this.picker.select('>tfoot th.today').
23045 // .text(dates[this.language].today)
23046 // .toggle(this.todayBtn !== false);
23048 this.updateNavArrows();
23051 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23053 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23055 prevMonth.setUTCDate(day);
23057 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23059 var nextMonth = new Date(prevMonth);
23061 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23063 nextMonth = nextMonth.valueOf();
23065 var fillMonths = false;
23067 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23069 while(prevMonth.valueOf() <= nextMonth) {
23072 if (prevMonth.getUTCDay() === this.weekStart) {
23074 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23082 if(this.calendarWeeks){
23083 // ISO 8601: First week contains first thursday.
23084 // ISO also states week starts on Monday, but we can be more abstract here.
23086 // Start of current week: based on weekstart/current date
23087 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23088 // Thursday of this week
23089 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23090 // First Thursday of year, year from thursday
23091 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23092 // Calendar week: ms between thursdays, div ms per day, div 7 days
23093 calWeek = (th - yth) / 864e5 / 7 + 1;
23095 fillMonths.cn.push({
23103 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23105 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23108 if (this.todayHighlight &&
23109 prevMonth.getUTCFullYear() == today.getFullYear() &&
23110 prevMonth.getUTCMonth() == today.getMonth() &&
23111 prevMonth.getUTCDate() == today.getDate()) {
23112 clsName += ' today';
23115 if (currentDate && prevMonth.valueOf() === currentDate) {
23116 clsName += ' active';
23119 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23120 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23121 clsName += ' disabled';
23124 fillMonths.cn.push({
23126 cls: 'day ' + clsName,
23127 html: prevMonth.getDate()
23130 prevMonth.setDate(prevMonth.getDate()+1);
23133 var currentYear = this.date && this.date.getUTCFullYear();
23134 var currentMonth = this.date && this.date.getUTCMonth();
23136 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23138 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23139 v.removeClass('active');
23141 if(currentYear === year && k === currentMonth){
23142 v.addClass('active');
23145 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23146 v.addClass('disabled');
23152 year = parseInt(year/10, 10) * 10;
23154 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23156 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23159 for (var i = -1; i < 11; i++) {
23160 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23162 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23170 showMode: function(dir)
23173 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23176 Roo.each(this.picker().select('>div',true).elements, function(v){
23177 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23180 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23185 if(this.isInline) {
23189 this.picker().removeClass(['bottom', 'top']);
23191 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23193 * place to the top of element!
23197 this.picker().addClass('top');
23198 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23203 this.picker().addClass('bottom');
23205 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23208 parseDate : function(value)
23210 if(!value || value instanceof Date){
23213 var v = Date.parseDate(value, this.format);
23214 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23215 v = Date.parseDate(value, 'Y-m-d');
23217 if(!v && this.altFormats){
23218 if(!this.altFormatsArray){
23219 this.altFormatsArray = this.altFormats.split("|");
23221 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23222 v = Date.parseDate(value, this.altFormatsArray[i]);
23228 formatDate : function(date, fmt)
23230 return (!date || !(date instanceof Date)) ?
23231 date : date.dateFormat(fmt || this.format);
23234 onFocus : function()
23236 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23240 onBlur : function()
23242 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23244 var d = this.inputEl().getValue();
23251 showPopup : function()
23253 this.picker().show();
23257 this.fireEvent('showpopup', this, this.date);
23260 hidePopup : function()
23262 if(this.isInline) {
23265 this.picker().hide();
23266 this.viewMode = this.startViewMode;
23269 this.fireEvent('hidepopup', this, this.date);
23273 onMousedown: function(e)
23275 e.stopPropagation();
23276 e.preventDefault();
23281 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23285 setValue: function(v)
23287 if(this.fireEvent('beforeselect', this, v) !== false){
23288 var d = new Date(this.parseDate(v) ).clearTime();
23290 if(isNaN(d.getTime())){
23291 this.date = this.viewDate = '';
23292 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23296 v = this.formatDate(d);
23298 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23300 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23304 this.fireEvent('select', this, this.date);
23308 getValue: function()
23310 return this.formatDate(this.date);
23313 fireKey: function(e)
23315 if (!this.picker().isVisible()){
23316 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23322 var dateChanged = false,
23324 newDate, newViewDate;
23329 e.preventDefault();
23333 if (!this.keyboardNavigation) {
23336 dir = e.keyCode == 37 ? -1 : 1;
23339 newDate = this.moveYear(this.date, dir);
23340 newViewDate = this.moveYear(this.viewDate, dir);
23341 } else if (e.shiftKey){
23342 newDate = this.moveMonth(this.date, dir);
23343 newViewDate = this.moveMonth(this.viewDate, dir);
23345 newDate = new Date(this.date);
23346 newDate.setUTCDate(this.date.getUTCDate() + dir);
23347 newViewDate = new Date(this.viewDate);
23348 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23350 if (this.dateWithinRange(newDate)){
23351 this.date = newDate;
23352 this.viewDate = newViewDate;
23353 this.setValue(this.formatDate(this.date));
23355 e.preventDefault();
23356 dateChanged = true;
23361 if (!this.keyboardNavigation) {
23364 dir = e.keyCode == 38 ? -1 : 1;
23366 newDate = this.moveYear(this.date, dir);
23367 newViewDate = this.moveYear(this.viewDate, dir);
23368 } else if (e.shiftKey){
23369 newDate = this.moveMonth(this.date, dir);
23370 newViewDate = this.moveMonth(this.viewDate, dir);
23372 newDate = new Date(this.date);
23373 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23374 newViewDate = new Date(this.viewDate);
23375 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23377 if (this.dateWithinRange(newDate)){
23378 this.date = newDate;
23379 this.viewDate = newViewDate;
23380 this.setValue(this.formatDate(this.date));
23382 e.preventDefault();
23383 dateChanged = true;
23387 this.setValue(this.formatDate(this.date));
23389 e.preventDefault();
23392 this.setValue(this.formatDate(this.date));
23406 onClick: function(e)
23408 e.stopPropagation();
23409 e.preventDefault();
23411 var target = e.getTarget();
23413 if(target.nodeName.toLowerCase() === 'i'){
23414 target = Roo.get(target).dom.parentNode;
23417 var nodeName = target.nodeName;
23418 var className = target.className;
23419 var html = target.innerHTML;
23420 //Roo.log(nodeName);
23422 switch(nodeName.toLowerCase()) {
23424 switch(className) {
23430 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23431 switch(this.viewMode){
23433 this.viewDate = this.moveMonth(this.viewDate, dir);
23437 this.viewDate = this.moveYear(this.viewDate, dir);
23443 var date = new Date();
23444 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23446 this.setValue(this.formatDate(this.date));
23453 if (className.indexOf('disabled') < 0) {
23454 if (!this.viewDate) {
23455 this.viewDate = new Date();
23457 this.viewDate.setUTCDate(1);
23458 if (className.indexOf('month') > -1) {
23459 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23461 var year = parseInt(html, 10) || 0;
23462 this.viewDate.setUTCFullYear(year);
23466 if(this.singleMode){
23467 this.setValue(this.formatDate(this.viewDate));
23478 //Roo.log(className);
23479 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23480 var day = parseInt(html, 10) || 1;
23481 var year = (this.viewDate || new Date()).getUTCFullYear(),
23482 month = (this.viewDate || new Date()).getUTCMonth();
23484 if (className.indexOf('old') > -1) {
23491 } else if (className.indexOf('new') > -1) {
23499 //Roo.log([year,month,day]);
23500 this.date = this.UTCDate(year, month, day,0,0,0,0);
23501 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23503 //Roo.log(this.formatDate(this.date));
23504 this.setValue(this.formatDate(this.date));
23511 setStartDate: function(startDate)
23513 this.startDate = startDate || -Infinity;
23514 if (this.startDate !== -Infinity) {
23515 this.startDate = this.parseDate(this.startDate);
23518 this.updateNavArrows();
23521 setEndDate: function(endDate)
23523 this.endDate = endDate || Infinity;
23524 if (this.endDate !== Infinity) {
23525 this.endDate = this.parseDate(this.endDate);
23528 this.updateNavArrows();
23531 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23533 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23534 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23535 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23537 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23538 return parseInt(d, 10);
23541 this.updateNavArrows();
23544 updateNavArrows: function()
23546 if(this.singleMode){
23550 var d = new Date(this.viewDate),
23551 year = d.getUTCFullYear(),
23552 month = d.getUTCMonth();
23554 Roo.each(this.picker().select('.prev', true).elements, function(v){
23556 switch (this.viewMode) {
23559 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23565 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23572 Roo.each(this.picker().select('.next', true).elements, function(v){
23574 switch (this.viewMode) {
23577 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23583 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23591 moveMonth: function(date, dir)
23596 var new_date = new Date(date.valueOf()),
23597 day = new_date.getUTCDate(),
23598 month = new_date.getUTCMonth(),
23599 mag = Math.abs(dir),
23601 dir = dir > 0 ? 1 : -1;
23604 // If going back one month, make sure month is not current month
23605 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23607 return new_date.getUTCMonth() == month;
23609 // If going forward one month, make sure month is as expected
23610 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23612 return new_date.getUTCMonth() != new_month;
23614 new_month = month + dir;
23615 new_date.setUTCMonth(new_month);
23616 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23617 if (new_month < 0 || new_month > 11) {
23618 new_month = (new_month + 12) % 12;
23621 // For magnitudes >1, move one month at a time...
23622 for (var i=0; i<mag; i++) {
23623 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23624 new_date = this.moveMonth(new_date, dir);
23626 // ...then reset the day, keeping it in the new month
23627 new_month = new_date.getUTCMonth();
23628 new_date.setUTCDate(day);
23630 return new_month != new_date.getUTCMonth();
23633 // Common date-resetting loop -- if date is beyond end of month, make it
23636 new_date.setUTCDate(--day);
23637 new_date.setUTCMonth(new_month);
23642 moveYear: function(date, dir)
23644 return this.moveMonth(date, dir*12);
23647 dateWithinRange: function(date)
23649 return date >= this.startDate && date <= this.endDate;
23655 this.picker().remove();
23658 validateValue : function(value)
23660 if(this.getVisibilityEl().hasClass('hidden')){
23664 if(value.length < 1) {
23665 if(this.allowBlank){
23671 if(value.length < this.minLength){
23674 if(value.length > this.maxLength){
23678 var vt = Roo.form.VTypes;
23679 if(!vt[this.vtype](value, this)){
23683 if(typeof this.validator == "function"){
23684 var msg = this.validator(value);
23690 if(this.regex && !this.regex.test(value)){
23694 if(typeof(this.parseDate(value)) == 'undefined'){
23698 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23702 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23712 this.date = this.viewDate = '';
23714 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23719 Roo.apply(Roo.bootstrap.form.DateField, {
23730 html: '<i class="fa fa-arrow-left"/>'
23740 html: '<i class="fa fa-arrow-right"/>'
23782 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23783 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23784 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23785 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23786 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23799 navFnc: 'FullYear',
23804 navFnc: 'FullYear',
23809 Roo.apply(Roo.bootstrap.form.DateField, {
23813 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23817 cls: 'datepicker-days',
23821 cls: 'table-condensed',
23823 Roo.bootstrap.form.DateField.head,
23827 Roo.bootstrap.form.DateField.footer
23834 cls: 'datepicker-months',
23838 cls: 'table-condensed',
23840 Roo.bootstrap.form.DateField.head,
23841 Roo.bootstrap.form.DateField.content,
23842 Roo.bootstrap.form.DateField.footer
23849 cls: 'datepicker-years',
23853 cls: 'table-condensed',
23855 Roo.bootstrap.form.DateField.head,
23856 Roo.bootstrap.form.DateField.content,
23857 Roo.bootstrap.form.DateField.footer
23876 * @class Roo.bootstrap.form.TimeField
23877 * @extends Roo.bootstrap.form.Input
23878 * Bootstrap DateField class
23882 * Create a new TimeField
23883 * @param {Object} config The config object
23886 Roo.bootstrap.form.TimeField = function(config){
23887 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23891 * Fires when this field show.
23892 * @param {Roo.bootstrap.form.DateField} thisthis
23893 * @param {Mixed} date The date value
23898 * Fires when this field hide.
23899 * @param {Roo.bootstrap.form.DateField} this
23900 * @param {Mixed} date The date value
23905 * Fires when select a date.
23906 * @param {Roo.bootstrap.form.DateField} this
23907 * @param {Mixed} date The date value
23913 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23916 * @cfg {String} format
23917 * The default time format string which can be overriden for localization support. The format must be
23918 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23922 getAutoCreate : function()
23924 this.after = '<i class="fa far fa-clock"></i>';
23925 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23929 onRender: function(ct, position)
23932 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23934 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23936 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23938 this.pop = this.picker().select('>.datepicker-time',true).first();
23939 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23941 this.picker().on('mousedown', this.onMousedown, this);
23942 this.picker().on('click', this.onClick, this);
23944 this.picker().addClass('datepicker-dropdown');
23949 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23950 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23951 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23952 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23953 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23954 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23958 fireKey: function(e){
23959 if (!this.picker().isVisible()){
23960 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23966 e.preventDefault();
23974 this.onTogglePeriod();
23977 this.onIncrementMinutes();
23980 this.onDecrementMinutes();
23989 onClick: function(e) {
23990 e.stopPropagation();
23991 e.preventDefault();
23994 picker : function()
23996 return this.pickerEl;
23999 fillTime: function()
24001 var time = this.pop.select('tbody', true).first();
24003 time.dom.innerHTML = '';
24018 cls: 'hours-up fa fas fa-chevron-up'
24038 cls: 'minutes-up fa fas fa-chevron-up'
24059 cls: 'timepicker-hour',
24074 cls: 'timepicker-minute',
24089 cls: 'btn btn-primary period',
24111 cls: 'hours-down fa fas fa-chevron-down'
24131 cls: 'minutes-down fa fas fa-chevron-down'
24149 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24156 var hours = this.time.getHours();
24157 var minutes = this.time.getMinutes();
24170 hours = hours - 12;
24174 hours = '0' + hours;
24178 minutes = '0' + minutes;
24181 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24182 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24183 this.pop.select('button', true).first().dom.innerHTML = period;
24189 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24191 var cls = ['bottom'];
24193 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24200 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24204 //this.picker().setXY(20000,20000);
24205 this.picker().addClass(cls.join('-'));
24209 Roo.each(cls, function(c){
24214 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24215 //_this.picker().setTop(_this.inputEl().getHeight());
24219 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24221 //_this.picker().setTop(0 - _this.picker().getHeight());
24226 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24230 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24238 onFocus : function()
24240 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24244 onBlur : function()
24246 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24252 this.picker().show();
24257 this.fireEvent('show', this, this.date);
24262 this.picker().hide();
24265 this.fireEvent('hide', this, this.date);
24268 setTime : function()
24271 this.setValue(this.time.format(this.format));
24273 this.fireEvent('select', this, this.date);
24278 onMousedown: function(e){
24279 e.stopPropagation();
24280 e.preventDefault();
24283 onIncrementHours: function()
24285 Roo.log('onIncrementHours');
24286 this.time = this.time.add(Date.HOUR, 1);
24291 onDecrementHours: function()
24293 Roo.log('onDecrementHours');
24294 this.time = this.time.add(Date.HOUR, -1);
24298 onIncrementMinutes: function()
24300 Roo.log('onIncrementMinutes');
24301 this.time = this.time.add(Date.MINUTE, 1);
24305 onDecrementMinutes: function()
24307 Roo.log('onDecrementMinutes');
24308 this.time = this.time.add(Date.MINUTE, -1);
24312 onTogglePeriod: function()
24314 Roo.log('onTogglePeriod');
24315 this.time = this.time.add(Date.HOUR, 12);
24323 Roo.apply(Roo.bootstrap.form.TimeField, {
24327 cls: 'datepicker dropdown-menu',
24331 cls: 'datepicker-time',
24335 cls: 'table-condensed',
24364 cls: 'btn btn-info ok',
24392 * @class Roo.bootstrap.form.MonthField
24393 * @extends Roo.bootstrap.form.Input
24394 * Bootstrap MonthField class
24396 * @cfg {String} language default en
24399 * Create a new MonthField
24400 * @param {Object} config The config object
24403 Roo.bootstrap.form.MonthField = function(config){
24404 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24409 * Fires when this field show.
24410 * @param {Roo.bootstrap.form.MonthField} this
24411 * @param {Mixed} date The date value
24416 * Fires when this field hide.
24417 * @param {Roo.bootstrap.form.MonthField} this
24418 * @param {Mixed} date The date value
24423 * Fires when select a date.
24424 * @param {Roo.bootstrap.form.MonthField} this
24425 * @param {String} oldvalue The old value
24426 * @param {String} newvalue The new value
24432 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24434 onRender: function(ct, position)
24437 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24439 this.language = this.language || 'en';
24440 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24441 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24443 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24444 this.isInline = false;
24445 this.isInput = true;
24446 this.component = this.el.select('.add-on', true).first() || false;
24447 this.component = (this.component && this.component.length === 0) ? false : this.component;
24448 this.hasInput = this.component && this.inputEL().length;
24450 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24452 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24454 this.picker().on('mousedown', this.onMousedown, this);
24455 this.picker().on('click', this.onClick, this);
24457 this.picker().addClass('datepicker-dropdown');
24459 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24460 v.setStyle('width', '189px');
24467 if(this.isInline) {
24473 setValue: function(v, suppressEvent)
24475 var o = this.getValue();
24477 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24481 if(suppressEvent !== true){
24482 this.fireEvent('select', this, o, v);
24487 getValue: function()
24492 onClick: function(e)
24494 e.stopPropagation();
24495 e.preventDefault();
24497 var target = e.getTarget();
24499 if(target.nodeName.toLowerCase() === 'i'){
24500 target = Roo.get(target).dom.parentNode;
24503 var nodeName = target.nodeName;
24504 var className = target.className;
24505 var html = target.innerHTML;
24507 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24511 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24513 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24519 picker : function()
24521 return this.pickerEl;
24524 fillMonths: function()
24527 var months = this.picker().select('>.datepicker-months td', true).first();
24529 months.dom.innerHTML = '';
24535 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24538 months.createChild(month);
24547 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24548 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24551 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24552 e.removeClass('active');
24554 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24555 e.addClass('active');
24562 if(this.isInline) {
24566 this.picker().removeClass(['bottom', 'top']);
24568 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24570 * place to the top of element!
24574 this.picker().addClass('top');
24575 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24580 this.picker().addClass('bottom');
24582 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24585 onFocus : function()
24587 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24591 onBlur : function()
24593 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24595 var d = this.inputEl().getValue();
24604 this.picker().show();
24605 this.picker().select('>.datepicker-months', true).first().show();
24609 this.fireEvent('show', this, this.date);
24614 if(this.isInline) {
24617 this.picker().hide();
24618 this.fireEvent('hide', this, this.date);
24622 onMousedown: function(e)
24624 e.stopPropagation();
24625 e.preventDefault();
24630 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24634 fireKey: function(e)
24636 if (!this.picker().isVisible()){
24637 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24648 e.preventDefault();
24652 dir = e.keyCode == 37 ? -1 : 1;
24654 this.vIndex = this.vIndex + dir;
24656 if(this.vIndex < 0){
24660 if(this.vIndex > 11){
24664 if(isNaN(this.vIndex)){
24668 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24674 dir = e.keyCode == 38 ? -1 : 1;
24676 this.vIndex = this.vIndex + dir * 4;
24678 if(this.vIndex < 0){
24682 if(this.vIndex > 11){
24686 if(isNaN(this.vIndex)){
24690 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24695 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24696 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24700 e.preventDefault();
24703 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24704 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720 this.picker().remove();
24725 Roo.apply(Roo.bootstrap.form.MonthField, {
24744 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24745 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24750 Roo.apply(Roo.bootstrap.form.MonthField, {
24754 cls: 'datepicker dropdown-menu roo-dynamic',
24758 cls: 'datepicker-months',
24762 cls: 'table-condensed',
24764 Roo.bootstrap.form.DateField.content
24784 * @class Roo.bootstrap.form.CheckBox
24785 * @extends Roo.bootstrap.form.Input
24786 * Bootstrap CheckBox class
24788 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24789 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24790 * @cfg {String} boxLabel The text that appears beside the checkbox
24791 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24792 * @cfg {Boolean} checked initnal the element
24793 * @cfg {Boolean} inline inline the element (default false)
24794 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24795 * @cfg {String} tooltip label tooltip
24798 * Create a new CheckBox
24799 * @param {Object} config The config object
24802 Roo.bootstrap.form.CheckBox = function(config){
24803 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24808 * Fires when the element is checked or unchecked.
24809 * @param {Roo.bootstrap.form.CheckBox} this This input
24810 * @param {Boolean} checked The new checked value
24815 * Fires when the element is click.
24816 * @param {Roo.bootstrap.form.CheckBox} this This input
24823 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24825 inputType: 'checkbox',
24834 // checkbox success does not make any sense really..
24839 getAutoCreate : function()
24841 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24847 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24850 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24856 type : this.inputType,
24857 value : this.inputValue,
24858 cls : 'roo-' + this.inputType, //'form-box',
24859 placeholder : this.placeholder || ''
24863 if(this.inputType != 'radio'){
24867 cls : 'roo-hidden-value',
24868 value : this.checked ? this.inputValue : this.valueOff
24873 if (this.weight) { // Validity check?
24874 cfg.cls += " " + this.inputType + "-" + this.weight;
24877 if (this.disabled) {
24878 input.disabled=true;
24882 input.checked = this.checked;
24887 input.name = this.name;
24889 if(this.inputType != 'radio'){
24890 hidden.name = this.name;
24891 input.name = '_hidden_' + this.name;
24896 input.cls += ' input-' + this.size;
24901 ['xs','sm','md','lg'].map(function(size){
24902 if (settings[size]) {
24903 cfg.cls += ' col-' + size + '-' + settings[size];
24907 var inputblock = input;
24909 if (this.before || this.after) {
24912 cls : 'input-group',
24917 inputblock.cn.push({
24919 cls : 'input-group-addon',
24924 inputblock.cn.push(input);
24926 if(this.inputType != 'radio'){
24927 inputblock.cn.push(hidden);
24931 inputblock.cn.push({
24933 cls : 'input-group-addon',
24939 var boxLabelCfg = false;
24945 //'for': id, // box label is handled by onclick - so no for...
24947 html: this.boxLabel
24950 boxLabelCfg.tooltip = this.tooltip;
24956 if (align ==='left' && this.fieldLabel.length) {
24957 // Roo.log("left and has label");
24962 cls : 'control-label',
24963 html : this.fieldLabel
24974 cfg.cn[1].cn.push(boxLabelCfg);
24977 if(this.labelWidth > 12){
24978 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24981 if(this.labelWidth < 13 && this.labelmd == 0){
24982 this.labelmd = this.labelWidth;
24985 if(this.labellg > 0){
24986 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24987 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24990 if(this.labelmd > 0){
24991 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24992 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24995 if(this.labelsm > 0){
24996 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24997 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25000 if(this.labelxs > 0){
25001 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25002 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25005 } else if ( this.fieldLabel.length) {
25006 // Roo.log(" label");
25010 tag: this.boxLabel ? 'span' : 'label',
25012 cls: 'control-label box-input-label',
25013 //cls : 'input-group-addon',
25014 html : this.fieldLabel
25021 cfg.cn.push(boxLabelCfg);
25026 // Roo.log(" no label && no align");
25027 cfg.cn = [ inputblock ] ;
25029 cfg.cn.push(boxLabelCfg);
25037 if(this.inputType != 'radio'){
25038 cfg.cn.push(hidden);
25046 * return the real input element.
25048 inputEl: function ()
25050 return this.el.select('input.roo-' + this.inputType,true).first();
25052 hiddenEl: function ()
25054 return this.el.select('input.roo-hidden-value',true).first();
25057 labelEl: function()
25059 return this.el.select('label.control-label',true).first();
25061 /* depricated... */
25065 return this.labelEl();
25068 boxLabelEl: function()
25070 return this.el.select('label.box-label',true).first();
25073 initEvents : function()
25075 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25077 this.inputEl().on('click', this.onClick, this);
25079 if (this.boxLabel) {
25080 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25083 this.startValue = this.getValue();
25086 Roo.bootstrap.form.CheckBox.register(this);
25090 onClick : function(e)
25092 if(this.fireEvent('click', this, e) !== false){
25093 this.setChecked(!this.checked);
25098 setChecked : function(state,suppressEvent)
25100 this.startValue = this.getValue();
25102 if(this.inputType == 'radio'){
25104 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25105 e.dom.checked = false;
25108 this.inputEl().dom.checked = true;
25110 this.inputEl().dom.value = this.inputValue;
25112 if(suppressEvent !== true){
25113 this.fireEvent('check', this, true);
25121 this.checked = state;
25123 this.inputEl().dom.checked = state;
25126 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25128 if(suppressEvent !== true){
25129 this.fireEvent('check', this, state);
25135 getValue : function()
25137 if(this.inputType == 'radio'){
25138 return this.getGroupValue();
25141 return this.hiddenEl().dom.value;
25145 getGroupValue : function()
25147 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25151 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25154 setValue : function(v,suppressEvent)
25156 if(this.inputType == 'radio'){
25157 this.setGroupValue(v, suppressEvent);
25161 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25166 setGroupValue : function(v, suppressEvent)
25168 this.startValue = this.getValue();
25170 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171 e.dom.checked = false;
25173 if(e.dom.value == v){
25174 e.dom.checked = true;
25178 if(suppressEvent !== true){
25179 this.fireEvent('check', this, true);
25187 validate : function()
25189 if(this.getVisibilityEl().hasClass('hidden')){
25195 (this.inputType == 'radio' && this.validateRadio()) ||
25196 (this.inputType == 'checkbox' && this.validateCheckbox())
25202 this.markInvalid();
25206 validateRadio : function()
25208 if(this.getVisibilityEl().hasClass('hidden')){
25212 if(this.allowBlank){
25218 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25219 if(!e.dom.checked){
25231 validateCheckbox : function()
25234 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25235 //return (this.getValue() == this.inputValue) ? true : false;
25238 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25246 for(var i in group){
25247 if(group[i].el.isVisible(true)){
25255 for(var i in group){
25260 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25267 * Mark this field as valid
25269 markValid : function()
25273 this.fireEvent('valid', this);
25275 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25278 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25285 if(this.inputType == 'radio'){
25286 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25287 var fg = e.findParent('.form-group', false, true);
25288 if (Roo.bootstrap.version == 3) {
25289 fg.removeClass([_this.invalidClass, _this.validClass]);
25290 fg.addClass(_this.validClass);
25292 fg.removeClass(['is-valid', 'is-invalid']);
25293 fg.addClass('is-valid');
25301 var fg = this.el.findParent('.form-group', false, true);
25302 if (Roo.bootstrap.version == 3) {
25303 fg.removeClass([this.invalidClass, this.validClass]);
25304 fg.addClass(this.validClass);
25306 fg.removeClass(['is-valid', 'is-invalid']);
25307 fg.addClass('is-valid');
25312 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25318 for(var i in group){
25319 var fg = group[i].el.findParent('.form-group', false, true);
25320 if (Roo.bootstrap.version == 3) {
25321 fg.removeClass([this.invalidClass, this.validClass]);
25322 fg.addClass(this.validClass);
25324 fg.removeClass(['is-valid', 'is-invalid']);
25325 fg.addClass('is-valid');
25331 * Mark this field as invalid
25332 * @param {String} msg The validation message
25334 markInvalid : function(msg)
25336 if(this.allowBlank){
25342 this.fireEvent('invalid', this, msg);
25344 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25347 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25351 label.markInvalid();
25354 if(this.inputType == 'radio'){
25356 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25357 var fg = e.findParent('.form-group', false, true);
25358 if (Roo.bootstrap.version == 3) {
25359 fg.removeClass([_this.invalidClass, _this.validClass]);
25360 fg.addClass(_this.invalidClass);
25362 fg.removeClass(['is-invalid', 'is-valid']);
25363 fg.addClass('is-invalid');
25371 var fg = this.el.findParent('.form-group', false, true);
25372 if (Roo.bootstrap.version == 3) {
25373 fg.removeClass([_this.invalidClass, _this.validClass]);
25374 fg.addClass(_this.invalidClass);
25376 fg.removeClass(['is-invalid', 'is-valid']);
25377 fg.addClass('is-invalid');
25382 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25388 for(var i in group){
25389 var fg = group[i].el.findParent('.form-group', false, true);
25390 if (Roo.bootstrap.version == 3) {
25391 fg.removeClass([_this.invalidClass, _this.validClass]);
25392 fg.addClass(_this.invalidClass);
25394 fg.removeClass(['is-invalid', 'is-valid']);
25395 fg.addClass('is-invalid');
25401 clearInvalid : function()
25403 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25405 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25407 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25409 if (label && label.iconEl) {
25410 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25411 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25415 disable : function()
25417 if(this.inputType != 'radio'){
25418 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25425 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25426 _this.getActionEl().addClass(this.disabledClass);
25427 e.dom.disabled = true;
25431 this.disabled = true;
25432 this.fireEvent("disable", this);
25436 enable : function()
25438 if(this.inputType != 'radio'){
25439 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25446 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25447 _this.getActionEl().removeClass(this.disabledClass);
25448 e.dom.disabled = false;
25452 this.disabled = false;
25453 this.fireEvent("enable", this);
25457 setBoxLabel : function(v)
25462 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25468 Roo.apply(Roo.bootstrap.form.CheckBox, {
25473 * register a CheckBox Group
25474 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25476 register : function(checkbox)
25478 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25479 this.groups[checkbox.groupId] = {};
25482 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25486 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25490 * fetch a CheckBox Group based on the group ID
25491 * @param {string} the group ID
25492 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25494 get: function(groupId) {
25495 if (typeof(this.groups[groupId]) == 'undefined') {
25499 return this.groups[groupId] ;
25512 * @class Roo.bootstrap.form.Radio
25513 * @extends Roo.bootstrap.Component
25514 * Bootstrap Radio class
25515 * @cfg {String} boxLabel - the label associated
25516 * @cfg {String} value - the value of radio
25519 * Create a new Radio
25520 * @param {Object} config The config object
25522 Roo.bootstrap.form.Radio = function(config){
25523 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25527 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25533 getAutoCreate : function()
25537 cls : 'form-group radio',
25542 html : this.boxLabel
25550 initEvents : function()
25552 this.parent().register(this);
25554 this.el.on('click', this.onClick, this);
25558 onClick : function(e)
25560 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25561 this.setChecked(true);
25565 setChecked : function(state, suppressEvent)
25567 this.parent().setValue(this.value, suppressEvent);
25571 setBoxLabel : function(v)
25576 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25591 * @class Roo.bootstrap.form.SecurePass
25592 * @extends Roo.bootstrap.form.Input
25593 * Bootstrap SecurePass class
25597 * Create a new SecurePass
25598 * @param {Object} config The config object
25601 Roo.bootstrap.form.SecurePass = function (config) {
25602 // these go here, so the translation tool can replace them..
25604 PwdEmpty: "Please type a password, and then retype it to confirm.",
25605 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25606 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25607 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25608 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25609 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25610 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25611 TooWeak: "Your password is Too Weak."
25613 this.meterLabel = "Password strength:";
25614 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25615 this.meterClass = [
25616 "roo-password-meter-tooweak",
25617 "roo-password-meter-weak",
25618 "roo-password-meter-medium",
25619 "roo-password-meter-strong",
25620 "roo-password-meter-grey"
25625 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25628 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25630 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25632 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25633 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25634 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25635 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25636 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25637 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25638 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25648 * @cfg {String/Object} Label for the strength meter (defaults to
25649 * 'Password strength:')
25654 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25655 * ['Weak', 'Medium', 'Strong'])
25658 pwdStrengths: false,
25671 initEvents: function ()
25673 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25675 if (this.el.is('input[type=password]') && Roo.isSafari) {
25676 this.el.on('keydown', this.SafariOnKeyDown, this);
25679 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25682 onRender: function (ct, position)
25684 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25685 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25686 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25688 this.trigger.createChild({
25693 cls: 'roo-password-meter-grey col-xs-12',
25696 //width: this.meterWidth + 'px'
25700 cls: 'roo-password-meter-text'
25706 if (this.hideTrigger) {
25707 this.trigger.setDisplayed(false);
25709 this.setSize(this.width || '', this.height || '');
25712 onDestroy: function ()
25714 if (this.trigger) {
25715 this.trigger.removeAllListeners();
25716 this.trigger.remove();
25719 this.wrap.remove();
25721 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25724 checkStrength: function ()
25726 var pwd = this.inputEl().getValue();
25727 if (pwd == this._lastPwd) {
25732 if (this.ClientSideStrongPassword(pwd)) {
25734 } else if (this.ClientSideMediumPassword(pwd)) {
25736 } else if (this.ClientSideWeakPassword(pwd)) {
25742 Roo.log('strength1: ' + strength);
25744 //var pm = this.trigger.child('div/div/div').dom;
25745 var pm = this.trigger.child('div/div');
25746 pm.removeClass(this.meterClass);
25747 pm.addClass(this.meterClass[strength]);
25750 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25752 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25754 this._lastPwd = pwd;
25758 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25760 this._lastPwd = '';
25762 var pm = this.trigger.child('div/div');
25763 pm.removeClass(this.meterClass);
25764 pm.addClass('roo-password-meter-grey');
25767 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25770 this.inputEl().dom.type='password';
25773 validateValue: function (value)
25775 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25778 if (value.length == 0) {
25779 if (this.allowBlank) {
25780 this.clearInvalid();
25784 this.markInvalid(this.errors.PwdEmpty);
25785 this.errorMsg = this.errors.PwdEmpty;
25793 if (!value.match(/[\x21-\x7e]+/)) {
25794 this.markInvalid(this.errors.PwdBadChar);
25795 this.errorMsg = this.errors.PwdBadChar;
25798 if (value.length < 6) {
25799 this.markInvalid(this.errors.PwdShort);
25800 this.errorMsg = this.errors.PwdShort;
25803 if (value.length > 16) {
25804 this.markInvalid(this.errors.PwdLong);
25805 this.errorMsg = this.errors.PwdLong;
25809 if (this.ClientSideStrongPassword(value)) {
25811 } else if (this.ClientSideMediumPassword(value)) {
25813 } else if (this.ClientSideWeakPassword(value)) {
25820 if (strength < 2) {
25821 //this.markInvalid(this.errors.TooWeak);
25822 this.errorMsg = this.errors.TooWeak;
25827 console.log('strength2: ' + strength);
25829 //var pm = this.trigger.child('div/div/div').dom;
25831 var pm = this.trigger.child('div/div');
25832 pm.removeClass(this.meterClass);
25833 pm.addClass(this.meterClass[strength]);
25835 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25837 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25839 this.errorMsg = '';
25843 CharacterSetChecks: function (type)
25846 this.fResult = false;
25849 isctype: function (character, type)
25852 case this.kCapitalLetter:
25853 if (character >= 'A' && character <= 'Z') {
25858 case this.kSmallLetter:
25859 if (character >= 'a' && character <= 'z') {
25865 if (character >= '0' && character <= '9') {
25870 case this.kPunctuation:
25871 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25882 IsLongEnough: function (pwd, size)
25884 return !(pwd == null || isNaN(size) || pwd.length < size);
25887 SpansEnoughCharacterSets: function (word, nb)
25889 if (!this.IsLongEnough(word, nb))
25894 var characterSetChecks = new Array(
25895 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25896 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25899 for (var index = 0; index < word.length; ++index) {
25900 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25901 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25902 characterSetChecks[nCharSet].fResult = true;
25909 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25910 if (characterSetChecks[nCharSet].fResult) {
25915 if (nCharSets < nb) {
25921 ClientSideStrongPassword: function (pwd)
25923 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25926 ClientSideMediumPassword: function (pwd)
25928 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25931 ClientSideWeakPassword: function (pwd)
25933 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25936 })//<script type="text/javascript">
25939 * Based Ext JS Library 1.1.1
25940 * Copyright(c) 2006-2007, Ext JS, LLC.
25946 * @class Roo.HtmlEditorCore
25947 * @extends Roo.Component
25948 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25950 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25953 Roo.HtmlEditorCore = function(config){
25956 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25961 * @event initialize
25962 * Fires when the editor is fully initialized (including the iframe)
25963 * @param {Roo.HtmlEditorCore} this
25968 * Fires when the editor is first receives the focus. Any insertion must wait
25969 * until after this event.
25970 * @param {Roo.HtmlEditorCore} this
25974 * @event beforesync
25975 * Fires before the textarea is updated with content from the editor iframe. Return false
25976 * to cancel the sync.
25977 * @param {Roo.HtmlEditorCore} this
25978 * @param {String} html
25982 * @event beforepush
25983 * Fires before the iframe editor is updated with content from the textarea. Return false
25984 * to cancel the push.
25985 * @param {Roo.HtmlEditorCore} this
25986 * @param {String} html
25991 * Fires when the textarea is updated with content from the editor iframe.
25992 * @param {Roo.HtmlEditorCore} this
25993 * @param {String} html
25998 * Fires when the iframe editor is updated with content from the textarea.
25999 * @param {Roo.HtmlEditorCore} this
26000 * @param {String} html
26005 * @event editorevent
26006 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26007 * @param {Roo.HtmlEditorCore} this
26013 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26015 // defaults : white / black...
26016 this.applyBlacklists();
26023 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26027 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26033 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26038 * @cfg {Number} height (in pixels)
26042 * @cfg {Number} width (in pixels)
26047 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26050 stylesheets: false,
26053 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26055 allowComments: false,
26059 // private properties
26060 validationEvent : false,
26062 initialized : false,
26064 sourceEditMode : false,
26065 onFocus : Roo.emptyFn,
26067 hideMode:'offsets',
26071 // blacklist + whitelisted elements..
26078 * Protected method that will not generally be called directly. It
26079 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26080 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26082 getDocMarkup : function(){
26086 // inherit styels from page...??
26087 if (this.stylesheets === false) {
26089 Roo.get(document.head).select('style').each(function(node) {
26090 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26093 Roo.get(document.head).select('link').each(function(node) {
26094 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26097 } else if (!this.stylesheets.length) {
26099 st = '<style type="text/css">' +
26100 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26103 for (var i in this.stylesheets) {
26104 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26109 st += '<style type="text/css">' +
26110 'IMG { cursor: pointer } ' +
26113 var cls = 'roo-htmleditor-body';
26115 if(this.bodyCls.length){
26116 cls += ' ' + this.bodyCls;
26119 return '<html><head>' + st +
26120 //<style type="text/css">' +
26121 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26123 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26127 onRender : function(ct, position)
26130 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26131 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26134 this.el.dom.style.border = '0 none';
26135 this.el.dom.setAttribute('tabIndex', -1);
26136 this.el.addClass('x-hidden hide');
26140 if(Roo.isIE){ // fix IE 1px bogus margin
26141 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26145 this.frameId = Roo.id();
26149 var iframe = this.owner.wrap.createChild({
26151 cls: 'form-control', // bootstrap..
26153 name: this.frameId,
26154 frameBorder : 'no',
26155 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26160 this.iframe = iframe.dom;
26162 this.assignDocWin();
26164 this.doc.designMode = 'on';
26167 this.doc.write(this.getDocMarkup());
26171 var task = { // must defer to wait for browser to be ready
26173 //console.log("run task?" + this.doc.readyState);
26174 this.assignDocWin();
26175 if(this.doc.body || this.doc.readyState == 'complete'){
26177 this.doc.designMode="on";
26181 Roo.TaskMgr.stop(task);
26182 this.initEditor.defer(10, this);
26189 Roo.TaskMgr.start(task);
26194 onResize : function(w, h)
26196 Roo.log('resize: ' +w + ',' + h );
26197 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26201 if(typeof w == 'number'){
26203 this.iframe.style.width = w + 'px';
26205 if(typeof h == 'number'){
26207 this.iframe.style.height = h + 'px';
26209 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26216 * Toggles the editor between standard and source edit mode.
26217 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26219 toggleSourceEdit : function(sourceEditMode){
26221 this.sourceEditMode = sourceEditMode === true;
26223 if(this.sourceEditMode){
26225 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26228 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26229 //this.iframe.className = '';
26232 //this.setSize(this.owner.wrap.getSize());
26233 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26240 * Protected method that will not generally be called directly. If you need/want
26241 * custom HTML cleanup, this is the method you should override.
26242 * @param {String} html The HTML to be cleaned
26243 * return {String} The cleaned HTML
26245 cleanHtml : function(html){
26246 html = String(html);
26247 if(html.length > 5){
26248 if(Roo.isSafari){ // strip safari nonsense
26249 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26252 if(html == ' '){
26259 * HTML Editor -> Textarea
26260 * Protected method that will not generally be called directly. Syncs the contents
26261 * of the editor iframe with the textarea.
26263 syncValue : function(){
26264 if(this.initialized){
26265 var bd = (this.doc.body || this.doc.documentElement);
26266 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26267 var html = bd.innerHTML;
26269 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26270 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26272 html = '<div style="'+m[0]+'">' + html + '</div>';
26275 html = this.cleanHtml(html);
26276 // fix up the special chars.. normaly like back quotes in word...
26277 // however we do not want to do this with chinese..
26278 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26280 var cc = match.charCodeAt();
26282 // Get the character value, handling surrogate pairs
26283 if (match.length == 2) {
26284 // It's a surrogate pair, calculate the Unicode code point
26285 var high = match.charCodeAt(0) - 0xD800;
26286 var low = match.charCodeAt(1) - 0xDC00;
26287 cc = (high * 0x400) + low + 0x10000;
26289 (cc >= 0x4E00 && cc < 0xA000 ) ||
26290 (cc >= 0x3400 && cc < 0x4E00 ) ||
26291 (cc >= 0xf900 && cc < 0xfb00 )
26296 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26297 return "&#" + cc + ";";
26304 if(this.owner.fireEvent('beforesync', this, html) !== false){
26305 this.el.dom.value = html;
26306 this.owner.fireEvent('sync', this, html);
26312 * Protected method that will not generally be called directly. Pushes the value of the textarea
26313 * into the iframe editor.
26315 pushValue : function(){
26316 if(this.initialized){
26317 var v = this.el.dom.value.trim();
26319 // if(v.length < 1){
26323 if(this.owner.fireEvent('beforepush', this, v) !== false){
26324 var d = (this.doc.body || this.doc.documentElement);
26326 this.cleanUpPaste();
26327 this.el.dom.value = d.innerHTML;
26328 this.owner.fireEvent('push', this, v);
26334 deferFocus : function(){
26335 this.focus.defer(10, this);
26339 focus : function(){
26340 if(this.win && !this.sourceEditMode){
26347 assignDocWin: function()
26349 var iframe = this.iframe;
26352 this.doc = iframe.contentWindow.document;
26353 this.win = iframe.contentWindow;
26355 // if (!Roo.get(this.frameId)) {
26358 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26359 // this.win = Roo.get(this.frameId).dom.contentWindow;
26361 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26365 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26366 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26371 initEditor : function(){
26372 //console.log("INIT EDITOR");
26373 this.assignDocWin();
26377 this.doc.designMode="on";
26379 this.doc.write(this.getDocMarkup());
26382 var dbody = (this.doc.body || this.doc.documentElement);
26383 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26384 // this copies styles from the containing element into thsi one..
26385 // not sure why we need all of this..
26386 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26388 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26389 //ss['background-attachment'] = 'fixed'; // w3c
26390 dbody.bgProperties = 'fixed'; // ie
26391 //Roo.DomHelper.applyStyles(dbody, ss);
26392 Roo.EventManager.on(this.doc, {
26393 //'mousedown': this.onEditorEvent,
26394 'mouseup': this.onEditorEvent,
26395 'dblclick': this.onEditorEvent,
26396 'click': this.onEditorEvent,
26397 'keyup': this.onEditorEvent,
26402 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26404 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26405 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26407 this.initialized = true;
26409 this.owner.fireEvent('initialize', this);
26414 onDestroy : function(){
26420 //for (var i =0; i < this.toolbars.length;i++) {
26421 // // fixme - ask toolbars for heights?
26422 // this.toolbars[i].onDestroy();
26425 //this.wrap.dom.innerHTML = '';
26426 //this.wrap.remove();
26431 onFirstFocus : function(){
26433 this.assignDocWin();
26436 this.activated = true;
26439 if(Roo.isGecko){ // prevent silly gecko errors
26441 var s = this.win.getSelection();
26442 if(!s.focusNode || s.focusNode.nodeType != 3){
26443 var r = s.getRangeAt(0);
26444 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26449 this.execCmd('useCSS', true);
26450 this.execCmd('styleWithCSS', false);
26453 this.owner.fireEvent('activate', this);
26457 adjustFont: function(btn){
26458 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26459 //if(Roo.isSafari){ // safari
26462 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26463 if(Roo.isSafari){ // safari
26464 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26465 v = (v < 10) ? 10 : v;
26466 v = (v > 48) ? 48 : v;
26467 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26472 v = Math.max(1, v+adjust);
26474 this.execCmd('FontSize', v );
26477 onEditorEvent : function(e)
26479 this.owner.fireEvent('editorevent', this, e);
26480 // this.updateToolbar();
26481 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26484 insertTag : function(tg)
26486 // could be a bit smarter... -> wrap the current selected tRoo..
26487 if (tg.toLowerCase() == 'span' ||
26488 tg.toLowerCase() == 'code' ||
26489 tg.toLowerCase() == 'sup' ||
26490 tg.toLowerCase() == 'sub'
26493 range = this.createRange(this.getSelection());
26494 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26495 wrappingNode.appendChild(range.extractContents());
26496 range.insertNode(wrappingNode);
26503 this.execCmd("formatblock", tg);
26507 insertText : function(txt)
26511 var range = this.createRange();
26512 range.deleteContents();
26513 //alert(Sender.getAttribute('label'));
26515 range.insertNode(this.doc.createTextNode(txt));
26521 * Executes a Midas editor command on the editor document and performs necessary focus and
26522 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26523 * @param {String} cmd The Midas command
26524 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26526 relayCmd : function(cmd, value){
26528 this.execCmd(cmd, value);
26529 this.owner.fireEvent('editorevent', this);
26530 //this.updateToolbar();
26531 this.owner.deferFocus();
26535 * Executes a Midas editor command directly on the editor document.
26536 * For visual commands, you should use {@link #relayCmd} instead.
26537 * <b>This should only be called after the editor is initialized.</b>
26538 * @param {String} cmd The Midas command
26539 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26541 execCmd : function(cmd, value){
26542 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26549 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26551 * @param {String} text | dom node..
26553 insertAtCursor : function(text)
26556 if(!this.activated){
26562 var r = this.doc.selection.createRange();
26573 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26577 // from jquery ui (MIT licenced)
26579 var win = this.win;
26581 if (win.getSelection && win.getSelection().getRangeAt) {
26582 range = win.getSelection().getRangeAt(0);
26583 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26584 range.insertNode(node);
26585 } else if (win.document.selection && win.document.selection.createRange) {
26586 // no firefox support
26587 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26588 win.document.selection.createRange().pasteHTML(txt);
26590 // no firefox support
26591 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26592 this.execCmd('InsertHTML', txt);
26601 mozKeyPress : function(e){
26603 var c = e.getCharCode(), cmd;
26606 c = String.fromCharCode(c).toLowerCase();
26620 this.cleanUpPaste.defer(100, this);
26628 e.preventDefault();
26636 fixKeys : function(){ // load time branching for fastest keydown performance
26638 return function(e){
26639 var k = e.getKey(), r;
26642 r = this.doc.selection.createRange();
26645 r.pasteHTML('    ');
26652 r = this.doc.selection.createRange();
26654 var target = r.parentElement();
26655 if(!target || target.tagName.toLowerCase() != 'li'){
26657 r.pasteHTML('<br />');
26663 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26664 this.cleanUpPaste.defer(100, this);
26670 }else if(Roo.isOpera){
26671 return function(e){
26672 var k = e.getKey();
26676 this.execCmd('InsertHTML','    ');
26679 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26680 this.cleanUpPaste.defer(100, this);
26685 }else if(Roo.isSafari){
26686 return function(e){
26687 var k = e.getKey();
26691 this.execCmd('InsertText','\t');
26695 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26696 this.cleanUpPaste.defer(100, this);
26704 getAllAncestors: function()
26706 var p = this.getSelectedNode();
26709 a.push(p); // push blank onto stack..
26710 p = this.getParentElement();
26714 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26718 a.push(this.doc.body);
26722 lastSelNode : false,
26725 getSelection : function()
26727 this.assignDocWin();
26728 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26731 getSelectedNode: function()
26733 // this may only work on Gecko!!!
26735 // should we cache this!!!!
26740 var range = this.createRange(this.getSelection()).cloneRange();
26743 var parent = range.parentElement();
26745 var testRange = range.duplicate();
26746 testRange.moveToElementText(parent);
26747 if (testRange.inRange(range)) {
26750 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26753 parent = parent.parentElement;
26758 // is ancestor a text element.
26759 var ac = range.commonAncestorContainer;
26760 if (ac.nodeType == 3) {
26761 ac = ac.parentNode;
26764 var ar = ac.childNodes;
26767 var other_nodes = [];
26768 var has_other_nodes = false;
26769 for (var i=0;i<ar.length;i++) {
26770 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26773 // fullly contained node.
26775 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26780 // probably selected..
26781 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26782 other_nodes.push(ar[i]);
26786 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26791 has_other_nodes = true;
26793 if (!nodes.length && other_nodes.length) {
26794 nodes= other_nodes;
26796 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26802 createRange: function(sel)
26804 // this has strange effects when using with
26805 // top toolbar - not sure if it's a great idea.
26806 //this.editor.contentWindow.focus();
26807 if (typeof sel != "undefined") {
26809 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26811 return this.doc.createRange();
26814 return this.doc.createRange();
26817 getParentElement: function()
26820 this.assignDocWin();
26821 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26823 var range = this.createRange(sel);
26826 var p = range.commonAncestorContainer;
26827 while (p.nodeType == 3) { // text node
26838 * Range intersection.. the hard stuff...
26842 * [ -- selected range --- ]
26846 * if end is before start or hits it. fail.
26847 * if start is after end or hits it fail.
26849 * if either hits (but other is outside. - then it's not
26855 // @see http://www.thismuchiknow.co.uk/?p=64.
26856 rangeIntersectsNode : function(range, node)
26858 var nodeRange = node.ownerDocument.createRange();
26860 nodeRange.selectNode(node);
26862 nodeRange.selectNodeContents(node);
26865 var rangeStartRange = range.cloneRange();
26866 rangeStartRange.collapse(true);
26868 var rangeEndRange = range.cloneRange();
26869 rangeEndRange.collapse(false);
26871 var nodeStartRange = nodeRange.cloneRange();
26872 nodeStartRange.collapse(true);
26874 var nodeEndRange = nodeRange.cloneRange();
26875 nodeEndRange.collapse(false);
26877 return rangeStartRange.compareBoundaryPoints(
26878 Range.START_TO_START, nodeEndRange) == -1 &&
26879 rangeEndRange.compareBoundaryPoints(
26880 Range.START_TO_START, nodeStartRange) == 1;
26884 rangeCompareNode : function(range, node)
26886 var nodeRange = node.ownerDocument.createRange();
26888 nodeRange.selectNode(node);
26890 nodeRange.selectNodeContents(node);
26894 range.collapse(true);
26896 nodeRange.collapse(true);
26898 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26899 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26901 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26903 var nodeIsBefore = ss == 1;
26904 var nodeIsAfter = ee == -1;
26906 if (nodeIsBefore && nodeIsAfter) {
26909 if (!nodeIsBefore && nodeIsAfter) {
26910 return 1; //right trailed.
26913 if (nodeIsBefore && !nodeIsAfter) {
26914 return 2; // left trailed.
26920 // private? - in a new class?
26921 cleanUpPaste : function()
26923 // cleans up the whole document..
26924 Roo.log('cleanuppaste');
26926 this.cleanUpChildren(this.doc.body);
26927 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26928 if (clean != this.doc.body.innerHTML) {
26929 this.doc.body.innerHTML = clean;
26934 cleanWordChars : function(input) {// change the chars to hex code
26935 var he = Roo.HtmlEditorCore;
26937 var output = input;
26938 Roo.each(he.swapCodes, function(sw) {
26939 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26941 output = output.replace(swapper, sw[1]);
26948 cleanUpChildren : function (n)
26950 if (!n.childNodes.length) {
26953 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26954 this.cleanUpChild(n.childNodes[i]);
26961 cleanUpChild : function (node)
26964 //console.log(node);
26965 if (node.nodeName == "#text") {
26966 // clean up silly Windows -- stuff?
26969 if (node.nodeName == "#comment") {
26970 if (!this.allowComments) {
26971 node.parentNode.removeChild(node);
26973 // clean up silly Windows -- stuff?
26976 var lcname = node.tagName.toLowerCase();
26977 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26978 // whitelist of tags..
26980 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26982 node.parentNode.removeChild(node);
26987 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26989 // spans with no attributes - just remove them..
26990 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26991 remove_keep_children = true;
26994 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26995 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26997 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26998 // remove_keep_children = true;
27001 if (remove_keep_children) {
27002 this.cleanUpChildren(node);
27003 // inserts everything just before this node...
27004 while (node.childNodes.length) {
27005 var cn = node.childNodes[0];
27006 node.removeChild(cn);
27007 node.parentNode.insertBefore(cn, node);
27009 node.parentNode.removeChild(node);
27013 if (!node.attributes || !node.attributes.length) {
27018 this.cleanUpChildren(node);
27022 function cleanAttr(n,v)
27025 if (v.match(/^\./) || v.match(/^\//)) {
27028 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27031 if (v.match(/^#/)) {
27034 if (v.match(/^\{/)) { // allow template editing.
27037 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27038 node.removeAttribute(n);
27042 var cwhite = this.cwhite;
27043 var cblack = this.cblack;
27045 function cleanStyle(n,v)
27047 if (v.match(/expression/)) { //XSS?? should we even bother..
27048 node.removeAttribute(n);
27052 var parts = v.split(/;/);
27055 Roo.each(parts, function(p) {
27056 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27060 var l = p.split(':').shift().replace(/\s+/g,'');
27061 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27063 if ( cwhite.length && cblack.indexOf(l) > -1) {
27064 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27065 //node.removeAttribute(n);
27069 // only allow 'c whitelisted system attributes'
27070 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27071 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27072 //node.removeAttribute(n);
27082 if (clean.length) {
27083 node.setAttribute(n, clean.join(';'));
27085 node.removeAttribute(n);
27091 for (var i = node.attributes.length-1; i > -1 ; i--) {
27092 var a = node.attributes[i];
27095 if (a.name.toLowerCase().substr(0,2)=='on') {
27096 node.removeAttribute(a.name);
27099 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27100 node.removeAttribute(a.name);
27103 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27104 cleanAttr(a.name,a.value); // fixme..
27107 if (a.name == 'style') {
27108 cleanStyle(a.name,a.value);
27111 /// clean up MS crap..
27112 // tecnically this should be a list of valid class'es..
27115 if (a.name == 'class') {
27116 if (a.value.match(/^Mso/)) {
27117 node.removeAttribute('class');
27120 if (a.value.match(/^body$/)) {
27121 node.removeAttribute('class');
27132 this.cleanUpChildren(node);
27138 * Clean up MS wordisms...
27140 cleanWord : function(node)
27143 this.cleanWord(this.doc.body);
27148 node.nodeName == 'SPAN' &&
27149 !node.hasAttributes() &&
27150 node.childNodes.length == 1 &&
27151 node.firstChild.nodeName == "#text"
27153 var textNode = node.firstChild;
27154 node.removeChild(textNode);
27155 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27156 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27158 node.parentNode.insertBefore(textNode, node);
27159 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27160 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27162 node.parentNode.removeChild(node);
27165 if (node.nodeName == "#text") {
27166 // clean up silly Windows -- stuff?
27169 if (node.nodeName == "#comment") {
27170 node.parentNode.removeChild(node);
27171 // clean up silly Windows -- stuff?
27175 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27176 node.parentNode.removeChild(node);
27179 //Roo.log(node.tagName);
27180 // remove - but keep children..
27181 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27182 //Roo.log('-- removed');
27183 while (node.childNodes.length) {
27184 var cn = node.childNodes[0];
27185 node.removeChild(cn);
27186 node.parentNode.insertBefore(cn, node);
27187 // move node to parent - and clean it..
27188 this.cleanWord(cn);
27190 node.parentNode.removeChild(node);
27191 /// no need to iterate chidlren = it's got none..
27192 //this.iterateChildren(node, this.cleanWord);
27196 if (node.className.length) {
27198 var cn = node.className.split(/\W+/);
27200 Roo.each(cn, function(cls) {
27201 if (cls.match(/Mso[a-zA-Z]+/)) {
27206 node.className = cna.length ? cna.join(' ') : '';
27208 node.removeAttribute("class");
27212 if (node.hasAttribute("lang")) {
27213 node.removeAttribute("lang");
27216 if (node.hasAttribute("style")) {
27218 var styles = node.getAttribute("style").split(";");
27220 Roo.each(styles, function(s) {
27221 if (!s.match(/:/)) {
27224 var kv = s.split(":");
27225 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27228 // what ever is left... we allow.
27231 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27232 if (!nstyle.length) {
27233 node.removeAttribute('style');
27236 this.iterateChildren(node, this.cleanWord);
27242 * iterateChildren of a Node, calling fn each time, using this as the scole..
27243 * @param {DomNode} node node to iterate children of.
27244 * @param {Function} fn method of this class to call on each item.
27246 iterateChildren : function(node, fn)
27248 if (!node.childNodes.length) {
27251 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27252 fn.call(this, node.childNodes[i])
27258 * cleanTableWidths.
27260 * Quite often pasting from word etc.. results in tables with column and widths.
27261 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27264 cleanTableWidths : function(node)
27269 this.cleanTableWidths(this.doc.body);
27274 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27277 Roo.log(node.tagName);
27278 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27279 this.iterateChildren(node, this.cleanTableWidths);
27282 if (node.hasAttribute('width')) {
27283 node.removeAttribute('width');
27287 if (node.hasAttribute("style")) {
27290 var styles = node.getAttribute("style").split(";");
27292 Roo.each(styles, function(s) {
27293 if (!s.match(/:/)) {
27296 var kv = s.split(":");
27297 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27300 // what ever is left... we allow.
27303 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27304 if (!nstyle.length) {
27305 node.removeAttribute('style');
27309 this.iterateChildren(node, this.cleanTableWidths);
27317 domToHTML : function(currentElement, depth, nopadtext) {
27319 depth = depth || 0;
27320 nopadtext = nopadtext || false;
27322 if (!currentElement) {
27323 return this.domToHTML(this.doc.body);
27326 //Roo.log(currentElement);
27328 var allText = false;
27329 var nodeName = currentElement.nodeName;
27330 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27332 if (nodeName == '#text') {
27334 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27339 if (nodeName != 'BODY') {
27342 // Prints the node tagName, such as <A>, <IMG>, etc
27345 for(i = 0; i < currentElement.attributes.length;i++) {
27347 var aname = currentElement.attributes.item(i).name;
27348 if (!currentElement.attributes.item(i).value.length) {
27351 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27354 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27363 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27366 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27371 // Traverse the tree
27373 var currentElementChild = currentElement.childNodes.item(i);
27374 var allText = true;
27375 var innerHTML = '';
27377 while (currentElementChild) {
27378 // Formatting code (indent the tree so it looks nice on the screen)
27379 var nopad = nopadtext;
27380 if (lastnode == 'SPAN') {
27384 if (currentElementChild.nodeName == '#text') {
27385 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27386 toadd = nopadtext ? toadd : toadd.trim();
27387 if (!nopad && toadd.length > 80) {
27388 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27390 innerHTML += toadd;
27393 currentElementChild = currentElement.childNodes.item(i);
27399 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27401 // Recursively traverse the tree structure of the child node
27402 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27403 lastnode = currentElementChild.nodeName;
27405 currentElementChild=currentElement.childNodes.item(i);
27411 // The remaining code is mostly for formatting the tree
27412 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27417 ret+= "</"+tagName+">";
27423 applyBlacklists : function()
27425 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27426 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27430 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27431 if (b.indexOf(tag) > -1) {
27434 this.white.push(tag);
27438 Roo.each(w, function(tag) {
27439 if (b.indexOf(tag) > -1) {
27442 if (this.white.indexOf(tag) > -1) {
27445 this.white.push(tag);
27450 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27451 if (w.indexOf(tag) > -1) {
27454 this.black.push(tag);
27458 Roo.each(b, function(tag) {
27459 if (w.indexOf(tag) > -1) {
27462 if (this.black.indexOf(tag) > -1) {
27465 this.black.push(tag);
27470 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27471 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27475 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27476 if (b.indexOf(tag) > -1) {
27479 this.cwhite.push(tag);
27483 Roo.each(w, function(tag) {
27484 if (b.indexOf(tag) > -1) {
27487 if (this.cwhite.indexOf(tag) > -1) {
27490 this.cwhite.push(tag);
27495 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27496 if (w.indexOf(tag) > -1) {
27499 this.cblack.push(tag);
27503 Roo.each(b, function(tag) {
27504 if (w.indexOf(tag) > -1) {
27507 if (this.cblack.indexOf(tag) > -1) {
27510 this.cblack.push(tag);
27515 setStylesheets : function(stylesheets)
27517 if(typeof(stylesheets) == 'string'){
27518 Roo.get(this.iframe.contentDocument.head).createChild({
27520 rel : 'stylesheet',
27529 Roo.each(stylesheets, function(s) {
27534 Roo.get(_this.iframe.contentDocument.head).createChild({
27536 rel : 'stylesheet',
27545 removeStylesheets : function()
27549 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27554 setStyle : function(style)
27556 Roo.get(this.iframe.contentDocument.head).createChild({
27565 // hide stuff that is not compatible
27579 * @event specialkey
27583 * @cfg {String} fieldClass @hide
27586 * @cfg {String} focusClass @hide
27589 * @cfg {String} autoCreate @hide
27592 * @cfg {String} inputType @hide
27595 * @cfg {String} invalidClass @hide
27598 * @cfg {String} invalidText @hide
27601 * @cfg {String} msgFx @hide
27604 * @cfg {String} validateOnBlur @hide
27608 Roo.HtmlEditorCore.white = [
27609 'area', 'br', 'img', 'input', 'hr', 'wbr',
27611 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27612 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27613 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27614 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27615 'table', 'ul', 'xmp',
27617 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27620 'dir', 'menu', 'ol', 'ul', 'dl',
27626 Roo.HtmlEditorCore.black = [
27627 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27629 'base', 'basefont', 'bgsound', 'blink', 'body',
27630 'frame', 'frameset', 'head', 'html', 'ilayer',
27631 'iframe', 'layer', 'link', 'meta', 'object',
27632 'script', 'style' ,'title', 'xml' // clean later..
27634 Roo.HtmlEditorCore.clean = [
27635 'script', 'style', 'title', 'xml'
27637 Roo.HtmlEditorCore.remove = [
27642 Roo.HtmlEditorCore.ablack = [
27646 Roo.HtmlEditorCore.aclean = [
27647 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27651 Roo.HtmlEditorCore.pwhite= [
27652 'http', 'https', 'mailto'
27655 // white listed style attributes.
27656 Roo.HtmlEditorCore.cwhite= [
27657 // 'text-align', /// default is to allow most things..
27663 // black listed style attributes.
27664 Roo.HtmlEditorCore.cblack= [
27665 // 'font-size' -- this can be set by the project
27669 Roo.HtmlEditorCore.swapCodes =[
27670 [ 8211, "–" ],
27671 [ 8212, "—" ],
27688 * @class Roo.bootstrap.form.HtmlEditor
27689 * @extends Roo.bootstrap.form.TextArea
27690 * Bootstrap HtmlEditor class
27693 * Create a new HtmlEditor
27694 * @param {Object} config The config object
27697 Roo.bootstrap.form.HtmlEditor = function(config){
27698 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27699 if (!this.toolbars) {
27700 this.toolbars = [];
27703 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27706 * @event initialize
27707 * Fires when the editor is fully initialized (including the iframe)
27708 * @param {HtmlEditor} this
27713 * Fires when the editor is first receives the focus. Any insertion must wait
27714 * until after this event.
27715 * @param {HtmlEditor} this
27719 * @event beforesync
27720 * Fires before the textarea is updated with content from the editor iframe. Return false
27721 * to cancel the sync.
27722 * @param {HtmlEditor} this
27723 * @param {String} html
27727 * @event beforepush
27728 * Fires before the iframe editor is updated with content from the textarea. Return false
27729 * to cancel the push.
27730 * @param {HtmlEditor} this
27731 * @param {String} html
27736 * Fires when the textarea is updated with content from the editor iframe.
27737 * @param {HtmlEditor} this
27738 * @param {String} html
27743 * Fires when the iframe editor is updated with content from the textarea.
27744 * @param {HtmlEditor} this
27745 * @param {String} html
27749 * @event editmodechange
27750 * Fires when the editor switches edit modes
27751 * @param {HtmlEditor} this
27752 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27754 editmodechange: true,
27756 * @event editorevent
27757 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27758 * @param {HtmlEditor} this
27762 * @event firstfocus
27763 * Fires when on first focus - needed by toolbars..
27764 * @param {HtmlEditor} this
27769 * Auto save the htmlEditor value as a file into Events
27770 * @param {HtmlEditor} this
27774 * @event savedpreview
27775 * preview the saved version of htmlEditor
27776 * @param {HtmlEditor} this
27783 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27787 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27792 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27797 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27802 * @cfg {Number} height (in pixels)
27806 * @cfg {Number} width (in pixels)
27811 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27814 stylesheets: false,
27819 // private properties
27820 validationEvent : false,
27822 initialized : false,
27825 onFocus : Roo.emptyFn,
27827 hideMode:'offsets',
27829 tbContainer : false,
27833 toolbarContainer :function() {
27834 return this.wrap.select('.x-html-editor-tb',true).first();
27838 * Protected method that will not generally be called directly. It
27839 * is called when the editor creates its toolbar. Override this method if you need to
27840 * add custom toolbar buttons.
27841 * @param {HtmlEditor} editor
27843 createToolbar : function(){
27844 Roo.log('renewing');
27845 Roo.log("create toolbars");
27847 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27848 this.toolbars[0].render(this.toolbarContainer());
27852 // if (!editor.toolbars || !editor.toolbars.length) {
27853 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27856 // for (var i =0 ; i < editor.toolbars.length;i++) {
27857 // editor.toolbars[i] = Roo.factory(
27858 // typeof(editor.toolbars[i]) == 'string' ?
27859 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27860 // Roo.bootstrap.form.HtmlEditor);
27861 // editor.toolbars[i].init(editor);
27867 onRender : function(ct, position)
27869 // Roo.log("Call onRender: " + this.xtype);
27871 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27873 this.wrap = this.inputEl().wrap({
27874 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27877 this.editorcore.onRender(ct, position);
27879 if (this.resizable) {
27880 this.resizeEl = new Roo.Resizable(this.wrap, {
27884 minHeight : this.height,
27885 height: this.height,
27886 handles : this.resizable,
27889 resize : function(r, w, h) {
27890 _t.onResize(w,h); // -something
27896 this.createToolbar(this);
27899 if(!this.width && this.resizable){
27900 this.setSize(this.wrap.getSize());
27902 if (this.resizeEl) {
27903 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27904 // should trigger onReize..
27910 onResize : function(w, h)
27912 Roo.log('resize: ' +w + ',' + h );
27913 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27917 if(this.inputEl() ){
27918 if(typeof w == 'number'){
27919 var aw = w - this.wrap.getFrameWidth('lr');
27920 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27923 if(typeof h == 'number'){
27924 var tbh = -11; // fixme it needs to tool bar size!
27925 for (var i =0; i < this.toolbars.length;i++) {
27926 // fixme - ask toolbars for heights?
27927 tbh += this.toolbars[i].el.getHeight();
27928 //if (this.toolbars[i].footer) {
27929 // tbh += this.toolbars[i].footer.el.getHeight();
27937 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27938 ah -= 5; // knock a few pixes off for look..
27939 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27943 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27944 this.editorcore.onResize(ew,eh);
27949 * Toggles the editor between standard and source edit mode.
27950 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27952 toggleSourceEdit : function(sourceEditMode)
27954 this.editorcore.toggleSourceEdit(sourceEditMode);
27956 if(this.editorcore.sourceEditMode){
27957 Roo.log('editor - showing textarea');
27960 // Roo.log(this.syncValue());
27962 this.inputEl().removeClass(['hide', 'x-hidden']);
27963 this.inputEl().dom.removeAttribute('tabIndex');
27964 this.inputEl().focus();
27966 Roo.log('editor - hiding textarea');
27968 // Roo.log(this.pushValue());
27971 this.inputEl().addClass(['hide', 'x-hidden']);
27972 this.inputEl().dom.setAttribute('tabIndex', -1);
27973 //this.deferFocus();
27976 if(this.resizable){
27977 this.setSize(this.wrap.getSize());
27980 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27983 // private (for BoxComponent)
27984 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27986 // private (for BoxComponent)
27987 getResizeEl : function(){
27991 // private (for BoxComponent)
27992 getPositionEl : function(){
27997 initEvents : function(){
27998 this.originalValue = this.getValue();
28002 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28005 // markInvalid : Roo.emptyFn,
28007 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28010 // clearInvalid : Roo.emptyFn,
28012 setValue : function(v){
28013 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28014 this.editorcore.pushValue();
28019 deferFocus : function(){
28020 this.focus.defer(10, this);
28024 focus : function(){
28025 this.editorcore.focus();
28031 onDestroy : function(){
28037 for (var i =0; i < this.toolbars.length;i++) {
28038 // fixme - ask toolbars for heights?
28039 this.toolbars[i].onDestroy();
28042 this.wrap.dom.innerHTML = '';
28043 this.wrap.remove();
28048 onFirstFocus : function(){
28049 //Roo.log("onFirstFocus");
28050 this.editorcore.onFirstFocus();
28051 for (var i =0; i < this.toolbars.length;i++) {
28052 this.toolbars[i].onFirstFocus();
28058 syncValue : function()
28060 this.editorcore.syncValue();
28063 pushValue : function()
28065 this.editorcore.pushValue();
28069 // hide stuff that is not compatible
28083 * @event specialkey
28087 * @cfg {String} fieldClass @hide
28090 * @cfg {String} focusClass @hide
28093 * @cfg {String} autoCreate @hide
28096 * @cfg {String} inputType @hide
28100 * @cfg {String} invalidText @hide
28103 * @cfg {String} msgFx @hide
28106 * @cfg {String} validateOnBlur @hide
28115 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28117 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28118 * @parent Roo.bootstrap.form.HtmlEditor
28119 * @extends Roo.bootstrap.nav.Simplebar
28125 new Roo.bootstrap.form.HtmlEditor({
28128 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28129 disable : { fonts: 1 , format: 1, ..., ... , ...],
28135 * @cfg {Object} disable List of elements to disable..
28136 * @cfg {Array} btns List of additional buttons.
28140 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28143 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28146 Roo.apply(this, config);
28148 // default disabled, based on 'good practice'..
28149 this.disable = this.disable || {};
28150 Roo.applyIf(this.disable, {
28153 specialElements : true
28155 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28157 this.editor = config.editor;
28158 this.editorcore = config.editor.editorcore;
28160 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28162 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28163 // dont call parent... till later.
28165 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28170 editorcore : false,
28175 "h1","h2","h3","h4","h5","h6",
28177 "abbr", "acronym", "address", "cite", "samp", "var",
28181 onRender : function(ct, position)
28183 // Roo.log("Call onRender: " + this.xtype);
28185 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28187 this.el.dom.style.marginBottom = '0';
28189 var editorcore = this.editorcore;
28190 var editor= this.editor;
28193 var btn = function(id,cmd , toggle, handler, html){
28195 var event = toggle ? 'toggle' : 'click';
28200 xns: Roo.bootstrap,
28204 enableToggle:toggle !== false,
28206 pressed : toggle ? false : null,
28209 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28210 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28216 // var cb_box = function...
28221 xns: Roo.bootstrap,
28226 xns: Roo.bootstrap,
28230 Roo.each(this.formats, function(f) {
28231 style.menu.items.push({
28233 xns: Roo.bootstrap,
28234 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28239 editorcore.insertTag(this.tagname);
28246 children.push(style);
28248 btn('bold',false,true);
28249 btn('italic',false,true);
28250 btn('align-left', 'justifyleft',true);
28251 btn('align-center', 'justifycenter',true);
28252 btn('align-right' , 'justifyright',true);
28253 btn('link', false, false, function(btn) {
28254 //Roo.log("create link?");
28255 var url = prompt(this.createLinkText, this.defaultLinkValue);
28256 if(url && url != 'http:/'+'/'){
28257 this.editorcore.relayCmd('createlink', url);
28260 btn('list','insertunorderedlist',true);
28261 btn('pencil', false,true, function(btn){
28263 this.toggleSourceEdit(btn.pressed);
28266 if (this.editor.btns.length > 0) {
28267 for (var i = 0; i<this.editor.btns.length; i++) {
28268 children.push(this.editor.btns[i]);
28276 xns: Roo.bootstrap,
28281 xns: Roo.bootstrap,
28286 cog.menu.items.push({
28288 xns: Roo.bootstrap,
28289 html : Clean styles,
28294 editorcore.insertTag(this.tagname);
28303 this.xtype = 'NavSimplebar';
28305 for(var i=0;i< children.length;i++) {
28307 this.buttons.add(this.addxtypeChild(children[i]));
28311 editor.on('editorevent', this.updateToolbar, this);
28313 onBtnClick : function(id)
28315 this.editorcore.relayCmd(id);
28316 this.editorcore.focus();
28320 * Protected method that will not generally be called directly. It triggers
28321 * a toolbar update by reading the markup state of the current selection in the editor.
28323 updateToolbar: function(){
28325 if(!this.editorcore.activated){
28326 this.editor.onFirstFocus(); // is this neeed?
28330 var btns = this.buttons;
28331 var doc = this.editorcore.doc;
28332 btns.get('bold').setActive(doc.queryCommandState('bold'));
28333 btns.get('italic').setActive(doc.queryCommandState('italic'));
28334 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28336 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28337 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28338 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28340 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28341 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28344 var ans = this.editorcore.getAllAncestors();
28345 if (this.formatCombo) {
28348 var store = this.formatCombo.store;
28349 this.formatCombo.setValue("");
28350 for (var i =0; i < ans.length;i++) {
28351 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28353 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28361 // hides menus... - so this cant be on a menu...
28362 Roo.bootstrap.MenuMgr.hideAll();
28364 Roo.bootstrap.menu.Manager.hideAll();
28365 //this.editorsyncValue();
28367 onFirstFocus: function() {
28368 this.buttons.each(function(item){
28372 toggleSourceEdit : function(sourceEditMode){
28375 if(sourceEditMode){
28376 Roo.log("disabling buttons");
28377 this.buttons.each( function(item){
28378 if(item.cmd != 'pencil'){
28384 Roo.log("enabling buttons");
28385 if(this.editorcore.initialized){
28386 this.buttons.each( function(item){
28392 Roo.log("calling toggole on editor");
28393 // tell the editor that it's been pressed..
28394 this.editor.toggleSourceEdit(sourceEditMode);
28408 * @class Roo.bootstrap.form.Markdown
28409 * @extends Roo.bootstrap.form.TextArea
28410 * Bootstrap Showdown editable area
28411 * @cfg {string} content
28414 * Create a new Showdown
28417 Roo.bootstrap.form.Markdown = function(config){
28418 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28422 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28426 initEvents : function()
28429 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28430 this.markdownEl = this.el.createChild({
28431 cls : 'roo-markdown-area'
28433 this.inputEl().addClass('d-none');
28434 if (this.getValue() == '') {
28435 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28438 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28440 this.markdownEl.on('click', this.toggleTextEdit, this);
28441 this.on('blur', this.toggleTextEdit, this);
28442 this.on('specialkey', this.resizeTextArea, this);
28445 toggleTextEdit : function()
28447 var sh = this.markdownEl.getHeight();
28448 this.inputEl().addClass('d-none');
28449 this.markdownEl.addClass('d-none');
28450 if (!this.editing) {
28452 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28453 this.inputEl().removeClass('d-none');
28454 this.inputEl().focus();
28455 this.editing = true;
28458 // show showdown...
28459 this.updateMarkdown();
28460 this.markdownEl.removeClass('d-none');
28461 this.editing = false;
28464 updateMarkdown : function()
28466 if (this.getValue() == '') {
28467 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28471 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28474 resizeTextArea: function () {
28477 Roo.log([sh, this.getValue().split("\n").length * 30]);
28478 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28480 setValue : function(val)
28482 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28483 if (!this.editing) {
28484 this.updateMarkdown();
28490 if (!this.editing) {
28491 this.toggleTextEdit();
28499 * Ext JS Library 1.1.1
28500 * Copyright(c) 2006-2007, Ext JS, LLC.
28502 * Originally Released Under LGPL - original licence link has changed is not relivant.
28505 * <script type="text/javascript">
28509 * @class Roo.bootstrap.PagingToolbar
28510 * @extends Roo.bootstrap.nav.Simplebar
28511 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28513 * Create a new PagingToolbar
28514 * @param {Object} config The config object
28515 * @param {Roo.data.Store} store
28517 Roo.bootstrap.PagingToolbar = function(config)
28519 // old args format still supported... - xtype is prefered..
28520 // created from xtype...
28522 this.ds = config.dataSource;
28524 if (config.store && !this.ds) {
28525 this.store= Roo.factory(config.store, Roo.data);
28526 this.ds = this.store;
28527 this.ds.xmodule = this.xmodule || false;
28530 this.toolbarItems = [];
28531 if (config.items) {
28532 this.toolbarItems = config.items;
28535 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28540 this.bind(this.ds);
28543 if (Roo.bootstrap.version == 4) {
28544 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28546 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28551 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28553 * @cfg {Roo.bootstrap.Button} buttons[]
28554 * Buttons for the toolbar
28557 * @cfg {Roo.data.Store} store
28558 * The underlying data store providing the paged data
28561 * @cfg {String/HTMLElement/Element} container
28562 * container The id or element that will contain the toolbar
28565 * @cfg {Boolean} displayInfo
28566 * True to display the displayMsg (defaults to false)
28569 * @cfg {Number} pageSize
28570 * The number of records to display per page (defaults to 20)
28574 * @cfg {String} displayMsg
28575 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28577 displayMsg : 'Displaying {0} - {1} of {2}',
28579 * @cfg {String} emptyMsg
28580 * The message to display when no records are found (defaults to "No data to display")
28582 emptyMsg : 'No data to display',
28584 * Customizable piece of the default paging text (defaults to "Page")
28587 beforePageText : "Page",
28589 * Customizable piece of the default paging text (defaults to "of %0")
28592 afterPageText : "of {0}",
28594 * Customizable piece of the default paging text (defaults to "First Page")
28597 firstText : "First Page",
28599 * Customizable piece of the default paging text (defaults to "Previous Page")
28602 prevText : "Previous Page",
28604 * Customizable piece of the default paging text (defaults to "Next Page")
28607 nextText : "Next Page",
28609 * Customizable piece of the default paging text (defaults to "Last Page")
28612 lastText : "Last Page",
28614 * Customizable piece of the default paging text (defaults to "Refresh")
28617 refreshText : "Refresh",
28621 onRender : function(ct, position)
28623 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28624 this.navgroup.parentId = this.id;
28625 this.navgroup.onRender(this.el, null);
28626 // add the buttons to the navgroup
28628 if(this.displayInfo){
28629 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28630 this.displayEl = this.el.select('.x-paging-info', true).first();
28631 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28632 // this.displayEl = navel.el.select('span',true).first();
28638 Roo.each(_this.buttons, function(e){ // this might need to use render????
28639 Roo.factory(e).render(_this.el);
28643 Roo.each(_this.toolbarItems, function(e) {
28644 _this.navgroup.addItem(e);
28648 this.first = this.navgroup.addItem({
28649 tooltip: this.firstText,
28650 cls: "prev btn-outline-secondary",
28651 html : ' <i class="fa fa-step-backward"></i>',
28653 preventDefault: true,
28654 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28657 this.prev = this.navgroup.addItem({
28658 tooltip: this.prevText,
28659 cls: "prev btn-outline-secondary",
28660 html : ' <i class="fa fa-backward"></i>',
28662 preventDefault: true,
28663 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28665 //this.addSeparator();
28668 var field = this.navgroup.addItem( {
28670 cls : 'x-paging-position btn-outline-secondary',
28672 html : this.beforePageText +
28673 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28674 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28677 this.field = field.el.select('input', true).first();
28678 this.field.on("keydown", this.onPagingKeydown, this);
28679 this.field.on("focus", function(){this.dom.select();});
28682 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28683 //this.field.setHeight(18);
28684 //this.addSeparator();
28685 this.next = this.navgroup.addItem({
28686 tooltip: this.nextText,
28687 cls: "next btn-outline-secondary",
28688 html : ' <i class="fa fa-forward"></i>',
28690 preventDefault: true,
28691 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28693 this.last = this.navgroup.addItem({
28694 tooltip: this.lastText,
28695 html : ' <i class="fa fa-step-forward"></i>',
28696 cls: "next btn-outline-secondary",
28698 preventDefault: true,
28699 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28701 //this.addSeparator();
28702 this.loading = this.navgroup.addItem({
28703 tooltip: this.refreshText,
28704 cls: "btn-outline-secondary",
28705 html : ' <i class="fa fa-refresh"></i>',
28706 preventDefault: true,
28707 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28713 updateInfo : function(){
28714 if(this.displayEl){
28715 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28716 var msg = count == 0 ?
28720 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28722 this.displayEl.update(msg);
28727 onLoad : function(ds, r, o)
28729 this.cursor = o.params && o.params.start ? o.params.start : 0;
28731 var d = this.getPageData(),
28736 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28737 this.field.dom.value = ap;
28738 this.first.setDisabled(ap == 1);
28739 this.prev.setDisabled(ap == 1);
28740 this.next.setDisabled(ap == ps);
28741 this.last.setDisabled(ap == ps);
28742 this.loading.enable();
28747 getPageData : function(){
28748 var total = this.ds.getTotalCount();
28751 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28752 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28757 onLoadError : function(){
28758 this.loading.enable();
28762 onPagingKeydown : function(e){
28763 var k = e.getKey();
28764 var d = this.getPageData();
28766 var v = this.field.dom.value, pageNum;
28767 if(!v || isNaN(pageNum = parseInt(v, 10))){
28768 this.field.dom.value = d.activePage;
28771 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28772 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28775 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))
28777 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28778 this.field.dom.value = pageNum;
28779 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28782 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28784 var v = this.field.dom.value, pageNum;
28785 var increment = (e.shiftKey) ? 10 : 1;
28786 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28789 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28790 this.field.dom.value = d.activePage;
28793 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28795 this.field.dom.value = parseInt(v, 10) + increment;
28796 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28797 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28804 beforeLoad : function(){
28806 this.loading.disable();
28811 onClick : function(which){
28820 ds.load({params:{start: 0, limit: this.pageSize}});
28823 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28826 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28829 var total = ds.getTotalCount();
28830 var extra = total % this.pageSize;
28831 var lastStart = extra ? (total - extra) : total-this.pageSize;
28832 ds.load({params:{start: lastStart, limit: this.pageSize}});
28835 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28841 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28842 * @param {Roo.data.Store} store The data store to unbind
28844 unbind : function(ds){
28845 ds.un("beforeload", this.beforeLoad, this);
28846 ds.un("load", this.onLoad, this);
28847 ds.un("loadexception", this.onLoadError, this);
28848 ds.un("remove", this.updateInfo, this);
28849 ds.un("add", this.updateInfo, this);
28850 this.ds = undefined;
28854 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28855 * @param {Roo.data.Store} store The data store to bind
28857 bind : function(ds){
28858 ds.on("beforeload", this.beforeLoad, this);
28859 ds.on("load", this.onLoad, this);
28860 ds.on("loadexception", this.onLoadError, this);
28861 ds.on("remove", this.updateInfo, this);
28862 ds.on("add", this.updateInfo, this);
28873 * @class Roo.bootstrap.MessageBar
28874 * @extends Roo.bootstrap.Component
28875 * Bootstrap MessageBar class
28876 * @cfg {String} html contents of the MessageBar
28877 * @cfg {String} weight (info | success | warning | danger) default info
28878 * @cfg {String} beforeClass insert the bar before the given class
28879 * @cfg {Boolean} closable (true | false) default false
28880 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28883 * Create a new Element
28884 * @param {Object} config The config object
28887 Roo.bootstrap.MessageBar = function(config){
28888 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28891 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28897 beforeClass: 'bootstrap-sticky-wrap',
28899 getAutoCreate : function(){
28903 cls: 'alert alert-dismissable alert-' + this.weight,
28908 html: this.html || ''
28914 cfg.cls += ' alert-messages-fixed';
28928 onRender : function(ct, position)
28930 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28933 var cfg = Roo.apply({}, this.getAutoCreate());
28937 cfg.cls += ' ' + this.cls;
28940 cfg.style = this.style;
28942 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28944 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28947 this.el.select('>button.close').on('click', this.hide, this);
28953 if (!this.rendered) {
28959 this.fireEvent('show', this);
28965 if (!this.rendered) {
28971 this.fireEvent('hide', this);
28974 update : function()
28976 // var e = this.el.dom.firstChild;
28978 // if(this.closable){
28979 // e = e.nextSibling;
28982 // e.data = this.html || '';
28984 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29000 * @class Roo.bootstrap.Graph
29001 * @extends Roo.bootstrap.Component
29002 * Bootstrap Graph class
29006 @cfg {String} graphtype bar | vbar | pie
29007 @cfg {number} g_x coodinator | centre x (pie)
29008 @cfg {number} g_y coodinator | centre y (pie)
29009 @cfg {number} g_r radius (pie)
29010 @cfg {number} g_height height of the chart (respected by all elements in the set)
29011 @cfg {number} g_width width of the chart (respected by all elements in the set)
29012 @cfg {Object} title The title of the chart
29015 -opts (object) options for the chart
29017 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29018 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29020 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.
29021 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29023 o stretch (boolean)
29025 -opts (object) options for the pie
29028 o startAngle (number)
29029 o endAngle (number)
29033 * Create a new Input
29034 * @param {Object} config The config object
29037 Roo.bootstrap.Graph = function(config){
29038 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29044 * The img click event for the img.
29045 * @param {Roo.EventObject} e
29051 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29062 //g_colors: this.colors,
29069 getAutoCreate : function(){
29080 onRender : function(ct,position){
29083 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29085 if (typeof(Raphael) == 'undefined') {
29086 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29090 this.raphael = Raphael(this.el.dom);
29092 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29093 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29094 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29095 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29097 r.text(160, 10, "Single Series Chart").attr(txtattr);
29098 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29099 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29100 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29102 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29103 r.barchart(330, 10, 300, 220, data1);
29104 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29105 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29108 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29109 // r.barchart(30, 30, 560, 250, xdata, {
29110 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29111 // axis : "0 0 1 1",
29112 // axisxlabels : xdata
29113 // //yvalues : cols,
29116 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29118 // this.load(null,xdata,{
29119 // axis : "0 0 1 1",
29120 // axisxlabels : xdata
29125 load : function(graphtype,xdata,opts)
29127 this.raphael.clear();
29129 graphtype = this.graphtype;
29134 var r = this.raphael,
29135 fin = function () {
29136 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29138 fout = function () {
29139 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29141 pfin = function() {
29142 this.sector.stop();
29143 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29146 this.label[0].stop();
29147 this.label[0].attr({ r: 7.5 });
29148 this.label[1].attr({ "font-weight": 800 });
29151 pfout = function() {
29152 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29155 this.label[0].animate({ r: 5 }, 500, "bounce");
29156 this.label[1].attr({ "font-weight": 400 });
29162 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29165 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29168 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29169 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29171 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29178 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29183 setTitle: function(o)
29188 initEvents: function() {
29191 this.el.on('click', this.onClick, this);
29195 onClick : function(e)
29197 Roo.log('img onclick');
29198 this.fireEvent('click', this, e);
29210 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29213 * @class Roo.bootstrap.dash.NumberBox
29214 * @extends Roo.bootstrap.Component
29215 * Bootstrap NumberBox class
29216 * @cfg {String} headline Box headline
29217 * @cfg {String} content Box content
29218 * @cfg {String} icon Box icon
29219 * @cfg {String} footer Footer text
29220 * @cfg {String} fhref Footer href
29223 * Create a new NumberBox
29224 * @param {Object} config The config object
29228 Roo.bootstrap.dash.NumberBox = function(config){
29229 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29233 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29242 getAutoCreate : function(){
29246 cls : 'small-box ',
29254 cls : 'roo-headline',
29255 html : this.headline
29259 cls : 'roo-content',
29260 html : this.content
29274 cls : 'ion ' + this.icon
29283 cls : 'small-box-footer',
29284 href : this.fhref || '#',
29288 cfg.cn.push(footer);
29295 onRender : function(ct,position){
29296 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29303 setHeadline: function (value)
29305 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29308 setFooter: function (value, href)
29310 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29313 this.el.select('a.small-box-footer',true).first().attr('href', href);
29318 setContent: function (value)
29320 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29323 initEvents: function()
29337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29340 * @class Roo.bootstrap.dash.TabBox
29341 * @extends Roo.bootstrap.Component
29342 * @children Roo.bootstrap.dash.TabPane
29343 * Bootstrap TabBox class
29344 * @cfg {String} title Title of the TabBox
29345 * @cfg {String} icon Icon of the TabBox
29346 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29347 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29350 * Create a new TabBox
29351 * @param {Object} config The config object
29355 Roo.bootstrap.dash.TabBox = function(config){
29356 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29361 * When a pane is added
29362 * @param {Roo.bootstrap.dash.TabPane} pane
29366 * @event activatepane
29367 * When a pane is activated
29368 * @param {Roo.bootstrap.dash.TabPane} pane
29370 "activatepane" : true
29378 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29383 tabScrollable : false,
29385 getChildContainer : function()
29387 return this.el.select('.tab-content', true).first();
29390 getAutoCreate : function(){
29394 cls: 'pull-left header',
29402 cls: 'fa ' + this.icon
29408 cls: 'nav nav-tabs pull-right',
29414 if(this.tabScrollable){
29421 cls: 'nav nav-tabs pull-right',
29432 cls: 'nav-tabs-custom',
29437 cls: 'tab-content no-padding',
29445 initEvents : function()
29447 //Roo.log('add add pane handler');
29448 this.on('addpane', this.onAddPane, this);
29451 * Updates the box title
29452 * @param {String} html to set the title to.
29454 setTitle : function(value)
29456 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29458 onAddPane : function(pane)
29460 this.panes.push(pane);
29461 //Roo.log('addpane');
29463 // tabs are rendere left to right..
29464 if(!this.showtabs){
29468 var ctr = this.el.select('.nav-tabs', true).first();
29471 var existing = ctr.select('.nav-tab',true);
29472 var qty = existing.getCount();;
29475 var tab = ctr.createChild({
29477 cls : 'nav-tab' + (qty ? '' : ' active'),
29485 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29488 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29490 pane.el.addClass('active');
29495 onTabClick : function(ev,un,ob,pane)
29497 //Roo.log('tab - prev default');
29498 ev.preventDefault();
29501 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29502 pane.tab.addClass('active');
29503 //Roo.log(pane.title);
29504 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29505 // technically we should have a deactivate event.. but maybe add later.
29506 // and it should not de-activate the selected tab...
29507 this.fireEvent('activatepane', pane);
29508 pane.el.addClass('active');
29509 pane.fireEvent('activate');
29514 getActivePane : function()
29517 Roo.each(this.panes, function(p) {
29518 if(p.el.hasClass('active')){
29539 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29541 * @class Roo.bootstrap.TabPane
29542 * @extends Roo.bootstrap.Component
29543 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29544 * Bootstrap TabPane class
29545 * @cfg {Boolean} active (false | true) Default false
29546 * @cfg {String} title title of panel
29550 * Create a new TabPane
29551 * @param {Object} config The config object
29554 Roo.bootstrap.dash.TabPane = function(config){
29555 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29561 * When a pane is activated
29562 * @param {Roo.bootstrap.dash.TabPane} pane
29569 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29574 // the tabBox that this is attached to.
29577 getAutoCreate : function()
29585 cfg.cls += ' active';
29590 initEvents : function()
29592 //Roo.log('trigger add pane handler');
29593 this.parent().fireEvent('addpane', this)
29597 * Updates the tab title
29598 * @param {String} html to set the title to.
29600 setTitle: function(str)
29606 this.tab.select('a', true).first().dom.innerHTML = str;
29625 * @class Roo.bootstrap.Tooltip
29626 * Bootstrap Tooltip class
29627 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29628 * to determine which dom element triggers the tooltip.
29630 * It needs to add support for additional attributes like tooltip-position
29633 * Create a new Toolti
29634 * @param {Object} config The config object
29637 Roo.bootstrap.Tooltip = function(config){
29638 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29640 this.alignment = Roo.bootstrap.Tooltip.alignment;
29642 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29643 this.alignment = config.alignment;
29648 Roo.apply(Roo.bootstrap.Tooltip, {
29650 * @function init initialize tooltip monitoring.
29654 currentTip : false,
29655 currentRegion : false,
29661 Roo.get(document).on('mouseover', this.enter ,this);
29662 Roo.get(document).on('mouseout', this.leave, this);
29665 this.currentTip = new Roo.bootstrap.Tooltip();
29668 enter : function(ev)
29670 var dom = ev.getTarget();
29672 //Roo.log(['enter',dom]);
29673 var el = Roo.fly(dom);
29674 if (this.currentEl) {
29676 //Roo.log(this.currentEl);
29677 //Roo.log(this.currentEl.contains(dom));
29678 if (this.currentEl == el) {
29681 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29687 if (this.currentTip.el) {
29688 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29692 if(!el || el.dom == document){
29698 if (!el.attr('tooltip')) {
29699 pel = el.findParent("[tooltip]");
29701 bindEl = Roo.get(pel);
29707 // you can not look for children, as if el is the body.. then everythign is the child..
29708 if (!pel && !el.attr('tooltip')) { //
29709 if (!el.select("[tooltip]").elements.length) {
29712 // is the mouse over this child...?
29713 bindEl = el.select("[tooltip]").first();
29714 var xy = ev.getXY();
29715 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29716 //Roo.log("not in region.");
29719 //Roo.log("child element over..");
29722 this.currentEl = el;
29723 this.currentTip.bind(bindEl);
29724 this.currentRegion = Roo.lib.Region.getRegion(dom);
29725 this.currentTip.enter();
29728 leave : function(ev)
29730 var dom = ev.getTarget();
29731 //Roo.log(['leave',dom]);
29732 if (!this.currentEl) {
29737 if (dom != this.currentEl.dom) {
29740 var xy = ev.getXY();
29741 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29744 // only activate leave if mouse cursor is outside... bounding box..
29749 if (this.currentTip) {
29750 this.currentTip.leave();
29752 //Roo.log('clear currentEl');
29753 this.currentEl = false;
29758 'left' : ['r-l', [-2,0], 'right'],
29759 'right' : ['l-r', [2,0], 'left'],
29760 'bottom' : ['t-b', [0,2], 'top'],
29761 'top' : [ 'b-t', [0,-2], 'bottom']
29767 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29772 delay : null, // can be { show : 300 , hide: 500}
29776 hoverState : null, //???
29778 placement : 'bottom',
29782 getAutoCreate : function(){
29789 cls : 'tooltip-arrow arrow'
29792 cls : 'tooltip-inner'
29799 bind : function(el)
29804 initEvents : function()
29806 this.arrowEl = this.el.select('.arrow', true).first();
29807 this.innerEl = this.el.select('.tooltip-inner', true).first();
29810 enter : function () {
29812 if (this.timeout != null) {
29813 clearTimeout(this.timeout);
29816 this.hoverState = 'in';
29817 //Roo.log("enter - show");
29818 if (!this.delay || !this.delay.show) {
29823 this.timeout = setTimeout(function () {
29824 if (_t.hoverState == 'in') {
29827 }, this.delay.show);
29831 clearTimeout(this.timeout);
29833 this.hoverState = 'out';
29834 if (!this.delay || !this.delay.hide) {
29840 this.timeout = setTimeout(function () {
29841 //Roo.log("leave - timeout");
29843 if (_t.hoverState == 'out') {
29845 Roo.bootstrap.Tooltip.currentEl = false;
29850 show : function (msg)
29853 this.render(document.body);
29856 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29858 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29860 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29862 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29863 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29865 var placement = typeof this.placement == 'function' ?
29866 this.placement.call(this, this.el, on_el) :
29869 var autoToken = /\s?auto?\s?/i;
29870 var autoPlace = autoToken.test(placement);
29872 placement = placement.replace(autoToken, '') || 'top';
29876 //this.el.setXY([0,0]);
29878 //this.el.dom.style.display='block';
29880 //this.el.appendTo(on_el);
29882 var p = this.getPosition();
29883 var box = this.el.getBox();
29889 var align = this.alignment[placement];
29891 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29893 if(placement == 'top' || placement == 'bottom'){
29895 placement = 'right';
29898 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29899 placement = 'left';
29902 var scroll = Roo.select('body', true).first().getScroll();
29904 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29908 align = this.alignment[placement];
29910 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29914 var elems = document.getElementsByTagName('div');
29915 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29916 for (var i = 0; i < elems.length; i++) {
29917 var zindex = Number.parseInt(
29918 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29921 if (zindex > highest) {
29928 this.el.dom.style.zIndex = highest;
29930 this.el.alignTo(this.bindEl, align[0],align[1]);
29931 //var arrow = this.el.select('.arrow',true).first();
29932 //arrow.set(align[2],
29934 this.el.addClass(placement);
29935 this.el.addClass("bs-tooltip-"+ placement);
29937 this.el.addClass('in fade show');
29939 this.hoverState = null;
29941 if (this.el.hasClass('fade')) {
29956 //this.el.setXY([0,0]);
29957 this.el.removeClass(['show', 'in']);
29973 * @class Roo.bootstrap.LocationPicker
29974 * @extends Roo.bootstrap.Component
29975 * Bootstrap LocationPicker class
29976 * @cfg {Number} latitude Position when init default 0
29977 * @cfg {Number} longitude Position when init default 0
29978 * @cfg {Number} zoom default 15
29979 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29980 * @cfg {Boolean} mapTypeControl default false
29981 * @cfg {Boolean} disableDoubleClickZoom default false
29982 * @cfg {Boolean} scrollwheel default true
29983 * @cfg {Boolean} streetViewControl default false
29984 * @cfg {Number} radius default 0
29985 * @cfg {String} locationName
29986 * @cfg {Boolean} draggable default true
29987 * @cfg {Boolean} enableAutocomplete default false
29988 * @cfg {Boolean} enableReverseGeocode default true
29989 * @cfg {String} markerTitle
29992 * Create a new LocationPicker
29993 * @param {Object} config The config object
29997 Roo.bootstrap.LocationPicker = function(config){
29999 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30004 * Fires when the picker initialized.
30005 * @param {Roo.bootstrap.LocationPicker} this
30006 * @param {Google Location} location
30010 * @event positionchanged
30011 * Fires when the picker position changed.
30012 * @param {Roo.bootstrap.LocationPicker} this
30013 * @param {Google Location} location
30015 positionchanged : true,
30018 * Fires when the map resize.
30019 * @param {Roo.bootstrap.LocationPicker} this
30024 * Fires when the map show.
30025 * @param {Roo.bootstrap.LocationPicker} this
30030 * Fires when the map hide.
30031 * @param {Roo.bootstrap.LocationPicker} this
30036 * Fires when click the map.
30037 * @param {Roo.bootstrap.LocationPicker} this
30038 * @param {Map event} e
30042 * @event mapRightClick
30043 * Fires when right click the map.
30044 * @param {Roo.bootstrap.LocationPicker} this
30045 * @param {Map event} e
30047 mapRightClick : true,
30049 * @event markerClick
30050 * Fires when click the marker.
30051 * @param {Roo.bootstrap.LocationPicker} this
30052 * @param {Map event} e
30054 markerClick : true,
30056 * @event markerRightClick
30057 * Fires when right click the marker.
30058 * @param {Roo.bootstrap.LocationPicker} this
30059 * @param {Map event} e
30061 markerRightClick : true,
30063 * @event OverlayViewDraw
30064 * Fires when OverlayView Draw
30065 * @param {Roo.bootstrap.LocationPicker} this
30067 OverlayViewDraw : true,
30069 * @event OverlayViewOnAdd
30070 * Fires when OverlayView Draw
30071 * @param {Roo.bootstrap.LocationPicker} this
30073 OverlayViewOnAdd : true,
30075 * @event OverlayViewOnRemove
30076 * Fires when OverlayView Draw
30077 * @param {Roo.bootstrap.LocationPicker} this
30079 OverlayViewOnRemove : true,
30081 * @event OverlayViewShow
30082 * Fires when OverlayView Draw
30083 * @param {Roo.bootstrap.LocationPicker} this
30084 * @param {Pixel} cpx
30086 OverlayViewShow : true,
30088 * @event OverlayViewHide
30089 * Fires when OverlayView Draw
30090 * @param {Roo.bootstrap.LocationPicker} this
30092 OverlayViewHide : true,
30094 * @event loadexception
30095 * Fires when load google lib failed.
30096 * @param {Roo.bootstrap.LocationPicker} this
30098 loadexception : true
30103 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30105 gMapContext: false,
30111 mapTypeControl: false,
30112 disableDoubleClickZoom: false,
30114 streetViewControl: false,
30118 enableAutocomplete: false,
30119 enableReverseGeocode: true,
30122 getAutoCreate: function()
30127 cls: 'roo-location-picker'
30133 initEvents: function(ct, position)
30135 if(!this.el.getWidth() || this.isApplied()){
30139 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30144 initial: function()
30146 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30147 this.fireEvent('loadexception', this);
30151 if(!this.mapTypeId){
30152 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30155 this.gMapContext = this.GMapContext();
30157 this.initOverlayView();
30159 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30163 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30164 _this.setPosition(_this.gMapContext.marker.position);
30167 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30168 _this.fireEvent('mapClick', this, event);
30172 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30173 _this.fireEvent('mapRightClick', this, event);
30177 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30178 _this.fireEvent('markerClick', this, event);
30182 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30183 _this.fireEvent('markerRightClick', this, event);
30187 this.setPosition(this.gMapContext.location);
30189 this.fireEvent('initial', this, this.gMapContext.location);
30192 initOverlayView: function()
30196 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30200 _this.fireEvent('OverlayViewDraw', _this);
30205 _this.fireEvent('OverlayViewOnAdd', _this);
30208 onRemove: function()
30210 _this.fireEvent('OverlayViewOnRemove', _this);
30213 show: function(cpx)
30215 _this.fireEvent('OverlayViewShow', _this, cpx);
30220 _this.fireEvent('OverlayViewHide', _this);
30226 fromLatLngToContainerPixel: function(event)
30228 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30231 isApplied: function()
30233 return this.getGmapContext() == false ? false : true;
30236 getGmapContext: function()
30238 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30241 GMapContext: function()
30243 var position = new google.maps.LatLng(this.latitude, this.longitude);
30245 var _map = new google.maps.Map(this.el.dom, {
30248 mapTypeId: this.mapTypeId,
30249 mapTypeControl: this.mapTypeControl,
30250 disableDoubleClickZoom: this.disableDoubleClickZoom,
30251 scrollwheel: this.scrollwheel,
30252 streetViewControl: this.streetViewControl,
30253 locationName: this.locationName,
30254 draggable: this.draggable,
30255 enableAutocomplete: this.enableAutocomplete,
30256 enableReverseGeocode: this.enableReverseGeocode
30259 var _marker = new google.maps.Marker({
30260 position: position,
30262 title: this.markerTitle,
30263 draggable: this.draggable
30270 location: position,
30271 radius: this.radius,
30272 locationName: this.locationName,
30273 addressComponents: {
30274 formatted_address: null,
30275 addressLine1: null,
30276 addressLine2: null,
30278 streetNumber: null,
30282 stateOrProvince: null
30285 domContainer: this.el.dom,
30286 geodecoder: new google.maps.Geocoder()
30290 drawCircle: function(center, radius, options)
30292 if (this.gMapContext.circle != null) {
30293 this.gMapContext.circle.setMap(null);
30297 options = Roo.apply({}, options, {
30298 strokeColor: "#0000FF",
30299 strokeOpacity: .35,
30301 fillColor: "#0000FF",
30305 options.map = this.gMapContext.map;
30306 options.radius = radius;
30307 options.center = center;
30308 this.gMapContext.circle = new google.maps.Circle(options);
30309 return this.gMapContext.circle;
30315 setPosition: function(location)
30317 this.gMapContext.location = location;
30318 this.gMapContext.marker.setPosition(location);
30319 this.gMapContext.map.panTo(location);
30320 this.drawCircle(location, this.gMapContext.radius, {});
30324 if (this.gMapContext.settings.enableReverseGeocode) {
30325 this.gMapContext.geodecoder.geocode({
30326 latLng: this.gMapContext.location
30327 }, function(results, status) {
30329 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30330 _this.gMapContext.locationName = results[0].formatted_address;
30331 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30333 _this.fireEvent('positionchanged', this, location);
30340 this.fireEvent('positionchanged', this, location);
30345 google.maps.event.trigger(this.gMapContext.map, "resize");
30347 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30349 this.fireEvent('resize', this);
30352 setPositionByLatLng: function(latitude, longitude)
30354 this.setPosition(new google.maps.LatLng(latitude, longitude));
30357 getCurrentPosition: function()
30360 latitude: this.gMapContext.location.lat(),
30361 longitude: this.gMapContext.location.lng()
30365 getAddressName: function()
30367 return this.gMapContext.locationName;
30370 getAddressComponents: function()
30372 return this.gMapContext.addressComponents;
30375 address_component_from_google_geocode: function(address_components)
30379 for (var i = 0; i < address_components.length; i++) {
30380 var component = address_components[i];
30381 if (component.types.indexOf("postal_code") >= 0) {
30382 result.postalCode = component.short_name;
30383 } else if (component.types.indexOf("street_number") >= 0) {
30384 result.streetNumber = component.short_name;
30385 } else if (component.types.indexOf("route") >= 0) {
30386 result.streetName = component.short_name;
30387 } else if (component.types.indexOf("neighborhood") >= 0) {
30388 result.city = component.short_name;
30389 } else if (component.types.indexOf("locality") >= 0) {
30390 result.city = component.short_name;
30391 } else if (component.types.indexOf("sublocality") >= 0) {
30392 result.district = component.short_name;
30393 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30394 result.stateOrProvince = component.short_name;
30395 } else if (component.types.indexOf("country") >= 0) {
30396 result.country = component.short_name;
30400 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30401 result.addressLine2 = "";
30405 setZoomLevel: function(zoom)
30407 this.gMapContext.map.setZoom(zoom);
30420 this.fireEvent('show', this);
30431 this.fireEvent('hide', this);
30436 Roo.apply(Roo.bootstrap.LocationPicker, {
30438 OverlayView : function(map, options)
30440 options = options || {};
30447 * @class Roo.bootstrap.Alert
30448 * @extends Roo.bootstrap.Component
30449 * Bootstrap Alert class - shows an alert area box
30451 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30452 Enter a valid email address
30455 * @cfg {String} title The title of alert
30456 * @cfg {String} html The content of alert
30457 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30458 * @cfg {String} fa font-awesomeicon
30459 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30460 * @cfg {Boolean} close true to show a x closer
30464 * Create a new alert
30465 * @param {Object} config The config object
30469 Roo.bootstrap.Alert = function(config){
30470 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30474 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30480 faicon: false, // BC
30484 getAutoCreate : function()
30496 style : this.close ? '' : 'display:none'
30500 cls : 'roo-alert-icon'
30505 cls : 'roo-alert-title',
30510 cls : 'roo-alert-text',
30517 cfg.cn[0].cls += ' fa ' + this.faicon;
30520 cfg.cn[0].cls += ' fa ' + this.fa;
30524 cfg.cls += ' alert-' + this.weight;
30530 initEvents: function()
30532 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30533 this.titleEl = this.el.select('.roo-alert-title',true).first();
30534 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30535 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30536 if (this.seconds > 0) {
30537 this.hide.defer(this.seconds, this);
30541 * Set the Title Message HTML
30542 * @param {String} html
30544 setTitle : function(str)
30546 this.titleEl.dom.innerHTML = str;
30550 * Set the Body Message HTML
30551 * @param {String} html
30553 setHtml : function(str)
30555 this.htmlEl.dom.innerHTML = str;
30558 * Set the Weight of the alert
30559 * @param {String} (success|info|warning|danger) weight
30562 setWeight : function(weight)
30565 this.el.removeClass('alert-' + this.weight);
30568 this.weight = weight;
30570 this.el.addClass('alert-' + this.weight);
30573 * Set the Icon of the alert
30574 * @param {String} see fontawsome names (name without the 'fa-' bit)
30576 setIcon : function(icon)
30579 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30582 this.faicon = icon;
30584 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30609 * @class Roo.bootstrap.UploadCropbox
30610 * @extends Roo.bootstrap.Component
30611 * Bootstrap UploadCropbox class
30612 * @cfg {String} emptyText show when image has been loaded
30613 * @cfg {String} rotateNotify show when image too small to rotate
30614 * @cfg {Number} errorTimeout default 3000
30615 * @cfg {Number} minWidth default 300
30616 * @cfg {Number} minHeight default 300
30617 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30618 * @cfg {Boolean} isDocument (true|false) default false
30619 * @cfg {String} url action url
30620 * @cfg {String} paramName default 'imageUpload'
30621 * @cfg {String} method default POST
30622 * @cfg {Boolean} loadMask (true|false) default true
30623 * @cfg {Boolean} loadingText default 'Loading...'
30626 * Create a new UploadCropbox
30627 * @param {Object} config The config object
30630 Roo.bootstrap.UploadCropbox = function(config){
30631 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30635 * @event beforeselectfile
30636 * Fire before select file
30637 * @param {Roo.bootstrap.UploadCropbox} this
30639 "beforeselectfile" : true,
30642 * Fire after initEvent
30643 * @param {Roo.bootstrap.UploadCropbox} this
30648 * Fire after initEvent
30649 * @param {Roo.bootstrap.UploadCropbox} this
30650 * @param {String} data
30655 * Fire when preparing the file data
30656 * @param {Roo.bootstrap.UploadCropbox} this
30657 * @param {Object} file
30662 * Fire when get exception
30663 * @param {Roo.bootstrap.UploadCropbox} this
30664 * @param {XMLHttpRequest} xhr
30666 "exception" : true,
30668 * @event beforeloadcanvas
30669 * Fire before load the canvas
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {String} src
30673 "beforeloadcanvas" : true,
30676 * Fire when trash image
30677 * @param {Roo.bootstrap.UploadCropbox} this
30682 * Fire when download the image
30683 * @param {Roo.bootstrap.UploadCropbox} this
30687 * @event footerbuttonclick
30688 * Fire when footerbuttonclick
30689 * @param {Roo.bootstrap.UploadCropbox} this
30690 * @param {String} type
30692 "footerbuttonclick" : true,
30696 * @param {Roo.bootstrap.UploadCropbox} this
30701 * Fire when rotate the image
30702 * @param {Roo.bootstrap.UploadCropbox} this
30703 * @param {String} pos
30708 * Fire when inspect the file
30709 * @param {Roo.bootstrap.UploadCropbox} this
30710 * @param {Object} file
30715 * Fire when xhr upload the file
30716 * @param {Roo.bootstrap.UploadCropbox} this
30717 * @param {Object} data
30722 * Fire when arrange the file data
30723 * @param {Roo.bootstrap.UploadCropbox} this
30724 * @param {Object} formData
30729 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30732 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30734 emptyText : 'Click to upload image',
30735 rotateNotify : 'Image is too small to rotate',
30736 errorTimeout : 3000,
30750 cropType : 'image/jpeg',
30752 canvasLoaded : false,
30753 isDocument : false,
30755 paramName : 'imageUpload',
30757 loadingText : 'Loading...',
30760 getAutoCreate : function()
30764 cls : 'roo-upload-cropbox',
30768 cls : 'roo-upload-cropbox-selector',
30773 cls : 'roo-upload-cropbox-body',
30774 style : 'cursor:pointer',
30778 cls : 'roo-upload-cropbox-preview'
30782 cls : 'roo-upload-cropbox-thumb'
30786 cls : 'roo-upload-cropbox-empty-notify',
30787 html : this.emptyText
30791 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30792 html : this.rotateNotify
30798 cls : 'roo-upload-cropbox-footer',
30801 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30811 onRender : function(ct, position)
30813 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30815 if (this.buttons.length) {
30817 Roo.each(this.buttons, function(bb) {
30819 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30821 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30827 this.maskEl = this.el;
30831 initEvents : function()
30833 this.urlAPI = (window.createObjectURL && window) ||
30834 (window.URL && URL.revokeObjectURL && URL) ||
30835 (window.webkitURL && webkitURL);
30837 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30838 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30840 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30841 this.selectorEl.hide();
30843 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30844 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30846 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30847 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30848 this.thumbEl.hide();
30850 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30851 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30853 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30854 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30855 this.errorEl.hide();
30857 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30858 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30859 this.footerEl.hide();
30861 this.setThumbBoxSize();
30867 this.fireEvent('initial', this);
30874 window.addEventListener("resize", function() { _this.resize(); } );
30876 this.bodyEl.on('click', this.beforeSelectFile, this);
30879 this.bodyEl.on('touchstart', this.onTouchStart, this);
30880 this.bodyEl.on('touchmove', this.onTouchMove, this);
30881 this.bodyEl.on('touchend', this.onTouchEnd, this);
30885 this.bodyEl.on('mousedown', this.onMouseDown, this);
30886 this.bodyEl.on('mousemove', this.onMouseMove, this);
30887 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30888 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30889 Roo.get(document).on('mouseup', this.onMouseUp, this);
30892 this.selectorEl.on('change', this.onFileSelected, this);
30898 this.baseScale = 1;
30900 this.baseRotate = 1;
30901 this.dragable = false;
30902 this.pinching = false;
30905 this.cropData = false;
30906 this.notifyEl.dom.innerHTML = this.emptyText;
30908 this.selectorEl.dom.value = '';
30912 resize : function()
30914 if(this.fireEvent('resize', this) != false){
30915 this.setThumbBoxPosition();
30916 this.setCanvasPosition();
30920 onFooterButtonClick : function(e, el, o, type)
30923 case 'rotate-left' :
30924 this.onRotateLeft(e);
30926 case 'rotate-right' :
30927 this.onRotateRight(e);
30930 this.beforeSelectFile(e);
30945 this.fireEvent('footerbuttonclick', this, type);
30948 beforeSelectFile : function(e)
30950 e.preventDefault();
30952 if(this.fireEvent('beforeselectfile', this) != false){
30953 this.selectorEl.dom.click();
30957 onFileSelected : function(e)
30959 e.preventDefault();
30961 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30965 var file = this.selectorEl.dom.files[0];
30967 if(this.fireEvent('inspect', this, file) != false){
30968 this.prepare(file);
30973 trash : function(e)
30975 this.fireEvent('trash', this);
30978 download : function(e)
30980 this.fireEvent('download', this);
30983 loadCanvas : function(src)
30985 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30989 this.imageEl = document.createElement('img');
30993 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30995 this.imageEl.src = src;
30999 onLoadCanvas : function()
31001 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31002 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31004 this.bodyEl.un('click', this.beforeSelectFile, this);
31006 this.notifyEl.hide();
31007 this.thumbEl.show();
31008 this.footerEl.show();
31010 this.baseRotateLevel();
31012 if(this.isDocument){
31013 this.setThumbBoxSize();
31016 this.setThumbBoxPosition();
31018 this.baseScaleLevel();
31024 this.canvasLoaded = true;
31027 this.maskEl.unmask();
31032 setCanvasPosition : function()
31034 if(!this.canvasEl){
31038 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31039 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31041 this.previewEl.setLeft(pw);
31042 this.previewEl.setTop(ph);
31046 onMouseDown : function(e)
31050 this.dragable = true;
31051 this.pinching = false;
31053 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31054 this.dragable = false;
31058 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31059 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31063 onMouseMove : function(e)
31067 if(!this.canvasLoaded){
31071 if (!this.dragable){
31075 var minX = Math.ceil(this.thumbEl.getLeft(true));
31076 var minY = Math.ceil(this.thumbEl.getTop(true));
31078 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31079 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31081 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31082 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31084 x = x - this.mouseX;
31085 y = y - this.mouseY;
31087 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31088 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31090 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31091 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31093 this.previewEl.setLeft(bgX);
31094 this.previewEl.setTop(bgY);
31096 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31100 onMouseUp : function(e)
31104 this.dragable = false;
31107 onMouseWheel : function(e)
31111 this.startScale = this.scale;
31113 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31115 if(!this.zoomable()){
31116 this.scale = this.startScale;
31125 zoomable : function()
31127 var minScale = this.thumbEl.getWidth() / this.minWidth;
31129 if(this.minWidth < this.minHeight){
31130 minScale = this.thumbEl.getHeight() / this.minHeight;
31133 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31134 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31138 (this.rotate == 0 || this.rotate == 180) &&
31140 width > this.imageEl.OriginWidth ||
31141 height > this.imageEl.OriginHeight ||
31142 (width < this.minWidth && height < this.minHeight)
31150 (this.rotate == 90 || this.rotate == 270) &&
31152 width > this.imageEl.OriginWidth ||
31153 height > this.imageEl.OriginHeight ||
31154 (width < this.minHeight && height < this.minWidth)
31161 !this.isDocument &&
31162 (this.rotate == 0 || this.rotate == 180) &&
31164 width < this.minWidth ||
31165 width > this.imageEl.OriginWidth ||
31166 height < this.minHeight ||
31167 height > this.imageEl.OriginHeight
31174 !this.isDocument &&
31175 (this.rotate == 90 || this.rotate == 270) &&
31177 width < this.minHeight ||
31178 width > this.imageEl.OriginWidth ||
31179 height < this.minWidth ||
31180 height > this.imageEl.OriginHeight
31190 onRotateLeft : function(e)
31192 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31194 var minScale = this.thumbEl.getWidth() / this.minWidth;
31196 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31197 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31199 this.startScale = this.scale;
31201 while (this.getScaleLevel() < minScale){
31203 this.scale = this.scale + 1;
31205 if(!this.zoomable()){
31210 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31211 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31216 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31223 this.scale = this.startScale;
31225 this.onRotateFail();
31230 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31232 if(this.isDocument){
31233 this.setThumbBoxSize();
31234 this.setThumbBoxPosition();
31235 this.setCanvasPosition();
31240 this.fireEvent('rotate', this, 'left');
31244 onRotateRight : function(e)
31246 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31248 var minScale = this.thumbEl.getWidth() / this.minWidth;
31250 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31251 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31253 this.startScale = this.scale;
31255 while (this.getScaleLevel() < minScale){
31257 this.scale = this.scale + 1;
31259 if(!this.zoomable()){
31264 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31265 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31270 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31277 this.scale = this.startScale;
31279 this.onRotateFail();
31284 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31286 if(this.isDocument){
31287 this.setThumbBoxSize();
31288 this.setThumbBoxPosition();
31289 this.setCanvasPosition();
31294 this.fireEvent('rotate', this, 'right');
31297 onRotateFail : function()
31299 this.errorEl.show(true);
31303 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31308 this.previewEl.dom.innerHTML = '';
31310 var canvasEl = document.createElement("canvas");
31312 var contextEl = canvasEl.getContext("2d");
31314 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31315 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31316 var center = this.imageEl.OriginWidth / 2;
31318 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31319 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31320 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31321 center = this.imageEl.OriginHeight / 2;
31324 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31326 contextEl.translate(center, center);
31327 contextEl.rotate(this.rotate * Math.PI / 180);
31329 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31331 this.canvasEl = document.createElement("canvas");
31333 this.contextEl = this.canvasEl.getContext("2d");
31335 switch (this.rotate) {
31338 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31339 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31341 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31346 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31347 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31349 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31350 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);
31354 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31359 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31360 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31362 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31363 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);
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);
31372 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31373 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31375 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31376 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
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);
31387 this.previewEl.appendChild(this.canvasEl);
31389 this.setCanvasPosition();
31394 if(!this.canvasLoaded){
31398 var imageCanvas = document.createElement("canvas");
31400 var imageContext = imageCanvas.getContext("2d");
31402 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31403 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31405 var center = imageCanvas.width / 2;
31407 imageContext.translate(center, center);
31409 imageContext.rotate(this.rotate * Math.PI / 180);
31411 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31413 var canvas = document.createElement("canvas");
31415 var context = canvas.getContext("2d");
31417 canvas.width = this.minWidth;
31418 canvas.height = this.minHeight;
31420 switch (this.rotate) {
31423 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31424 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31426 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31427 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31429 var targetWidth = this.minWidth - 2 * x;
31430 var targetHeight = this.minHeight - 2 * y;
31434 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31435 scale = targetWidth / width;
31438 if(x > 0 && y == 0){
31439 scale = targetHeight / height;
31442 if(x > 0 && y > 0){
31443 scale = targetWidth / width;
31445 if(width < height){
31446 scale = targetHeight / height;
31450 context.scale(scale, scale);
31452 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31453 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31455 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31456 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31458 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31463 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31464 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31466 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31467 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31469 var targetWidth = this.minWidth - 2 * x;
31470 var targetHeight = this.minHeight - 2 * y;
31474 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31475 scale = targetWidth / width;
31478 if(x > 0 && y == 0){
31479 scale = targetHeight / height;
31482 if(x > 0 && y > 0){
31483 scale = targetWidth / width;
31485 if(width < height){
31486 scale = targetHeight / height;
31490 context.scale(scale, scale);
31492 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31493 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31495 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31496 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31498 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31500 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31505 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31506 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31508 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31509 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31511 var targetWidth = this.minWidth - 2 * x;
31512 var targetHeight = this.minHeight - 2 * y;
31516 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31517 scale = targetWidth / width;
31520 if(x > 0 && y == 0){
31521 scale = targetHeight / height;
31524 if(x > 0 && y > 0){
31525 scale = targetWidth / width;
31527 if(width < height){
31528 scale = targetHeight / height;
31532 context.scale(scale, scale);
31534 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31535 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31537 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31538 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31540 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31541 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31543 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31548 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31549 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31551 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31552 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31554 var targetWidth = this.minWidth - 2 * x;
31555 var targetHeight = this.minHeight - 2 * y;
31559 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31560 scale = targetWidth / width;
31563 if(x > 0 && y == 0){
31564 scale = targetHeight / height;
31567 if(x > 0 && y > 0){
31568 scale = targetWidth / width;
31570 if(width < height){
31571 scale = targetHeight / height;
31575 context.scale(scale, scale);
31577 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31578 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31580 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31581 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31583 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31585 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31592 this.cropData = canvas.toDataURL(this.cropType);
31594 if(this.fireEvent('crop', this, this.cropData) !== false){
31595 this.process(this.file, this.cropData);
31602 setThumbBoxSize : function()
31606 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31607 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31608 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31610 this.minWidth = width;
31611 this.minHeight = height;
31613 if(this.rotate == 90 || this.rotate == 270){
31614 this.minWidth = height;
31615 this.minHeight = width;
31620 width = Math.ceil(this.minWidth * height / this.minHeight);
31622 if(this.minWidth > this.minHeight){
31624 height = Math.ceil(this.minHeight * width / this.minWidth);
31627 this.thumbEl.setStyle({
31628 width : width + 'px',
31629 height : height + 'px'
31636 setThumbBoxPosition : function()
31638 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31639 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31641 this.thumbEl.setLeft(x);
31642 this.thumbEl.setTop(y);
31646 baseRotateLevel : function()
31648 this.baseRotate = 1;
31651 typeof(this.exif) != 'undefined' &&
31652 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31653 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31655 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31658 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31662 baseScaleLevel : function()
31666 if(this.isDocument){
31668 if(this.baseRotate == 6 || this.baseRotate == 8){
31670 height = this.thumbEl.getHeight();
31671 this.baseScale = height / this.imageEl.OriginWidth;
31673 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31674 width = this.thumbEl.getWidth();
31675 this.baseScale = width / this.imageEl.OriginHeight;
31681 height = this.thumbEl.getHeight();
31682 this.baseScale = height / this.imageEl.OriginHeight;
31684 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31685 width = this.thumbEl.getWidth();
31686 this.baseScale = width / this.imageEl.OriginWidth;
31692 if(this.baseRotate == 6 || this.baseRotate == 8){
31694 width = this.thumbEl.getHeight();
31695 this.baseScale = width / this.imageEl.OriginHeight;
31697 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31698 height = this.thumbEl.getWidth();
31699 this.baseScale = height / this.imageEl.OriginHeight;
31702 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31703 height = this.thumbEl.getWidth();
31704 this.baseScale = height / this.imageEl.OriginHeight;
31706 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31707 width = this.thumbEl.getHeight();
31708 this.baseScale = width / this.imageEl.OriginWidth;
31715 width = this.thumbEl.getWidth();
31716 this.baseScale = width / this.imageEl.OriginWidth;
31718 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31719 height = this.thumbEl.getHeight();
31720 this.baseScale = height / this.imageEl.OriginHeight;
31723 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31725 height = this.thumbEl.getHeight();
31726 this.baseScale = height / this.imageEl.OriginHeight;
31728 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31729 width = this.thumbEl.getWidth();
31730 this.baseScale = width / this.imageEl.OriginWidth;
31738 getScaleLevel : function()
31740 return this.baseScale * Math.pow(1.1, this.scale);
31743 onTouchStart : function(e)
31745 if(!this.canvasLoaded){
31746 this.beforeSelectFile(e);
31750 var touches = e.browserEvent.touches;
31756 if(touches.length == 1){
31757 this.onMouseDown(e);
31761 if(touches.length != 2){
31767 for(var i = 0, finger; finger = touches[i]; i++){
31768 coords.push(finger.pageX, finger.pageY);
31771 var x = Math.pow(coords[0] - coords[2], 2);
31772 var y = Math.pow(coords[1] - coords[3], 2);
31774 this.startDistance = Math.sqrt(x + y);
31776 this.startScale = this.scale;
31778 this.pinching = true;
31779 this.dragable = false;
31783 onTouchMove : function(e)
31785 if(!this.pinching && !this.dragable){
31789 var touches = e.browserEvent.touches;
31796 this.onMouseMove(e);
31802 for(var i = 0, finger; finger = touches[i]; i++){
31803 coords.push(finger.pageX, finger.pageY);
31806 var x = Math.pow(coords[0] - coords[2], 2);
31807 var y = Math.pow(coords[1] - coords[3], 2);
31809 this.endDistance = Math.sqrt(x + y);
31811 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31813 if(!this.zoomable()){
31814 this.scale = this.startScale;
31822 onTouchEnd : function(e)
31824 this.pinching = false;
31825 this.dragable = false;
31829 process : function(file, crop)
31832 this.maskEl.mask(this.loadingText);
31835 this.xhr = new XMLHttpRequest();
31837 file.xhr = this.xhr;
31839 this.xhr.open(this.method, this.url, true);
31842 "Accept": "application/json",
31843 "Cache-Control": "no-cache",
31844 "X-Requested-With": "XMLHttpRequest"
31847 for (var headerName in headers) {
31848 var headerValue = headers[headerName];
31850 this.xhr.setRequestHeader(headerName, headerValue);
31856 this.xhr.onload = function()
31858 _this.xhrOnLoad(_this.xhr);
31861 this.xhr.onerror = function()
31863 _this.xhrOnError(_this.xhr);
31866 var formData = new FormData();
31868 formData.append('returnHTML', 'NO');
31871 formData.append('crop', crop);
31874 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31875 formData.append(this.paramName, file, file.name);
31878 if(typeof(file.filename) != 'undefined'){
31879 formData.append('filename', file.filename);
31882 if(typeof(file.mimetype) != 'undefined'){
31883 formData.append('mimetype', file.mimetype);
31886 if(this.fireEvent('arrange', this, formData) != false){
31887 this.xhr.send(formData);
31891 xhrOnLoad : function(xhr)
31894 this.maskEl.unmask();
31897 if (xhr.readyState !== 4) {
31898 this.fireEvent('exception', this, xhr);
31902 var response = Roo.decode(xhr.responseText);
31904 if(!response.success){
31905 this.fireEvent('exception', this, xhr);
31909 var response = Roo.decode(xhr.responseText);
31911 this.fireEvent('upload', this, response);
31915 xhrOnError : function()
31918 this.maskEl.unmask();
31921 Roo.log('xhr on error');
31923 var response = Roo.decode(xhr.responseText);
31929 prepare : function(file)
31932 this.maskEl.mask(this.loadingText);
31938 if(typeof(file) === 'string'){
31939 this.loadCanvas(file);
31943 if(!file || !this.urlAPI){
31948 this.cropType = file.type;
31952 if(this.fireEvent('prepare', this, this.file) != false){
31954 var reader = new FileReader();
31956 reader.onload = function (e) {
31957 if (e.target.error) {
31958 Roo.log(e.target.error);
31962 var buffer = e.target.result,
31963 dataView = new DataView(buffer),
31965 maxOffset = dataView.byteLength - 4,
31969 if (dataView.getUint16(0) === 0xffd8) {
31970 while (offset < maxOffset) {
31971 markerBytes = dataView.getUint16(offset);
31973 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31974 markerLength = dataView.getUint16(offset + 2) + 2;
31975 if (offset + markerLength > dataView.byteLength) {
31976 Roo.log('Invalid meta data: Invalid segment size.');
31980 if(markerBytes == 0xffe1){
31981 _this.parseExifData(
31988 offset += markerLength;
31998 var url = _this.urlAPI.createObjectURL(_this.file);
32000 _this.loadCanvas(url);
32005 reader.readAsArrayBuffer(this.file);
32011 parseExifData : function(dataView, offset, length)
32013 var tiffOffset = offset + 10,
32017 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32018 // No Exif data, might be XMP data instead
32022 // Check for the ASCII code for "Exif" (0x45786966):
32023 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32024 // No Exif data, might be XMP data instead
32027 if (tiffOffset + 8 > dataView.byteLength) {
32028 Roo.log('Invalid Exif data: Invalid segment size.');
32031 // Check for the two null bytes:
32032 if (dataView.getUint16(offset + 8) !== 0x0000) {
32033 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32036 // Check the byte alignment:
32037 switch (dataView.getUint16(tiffOffset)) {
32039 littleEndian = true;
32042 littleEndian = false;
32045 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32048 // Check for the TIFF tag marker (0x002A):
32049 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32050 Roo.log('Invalid Exif data: Missing TIFF marker.');
32053 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32054 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32056 this.parseExifTags(
32059 tiffOffset + dirOffset,
32064 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32069 if (dirOffset + 6 > dataView.byteLength) {
32070 Roo.log('Invalid Exif data: Invalid directory offset.');
32073 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32074 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32075 if (dirEndOffset + 4 > dataView.byteLength) {
32076 Roo.log('Invalid Exif data: Invalid directory size.');
32079 for (i = 0; i < tagsNumber; i += 1) {
32083 dirOffset + 2 + 12 * i, // tag offset
32087 // Return the offset to the next directory:
32088 return dataView.getUint32(dirEndOffset, littleEndian);
32091 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32093 var tag = dataView.getUint16(offset, littleEndian);
32095 this.exif[tag] = this.getExifValue(
32099 dataView.getUint16(offset + 2, littleEndian), // tag type
32100 dataView.getUint32(offset + 4, littleEndian), // tag length
32105 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32107 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32116 Roo.log('Invalid Exif data: Invalid tag type.');
32120 tagSize = tagType.size * length;
32121 // Determine if the value is contained in the dataOffset bytes,
32122 // or if the value at the dataOffset is a pointer to the actual data:
32123 dataOffset = tagSize > 4 ?
32124 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32125 if (dataOffset + tagSize > dataView.byteLength) {
32126 Roo.log('Invalid Exif data: Invalid data offset.');
32129 if (length === 1) {
32130 return tagType.getValue(dataView, dataOffset, littleEndian);
32133 for (i = 0; i < length; i += 1) {
32134 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32137 if (tagType.ascii) {
32139 // Concatenate the chars:
32140 for (i = 0; i < values.length; i += 1) {
32142 // Ignore the terminating NULL byte(s):
32143 if (c === '\u0000') {
32155 Roo.apply(Roo.bootstrap.UploadCropbox, {
32157 'Orientation': 0x0112
32161 1: 0, //'top-left',
32163 3: 180, //'bottom-right',
32164 // 4: 'bottom-left',
32166 6: 90, //'right-top',
32167 // 7: 'right-bottom',
32168 8: 270 //'left-bottom'
32172 // byte, 8-bit unsigned int:
32174 getValue: function (dataView, dataOffset) {
32175 return dataView.getUint8(dataOffset);
32179 // ascii, 8-bit byte:
32181 getValue: function (dataView, dataOffset) {
32182 return String.fromCharCode(dataView.getUint8(dataOffset));
32187 // short, 16 bit int:
32189 getValue: function (dataView, dataOffset, littleEndian) {
32190 return dataView.getUint16(dataOffset, littleEndian);
32194 // long, 32 bit int:
32196 getValue: function (dataView, dataOffset, littleEndian) {
32197 return dataView.getUint32(dataOffset, littleEndian);
32201 // rational = two long values, first is numerator, second is denominator:
32203 getValue: function (dataView, dataOffset, littleEndian) {
32204 return dataView.getUint32(dataOffset, littleEndian) /
32205 dataView.getUint32(dataOffset + 4, littleEndian);
32209 // slong, 32 bit signed int:
32211 getValue: function (dataView, dataOffset, littleEndian) {
32212 return dataView.getInt32(dataOffset, littleEndian);
32216 // srational, two slongs, first is numerator, second is denominator:
32218 getValue: function (dataView, dataOffset, littleEndian) {
32219 return dataView.getInt32(dataOffset, littleEndian) /
32220 dataView.getInt32(dataOffset + 4, littleEndian);
32230 cls : 'btn-group roo-upload-cropbox-rotate-left',
32231 action : 'rotate-left',
32235 cls : 'btn btn-default',
32236 html : '<i class="fa fa-undo"></i>'
32242 cls : 'btn-group roo-upload-cropbox-picture',
32243 action : 'picture',
32247 cls : 'btn btn-default',
32248 html : '<i class="fa fa-picture-o"></i>'
32254 cls : 'btn-group roo-upload-cropbox-rotate-right',
32255 action : 'rotate-right',
32259 cls : 'btn btn-default',
32260 html : '<i class="fa fa-repeat"></i>'
32268 cls : 'btn-group roo-upload-cropbox-rotate-left',
32269 action : 'rotate-left',
32273 cls : 'btn btn-default',
32274 html : '<i class="fa fa-undo"></i>'
32280 cls : 'btn-group roo-upload-cropbox-download',
32281 action : 'download',
32285 cls : 'btn btn-default',
32286 html : '<i class="fa fa-download"></i>'
32292 cls : 'btn-group roo-upload-cropbox-crop',
32297 cls : 'btn btn-default',
32298 html : '<i class="fa fa-crop"></i>'
32304 cls : 'btn-group roo-upload-cropbox-trash',
32309 cls : 'btn btn-default',
32310 html : '<i class="fa fa-trash"></i>'
32316 cls : 'btn-group roo-upload-cropbox-rotate-right',
32317 action : 'rotate-right',
32321 cls : 'btn btn-default',
32322 html : '<i class="fa fa-repeat"></i>'
32330 cls : 'btn-group roo-upload-cropbox-rotate-left',
32331 action : 'rotate-left',
32335 cls : 'btn btn-default',
32336 html : '<i class="fa fa-undo"></i>'
32342 cls : 'btn-group roo-upload-cropbox-rotate-right',
32343 action : 'rotate-right',
32347 cls : 'btn btn-default',
32348 html : '<i class="fa fa-repeat"></i>'
32361 * @class Roo.bootstrap.DocumentManager
32362 * @extends Roo.bootstrap.Component
32363 * Bootstrap DocumentManager class
32364 * @cfg {String} paramName default 'imageUpload'
32365 * @cfg {String} toolTipName default 'filename'
32366 * @cfg {String} method default POST
32367 * @cfg {String} url action url
32368 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32369 * @cfg {Boolean} multiple multiple upload default true
32370 * @cfg {Number} thumbSize default 300
32371 * @cfg {String} fieldLabel
32372 * @cfg {Number} labelWidth default 4
32373 * @cfg {String} labelAlign (left|top) default left
32374 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32375 * @cfg {Number} labellg set the width of label (1-12)
32376 * @cfg {Number} labelmd set the width of label (1-12)
32377 * @cfg {Number} labelsm set the width of label (1-12)
32378 * @cfg {Number} labelxs set the width of label (1-12)
32381 * Create a new DocumentManager
32382 * @param {Object} config The config object
32385 Roo.bootstrap.DocumentManager = function(config){
32386 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32389 this.delegates = [];
32394 * Fire when initial the DocumentManager
32395 * @param {Roo.bootstrap.DocumentManager} this
32400 * inspect selected file
32401 * @param {Roo.bootstrap.DocumentManager} this
32402 * @param {File} file
32407 * Fire when xhr load exception
32408 * @param {Roo.bootstrap.DocumentManager} this
32409 * @param {XMLHttpRequest} xhr
32411 "exception" : true,
32413 * @event afterupload
32414 * Fire when xhr load exception
32415 * @param {Roo.bootstrap.DocumentManager} this
32416 * @param {XMLHttpRequest} xhr
32418 "afterupload" : true,
32421 * prepare the form data
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {Object} formData
32428 * Fire when remove the file
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {Object} file
32435 * Fire after refresh the file
32436 * @param {Roo.bootstrap.DocumentManager} this
32441 * Fire after click the image
32442 * @param {Roo.bootstrap.DocumentManager} this
32443 * @param {Object} file
32448 * Fire when upload a image and editable set to true
32449 * @param {Roo.bootstrap.DocumentManager} this
32450 * @param {Object} file
32454 * @event beforeselectfile
32455 * Fire before select file
32456 * @param {Roo.bootstrap.DocumentManager} this
32458 "beforeselectfile" : true,
32461 * Fire before process file
32462 * @param {Roo.bootstrap.DocumentManager} this
32463 * @param {Object} file
32467 * @event previewrendered
32468 * Fire when preview rendered
32469 * @param {Roo.bootstrap.DocumentManager} this
32470 * @param {Object} file
32472 "previewrendered" : true,
32475 "previewResize" : true
32480 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32489 paramName : 'imageUpload',
32490 toolTipName : 'filename',
32493 labelAlign : 'left',
32503 getAutoCreate : function()
32505 var managerWidget = {
32507 cls : 'roo-document-manager',
32511 cls : 'roo-document-manager-selector',
32516 cls : 'roo-document-manager-uploader',
32520 cls : 'roo-document-manager-upload-btn',
32521 html : '<i class="fa fa-plus"></i>'
32532 cls : 'column col-md-12',
32537 if(this.fieldLabel.length){
32542 cls : 'column col-md-12',
32543 html : this.fieldLabel
32547 cls : 'column col-md-12',
32552 if(this.labelAlign == 'left'){
32557 html : this.fieldLabel
32566 if(this.labelWidth > 12){
32567 content[0].style = "width: " + this.labelWidth + 'px';
32570 if(this.labelWidth < 13 && this.labelmd == 0){
32571 this.labelmd = this.labelWidth;
32574 if(this.labellg > 0){
32575 content[0].cls += ' col-lg-' + this.labellg;
32576 content[1].cls += ' col-lg-' + (12 - this.labellg);
32579 if(this.labelmd > 0){
32580 content[0].cls += ' col-md-' + this.labelmd;
32581 content[1].cls += ' col-md-' + (12 - this.labelmd);
32584 if(this.labelsm > 0){
32585 content[0].cls += ' col-sm-' + this.labelsm;
32586 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32589 if(this.labelxs > 0){
32590 content[0].cls += ' col-xs-' + this.labelxs;
32591 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32599 cls : 'row clearfix',
32607 initEvents : function()
32609 this.managerEl = this.el.select('.roo-document-manager', true).first();
32610 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32612 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32613 this.selectorEl.hide();
32616 this.selectorEl.attr('multiple', 'multiple');
32619 this.selectorEl.on('change', this.onFileSelected, this);
32621 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32622 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32624 this.uploader.on('click', this.onUploaderClick, this);
32626 this.renderProgressDialog();
32630 window.addEventListener("resize", function() { _this.refresh(); } );
32632 this.fireEvent('initial', this);
32635 renderProgressDialog : function()
32639 this.progressDialog = new Roo.bootstrap.Modal({
32640 cls : 'roo-document-manager-progress-dialog',
32641 allow_close : false,
32652 btnclick : function() {
32653 _this.uploadCancel();
32659 this.progressDialog.render(Roo.get(document.body));
32661 this.progress = new Roo.bootstrap.Progress({
32662 cls : 'roo-document-manager-progress',
32667 this.progress.render(this.progressDialog.getChildContainer());
32669 this.progressBar = new Roo.bootstrap.ProgressBar({
32670 cls : 'roo-document-manager-progress-bar',
32673 aria_valuemax : 12,
32677 this.progressBar.render(this.progress.getChildContainer());
32680 onUploaderClick : function(e)
32682 e.preventDefault();
32684 if(this.fireEvent('beforeselectfile', this) != false){
32685 this.selectorEl.dom.click();
32690 onFileSelected : function(e)
32692 e.preventDefault();
32694 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32698 Roo.each(this.selectorEl.dom.files, function(file){
32699 if(this.fireEvent('inspect', this, file) != false){
32700 this.files.push(file);
32710 this.selectorEl.dom.value = '';
32712 if(!this.files || !this.files.length){
32716 if(this.boxes > 0 && this.files.length > this.boxes){
32717 this.files = this.files.slice(0, this.boxes);
32720 this.uploader.show();
32722 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32723 this.uploader.hide();
32732 Roo.each(this.files, function(file){
32734 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32735 var f = this.renderPreview(file);
32740 if(file.type.indexOf('image') != -1){
32741 this.delegates.push(
32743 _this.process(file);
32744 }).createDelegate(this)
32752 _this.process(file);
32753 }).createDelegate(this)
32758 this.files = files;
32760 this.delegates = this.delegates.concat(docs);
32762 if(!this.delegates.length){
32767 this.progressBar.aria_valuemax = this.delegates.length;
32774 arrange : function()
32776 if(!this.delegates.length){
32777 this.progressDialog.hide();
32782 var delegate = this.delegates.shift();
32784 this.progressDialog.show();
32786 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32788 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32793 refresh : function()
32795 this.uploader.show();
32797 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32798 this.uploader.hide();
32801 Roo.isTouch ? this.closable(false) : this.closable(true);
32803 this.fireEvent('refresh', this);
32806 onRemove : function(e, el, o)
32808 e.preventDefault();
32810 this.fireEvent('remove', this, o);
32814 remove : function(o)
32818 Roo.each(this.files, function(file){
32819 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32828 this.files = files;
32835 Roo.each(this.files, function(file){
32840 file.target.remove();
32849 onClick : function(e, el, o)
32851 e.preventDefault();
32853 this.fireEvent('click', this, o);
32857 closable : function(closable)
32859 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32861 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32873 xhrOnLoad : function(xhr)
32875 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32879 if (xhr.readyState !== 4) {
32881 this.fireEvent('exception', this, xhr);
32885 var response = Roo.decode(xhr.responseText);
32887 if(!response.success){
32889 this.fireEvent('exception', this, xhr);
32893 var file = this.renderPreview(response.data);
32895 this.files.push(file);
32899 this.fireEvent('afterupload', this, xhr);
32903 xhrOnError : function(xhr)
32905 Roo.log('xhr on error');
32907 var response = Roo.decode(xhr.responseText);
32914 process : function(file)
32916 if(this.fireEvent('process', this, file) !== false){
32917 if(this.editable && file.type.indexOf('image') != -1){
32918 this.fireEvent('edit', this, file);
32922 this.uploadStart(file, false);
32929 uploadStart : function(file, crop)
32931 this.xhr = new XMLHttpRequest();
32933 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32938 file.xhr = this.xhr;
32940 this.managerEl.createChild({
32942 cls : 'roo-document-manager-loading',
32946 tooltip : file.name,
32947 cls : 'roo-document-manager-thumb',
32948 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32954 this.xhr.open(this.method, this.url, true);
32957 "Accept": "application/json",
32958 "Cache-Control": "no-cache",
32959 "X-Requested-With": "XMLHttpRequest"
32962 for (var headerName in headers) {
32963 var headerValue = headers[headerName];
32965 this.xhr.setRequestHeader(headerName, headerValue);
32971 this.xhr.onload = function()
32973 _this.xhrOnLoad(_this.xhr);
32976 this.xhr.onerror = function()
32978 _this.xhrOnError(_this.xhr);
32981 var formData = new FormData();
32983 formData.append('returnHTML', 'NO');
32986 formData.append('crop', crop);
32989 formData.append(this.paramName, file, file.name);
32996 if(this.fireEvent('prepare', this, formData, options) != false){
32998 if(options.manually){
33002 this.xhr.send(formData);
33006 this.uploadCancel();
33009 uploadCancel : function()
33015 this.delegates = [];
33017 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33024 renderPreview : function(file)
33026 if(typeof(file.target) != 'undefined' && file.target){
33030 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33032 var previewEl = this.managerEl.createChild({
33034 cls : 'roo-document-manager-preview',
33038 tooltip : file[this.toolTipName],
33039 cls : 'roo-document-manager-thumb',
33040 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33045 html : '<i class="fa fa-times-circle"></i>'
33050 var close = previewEl.select('button.close', true).first();
33052 close.on('click', this.onRemove, this, file);
33054 file.target = previewEl;
33056 var image = previewEl.select('img', true).first();
33060 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33062 image.on('click', this.onClick, this, file);
33064 this.fireEvent('previewrendered', this, file);
33070 onPreviewLoad : function(file, image)
33072 if(typeof(file.target) == 'undefined' || !file.target){
33076 var width = image.dom.naturalWidth || image.dom.width;
33077 var height = image.dom.naturalHeight || image.dom.height;
33079 if(!this.previewResize) {
33083 if(width > height){
33084 file.target.addClass('wide');
33088 file.target.addClass('tall');
33093 uploadFromSource : function(file, crop)
33095 this.xhr = new XMLHttpRequest();
33097 this.managerEl.createChild({
33099 cls : 'roo-document-manager-loading',
33103 tooltip : file.name,
33104 cls : 'roo-document-manager-thumb',
33105 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33111 this.xhr.open(this.method, this.url, true);
33114 "Accept": "application/json",
33115 "Cache-Control": "no-cache",
33116 "X-Requested-With": "XMLHttpRequest"
33119 for (var headerName in headers) {
33120 var headerValue = headers[headerName];
33122 this.xhr.setRequestHeader(headerName, headerValue);
33128 this.xhr.onload = function()
33130 _this.xhrOnLoad(_this.xhr);
33133 this.xhr.onerror = function()
33135 _this.xhrOnError(_this.xhr);
33138 var formData = new FormData();
33140 formData.append('returnHTML', 'NO');
33142 formData.append('crop', crop);
33144 if(typeof(file.filename) != 'undefined'){
33145 formData.append('filename', file.filename);
33148 if(typeof(file.mimetype) != 'undefined'){
33149 formData.append('mimetype', file.mimetype);
33154 if(this.fireEvent('prepare', this, formData) != false){
33155 this.xhr.send(formData);
33165 * @class Roo.bootstrap.DocumentViewer
33166 * @extends Roo.bootstrap.Component
33167 * Bootstrap DocumentViewer class
33168 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33169 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33172 * Create a new DocumentViewer
33173 * @param {Object} config The config object
33176 Roo.bootstrap.DocumentViewer = function(config){
33177 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33182 * Fire after initEvent
33183 * @param {Roo.bootstrap.DocumentViewer} this
33189 * @param {Roo.bootstrap.DocumentViewer} this
33194 * Fire after download button
33195 * @param {Roo.bootstrap.DocumentViewer} this
33200 * Fire after trash button
33201 * @param {Roo.bootstrap.DocumentViewer} this
33208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33210 showDownload : true,
33214 getAutoCreate : function()
33218 cls : 'roo-document-viewer',
33222 cls : 'roo-document-viewer-body',
33226 cls : 'roo-document-viewer-thumb',
33230 cls : 'roo-document-viewer-image'
33238 cls : 'roo-document-viewer-footer',
33241 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33245 cls : 'btn-group roo-document-viewer-download',
33249 cls : 'btn btn-default',
33250 html : '<i class="fa fa-download"></i>'
33256 cls : 'btn-group roo-document-viewer-trash',
33260 cls : 'btn btn-default',
33261 html : '<i class="fa fa-trash"></i>'
33274 initEvents : function()
33276 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33277 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33279 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33280 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33282 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33283 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33285 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33286 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33288 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33289 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33291 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33292 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33294 this.bodyEl.on('click', this.onClick, this);
33295 this.downloadBtn.on('click', this.onDownload, this);
33296 this.trashBtn.on('click', this.onTrash, this);
33298 this.downloadBtn.hide();
33299 this.trashBtn.hide();
33301 if(this.showDownload){
33302 this.downloadBtn.show();
33305 if(this.showTrash){
33306 this.trashBtn.show();
33309 if(!this.showDownload && !this.showTrash) {
33310 this.footerEl.hide();
33315 initial : function()
33317 this.fireEvent('initial', this);
33321 onClick : function(e)
33323 e.preventDefault();
33325 this.fireEvent('click', this);
33328 onDownload : function(e)
33330 e.preventDefault();
33332 this.fireEvent('download', this);
33335 onTrash : function(e)
33337 e.preventDefault();
33339 this.fireEvent('trash', this);
33351 * @class Roo.bootstrap.form.FieldLabel
33352 * @extends Roo.bootstrap.Component
33353 * Bootstrap FieldLabel class
33354 * @cfg {String} html contents of the element
33355 * @cfg {String} tag tag of the element default label
33356 * @cfg {String} cls class of the element
33357 * @cfg {String} target label target
33358 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33359 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33360 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33361 * @cfg {String} iconTooltip default "This field is required"
33362 * @cfg {String} indicatorpos (left|right) default left
33365 * Create a new FieldLabel
33366 * @param {Object} config The config object
33369 Roo.bootstrap.form.FieldLabel = function(config){
33370 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33375 * Fires after the field has been marked as invalid.
33376 * @param {Roo.form.FieldLabel} this
33377 * @param {String} msg The validation message
33382 * Fires after the field has been validated with no errors.
33383 * @param {Roo.form.FieldLabel} this
33389 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33396 invalidClass : 'has-warning',
33397 validClass : 'has-success',
33398 iconTooltip : 'This field is required',
33399 indicatorpos : 'left',
33401 getAutoCreate : function(){
33404 if (!this.allowBlank) {
33410 cls : 'roo-bootstrap-field-label ' + this.cls,
33415 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33416 tooltip : this.iconTooltip
33425 if(this.indicatorpos == 'right'){
33428 cls : 'roo-bootstrap-field-label ' + this.cls,
33437 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33438 tooltip : this.iconTooltip
33447 initEvents: function()
33449 Roo.bootstrap.Element.superclass.initEvents.call(this);
33451 this.indicator = this.indicatorEl();
33453 if(this.indicator){
33454 this.indicator.removeClass('visible');
33455 this.indicator.addClass('invisible');
33458 Roo.bootstrap.form.FieldLabel.register(this);
33461 indicatorEl : function()
33463 var indicator = this.el.select('i.roo-required-indicator',true).first();
33474 * Mark this field as valid
33476 markValid : function()
33478 if(this.indicator){
33479 this.indicator.removeClass('visible');
33480 this.indicator.addClass('invisible');
33482 if (Roo.bootstrap.version == 3) {
33483 this.el.removeClass(this.invalidClass);
33484 this.el.addClass(this.validClass);
33486 this.el.removeClass('is-invalid');
33487 this.el.addClass('is-valid');
33491 this.fireEvent('valid', this);
33495 * Mark this field as invalid
33496 * @param {String} msg The validation message
33498 markInvalid : function(msg)
33500 if(this.indicator){
33501 this.indicator.removeClass('invisible');
33502 this.indicator.addClass('visible');
33504 if (Roo.bootstrap.version == 3) {
33505 this.el.removeClass(this.validClass);
33506 this.el.addClass(this.invalidClass);
33508 this.el.removeClass('is-valid');
33509 this.el.addClass('is-invalid');
33513 this.fireEvent('invalid', this, msg);
33519 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33524 * register a FieldLabel Group
33525 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33527 register : function(label)
33529 if(this.groups.hasOwnProperty(label.target)){
33533 this.groups[label.target] = label;
33537 * fetch a FieldLabel Group based on the target
33538 * @param {string} target
33539 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33541 get: function(target) {
33542 if (typeof(this.groups[target]) == 'undefined') {
33546 return this.groups[target] ;
33555 * page DateSplitField.
33561 * @class Roo.bootstrap.form.DateSplitField
33562 * @extends Roo.bootstrap.Component
33563 * Bootstrap DateSplitField class
33564 * @cfg {string} fieldLabel - the label associated
33565 * @cfg {Number} labelWidth set the width of label (0-12)
33566 * @cfg {String} labelAlign (top|left)
33567 * @cfg {Boolean} dayAllowBlank (true|false) default false
33568 * @cfg {Boolean} monthAllowBlank (true|false) default false
33569 * @cfg {Boolean} yearAllowBlank (true|false) default false
33570 * @cfg {string} dayPlaceholder
33571 * @cfg {string} monthPlaceholder
33572 * @cfg {string} yearPlaceholder
33573 * @cfg {string} dayFormat default 'd'
33574 * @cfg {string} monthFormat default 'm'
33575 * @cfg {string} yearFormat default 'Y'
33576 * @cfg {Number} labellg set the width of label (1-12)
33577 * @cfg {Number} labelmd set the width of label (1-12)
33578 * @cfg {Number} labelsm set the width of label (1-12)
33579 * @cfg {Number} labelxs set the width of label (1-12)
33583 * Create a new DateSplitField
33584 * @param {Object} config The config object
33587 Roo.bootstrap.form.DateSplitField = function(config){
33588 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33594 * getting the data of years
33595 * @param {Roo.bootstrap.form.DateSplitField} this
33596 * @param {Object} years
33601 * getting the data of days
33602 * @param {Roo.bootstrap.form.DateSplitField} this
33603 * @param {Object} days
33608 * Fires after the field has been marked as invalid.
33609 * @param {Roo.form.Field} this
33610 * @param {String} msg The validation message
33615 * Fires after the field has been validated with no errors.
33616 * @param {Roo.form.Field} this
33622 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33625 labelAlign : 'top',
33627 dayAllowBlank : false,
33628 monthAllowBlank : false,
33629 yearAllowBlank : false,
33630 dayPlaceholder : '',
33631 monthPlaceholder : '',
33632 yearPlaceholder : '',
33636 isFormField : true,
33642 getAutoCreate : function()
33646 cls : 'row roo-date-split-field-group',
33651 cls : 'form-hidden-field roo-date-split-field-group-value',
33657 var labelCls = 'col-md-12';
33658 var contentCls = 'col-md-4';
33660 if(this.fieldLabel){
33664 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33668 html : this.fieldLabel
33673 if(this.labelAlign == 'left'){
33675 if(this.labelWidth > 12){
33676 label.style = "width: " + this.labelWidth + 'px';
33679 if(this.labelWidth < 13 && this.labelmd == 0){
33680 this.labelmd = this.labelWidth;
33683 if(this.labellg > 0){
33684 labelCls = ' col-lg-' + this.labellg;
33685 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33688 if(this.labelmd > 0){
33689 labelCls = ' col-md-' + this.labelmd;
33690 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33693 if(this.labelsm > 0){
33694 labelCls = ' col-sm-' + this.labelsm;
33695 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33698 if(this.labelxs > 0){
33699 labelCls = ' col-xs-' + this.labelxs;
33700 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33704 label.cls += ' ' + labelCls;
33706 cfg.cn.push(label);
33709 Roo.each(['day', 'month', 'year'], function(t){
33712 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33719 inputEl: function ()
33721 return this.el.select('.roo-date-split-field-group-value', true).first();
33724 onRender : function(ct, position)
33728 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33730 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33732 this.dayField = new Roo.bootstrap.form.ComboBox({
33733 allowBlank : this.dayAllowBlank,
33734 alwaysQuery : true,
33735 displayField : 'value',
33738 forceSelection : true,
33740 placeholder : this.dayPlaceholder,
33741 selectOnFocus : true,
33742 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33743 triggerAction : 'all',
33745 valueField : 'value',
33746 store : new Roo.data.SimpleStore({
33747 data : (function() {
33749 _this.fireEvent('days', _this, days);
33752 fields : [ 'value' ]
33755 select : function (_self, record, index)
33757 _this.setValue(_this.getValue());
33762 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33764 this.monthField = new Roo.bootstrap.form.MonthField({
33765 after : '<i class=\"fa fa-calendar\"></i>',
33766 allowBlank : this.monthAllowBlank,
33767 placeholder : this.monthPlaceholder,
33770 render : function (_self)
33772 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33773 e.preventDefault();
33777 select : function (_self, oldvalue, newvalue)
33779 _this.setValue(_this.getValue());
33784 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33786 this.yearField = new Roo.bootstrap.form.ComboBox({
33787 allowBlank : this.yearAllowBlank,
33788 alwaysQuery : true,
33789 displayField : 'value',
33792 forceSelection : true,
33794 placeholder : this.yearPlaceholder,
33795 selectOnFocus : true,
33796 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33797 triggerAction : 'all',
33799 valueField : 'value',
33800 store : new Roo.data.SimpleStore({
33801 data : (function() {
33803 _this.fireEvent('years', _this, years);
33806 fields : [ 'value' ]
33809 select : function (_self, record, index)
33811 _this.setValue(_this.getValue());
33816 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33819 setValue : function(v, format)
33821 this.inputEl.dom.value = v;
33823 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33825 var d = Date.parseDate(v, f);
33832 this.setDay(d.format(this.dayFormat));
33833 this.setMonth(d.format(this.monthFormat));
33834 this.setYear(d.format(this.yearFormat));
33841 setDay : function(v)
33843 this.dayField.setValue(v);
33844 this.inputEl.dom.value = this.getValue();
33849 setMonth : function(v)
33851 this.monthField.setValue(v, true);
33852 this.inputEl.dom.value = this.getValue();
33857 setYear : function(v)
33859 this.yearField.setValue(v);
33860 this.inputEl.dom.value = this.getValue();
33865 getDay : function()
33867 return this.dayField.getValue();
33870 getMonth : function()
33872 return this.monthField.getValue();
33875 getYear : function()
33877 return this.yearField.getValue();
33880 getValue : function()
33882 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33884 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33894 this.inputEl.dom.value = '';
33899 validate : function()
33901 var d = this.dayField.validate();
33902 var m = this.monthField.validate();
33903 var y = this.yearField.validate();
33908 (!this.dayAllowBlank && !d) ||
33909 (!this.monthAllowBlank && !m) ||
33910 (!this.yearAllowBlank && !y)
33915 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33924 this.markInvalid();
33929 markValid : function()
33932 var label = this.el.select('label', true).first();
33933 var icon = this.el.select('i.fa-star', true).first();
33939 this.fireEvent('valid', this);
33943 * Mark this field as invalid
33944 * @param {String} msg The validation message
33946 markInvalid : function(msg)
33949 var label = this.el.select('label', true).first();
33950 var icon = this.el.select('i.fa-star', true).first();
33952 if(label && !icon){
33953 this.el.select('.roo-date-split-field-label', true).createChild({
33955 cls : 'text-danger fa fa-lg fa-star',
33956 tooltip : 'This field is required',
33957 style : 'margin-right:5px;'
33961 this.fireEvent('invalid', this, msg);
33964 clearInvalid : function()
33966 var label = this.el.select('label', true).first();
33967 var icon = this.el.select('i.fa-star', true).first();
33973 this.fireEvent('valid', this);
33976 getName: function()
33986 * @class Roo.bootstrap.LayoutMasonry
33987 * @extends Roo.bootstrap.Component
33988 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
33989 * Bootstrap Layout Masonry class
33992 * http://masonry.desandro.com
33994 * The idea is to render all the bricks based on vertical width...
33996 * The original code extends 'outlayer' - we might need to use that....
33999 * Create a new Element
34000 * @param {Object} config The config object
34003 Roo.bootstrap.LayoutMasonry = function(config){
34005 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34009 Roo.bootstrap.LayoutMasonry.register(this);
34015 * Fire after layout the items
34016 * @param {Roo.bootstrap.LayoutMasonry} this
34017 * @param {Roo.EventObject} e
34024 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34027 * @cfg {Boolean} isLayoutInstant = no animation?
34029 isLayoutInstant : false, // needed?
34032 * @cfg {Number} boxWidth width of the columns
34037 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34042 * @cfg {Number} padWidth padding below box..
34047 * @cfg {Number} gutter gutter width..
34052 * @cfg {Number} maxCols maximum number of columns
34058 * @cfg {Boolean} isAutoInitial defalut true
34060 isAutoInitial : true,
34065 * @cfg {Boolean} isHorizontal defalut false
34067 isHorizontal : false,
34069 currentSize : null,
34075 bricks: null, //CompositeElement
34079 _isLayoutInited : false,
34081 // isAlternative : false, // only use for vertical layout...
34084 * @cfg {Number} alternativePadWidth padding below box..
34086 alternativePadWidth : 50,
34088 selectedBrick : [],
34090 getAutoCreate : function(){
34092 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34096 cls: 'blog-masonary-wrapper ' + this.cls,
34098 cls : 'mas-boxes masonary'
34105 getChildContainer: function( )
34107 if (this.boxesEl) {
34108 return this.boxesEl;
34111 this.boxesEl = this.el.select('.mas-boxes').first();
34113 return this.boxesEl;
34117 initEvents : function()
34121 if(this.isAutoInitial){
34122 Roo.log('hook children rendered');
34123 this.on('childrenrendered', function() {
34124 Roo.log('children rendered');
34130 initial : function()
34132 this.selectedBrick = [];
34134 this.currentSize = this.el.getBox(true);
34136 Roo.EventManager.onWindowResize(this.resize, this);
34138 if(!this.isAutoInitial){
34146 //this.layout.defer(500,this);
34150 resize : function()
34152 var cs = this.el.getBox(true);
34155 this.currentSize.width == cs.width &&
34156 this.currentSize.x == cs.x &&
34157 this.currentSize.height == cs.height &&
34158 this.currentSize.y == cs.y
34160 Roo.log("no change in with or X or Y");
34164 this.currentSize = cs;
34170 layout : function()
34172 this._resetLayout();
34174 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34176 this.layoutItems( isInstant );
34178 this._isLayoutInited = true;
34180 this.fireEvent('layout', this);
34184 _resetLayout : function()
34186 if(this.isHorizontal){
34187 this.horizontalMeasureColumns();
34191 this.verticalMeasureColumns();
34195 verticalMeasureColumns : function()
34197 this.getContainerWidth();
34199 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34200 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34204 var boxWidth = this.boxWidth + this.padWidth;
34206 if(this.containerWidth < this.boxWidth){
34207 boxWidth = this.containerWidth
34210 var containerWidth = this.containerWidth;
34212 var cols = Math.floor(containerWidth / boxWidth);
34214 this.cols = Math.max( cols, 1 );
34216 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34218 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34220 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34222 this.colWidth = boxWidth + avail - this.padWidth;
34224 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34225 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34228 horizontalMeasureColumns : function()
34230 this.getContainerWidth();
34232 var boxWidth = this.boxWidth;
34234 if(this.containerWidth < boxWidth){
34235 boxWidth = this.containerWidth;
34238 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34240 this.el.setHeight(boxWidth);
34244 getContainerWidth : function()
34246 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34249 layoutItems : function( isInstant )
34251 Roo.log(this.bricks);
34253 var items = Roo.apply([], this.bricks);
34255 if(this.isHorizontal){
34256 this._horizontalLayoutItems( items , isInstant );
34260 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34261 // this._verticalAlternativeLayoutItems( items , isInstant );
34265 this._verticalLayoutItems( items , isInstant );
34269 _verticalLayoutItems : function ( items , isInstant)
34271 if ( !items || !items.length ) {
34276 ['xs', 'xs', 'xs', 'tall'],
34277 ['xs', 'xs', 'tall'],
34278 ['xs', 'xs', 'sm'],
34279 ['xs', 'xs', 'xs'],
34285 ['sm', 'xs', 'xs'],
34289 ['tall', 'xs', 'xs', 'xs'],
34290 ['tall', 'xs', 'xs'],
34302 Roo.each(items, function(item, k){
34304 switch (item.size) {
34305 // these layouts take up a full box,
34316 boxes.push([item]);
34339 var filterPattern = function(box, length)
34347 var pattern = box.slice(0, length);
34351 Roo.each(pattern, function(i){
34352 format.push(i.size);
34355 Roo.each(standard, function(s){
34357 if(String(s) != String(format)){
34366 if(!match && length == 1){
34371 filterPattern(box, length - 1);
34375 queue.push(pattern);
34377 box = box.slice(length, box.length);
34379 filterPattern(box, 4);
34385 Roo.each(boxes, function(box, k){
34391 if(box.length == 1){
34396 filterPattern(box, 4);
34400 this._processVerticalLayoutQueue( queue, isInstant );
34404 // _verticalAlternativeLayoutItems : function( items , isInstant )
34406 // if ( !items || !items.length ) {
34410 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34414 _horizontalLayoutItems : function ( items , isInstant)
34416 if ( !items || !items.length || items.length < 3) {
34422 var eItems = items.slice(0, 3);
34424 items = items.slice(3, items.length);
34427 ['xs', 'xs', 'xs', 'wide'],
34428 ['xs', 'xs', 'wide'],
34429 ['xs', 'xs', 'sm'],
34430 ['xs', 'xs', 'xs'],
34436 ['sm', 'xs', 'xs'],
34440 ['wide', 'xs', 'xs', 'xs'],
34441 ['wide', 'xs', 'xs'],
34454 Roo.each(items, function(item, k){
34456 switch (item.size) {
34467 boxes.push([item]);
34491 var filterPattern = function(box, length)
34499 var pattern = box.slice(0, length);
34503 Roo.each(pattern, function(i){
34504 format.push(i.size);
34507 Roo.each(standard, function(s){
34509 if(String(s) != String(format)){
34518 if(!match && length == 1){
34523 filterPattern(box, length - 1);
34527 queue.push(pattern);
34529 box = box.slice(length, box.length);
34531 filterPattern(box, 4);
34537 Roo.each(boxes, function(box, k){
34543 if(box.length == 1){
34548 filterPattern(box, 4);
34555 var pos = this.el.getBox(true);
34559 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34561 var hit_end = false;
34563 Roo.each(queue, function(box){
34567 Roo.each(box, function(b){
34569 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34579 Roo.each(box, function(b){
34581 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34584 mx = Math.max(mx, b.x);
34588 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34592 Roo.each(box, function(b){
34594 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34608 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34611 /** Sets position of item in DOM
34612 * @param {Element} item
34613 * @param {Number} x - horizontal position
34614 * @param {Number} y - vertical position
34615 * @param {Boolean} isInstant - disables transitions
34617 _processVerticalLayoutQueue : function( queue, isInstant )
34619 var pos = this.el.getBox(true);
34624 for (var i = 0; i < this.cols; i++){
34628 Roo.each(queue, function(box, k){
34630 var col = k % this.cols;
34632 Roo.each(box, function(b,kk){
34634 b.el.position('absolute');
34636 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34637 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34639 if(b.size == 'md-left' || b.size == 'md-right'){
34640 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34641 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34644 b.el.setWidth(width);
34645 b.el.setHeight(height);
34647 b.el.select('iframe',true).setSize(width,height);
34651 for (var i = 0; i < this.cols; i++){
34653 if(maxY[i] < maxY[col]){
34658 col = Math.min(col, i);
34662 x = pos.x + col * (this.colWidth + this.padWidth);
34666 var positions = [];
34668 switch (box.length){
34670 positions = this.getVerticalOneBoxColPositions(x, y, box);
34673 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34676 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34679 positions = this.getVerticalFourBoxColPositions(x, y, box);
34685 Roo.each(box, function(b,kk){
34687 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34689 var sz = b.el.getSize();
34691 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34699 for (var i = 0; i < this.cols; i++){
34700 mY = Math.max(mY, maxY[i]);
34703 this.el.setHeight(mY - pos.y);
34707 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34709 // var pos = this.el.getBox(true);
34712 // var maxX = pos.right;
34714 // var maxHeight = 0;
34716 // Roo.each(items, function(item, k){
34720 // item.el.position('absolute');
34722 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34724 // item.el.setWidth(width);
34726 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34728 // item.el.setHeight(height);
34731 // item.el.setXY([x, y], isInstant ? false : true);
34733 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34736 // y = y + height + this.alternativePadWidth;
34738 // maxHeight = maxHeight + height + this.alternativePadWidth;
34742 // this.el.setHeight(maxHeight);
34746 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34748 var pos = this.el.getBox(true);
34753 var maxX = pos.right;
34755 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34757 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34759 Roo.each(queue, function(box, k){
34761 Roo.each(box, function(b, kk){
34763 b.el.position('absolute');
34765 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34766 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34768 if(b.size == 'md-left' || b.size == 'md-right'){
34769 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34770 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34773 b.el.setWidth(width);
34774 b.el.setHeight(height);
34782 var positions = [];
34784 switch (box.length){
34786 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34789 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34792 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34795 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34801 Roo.each(box, function(b,kk){
34803 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34805 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34813 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34815 Roo.each(eItems, function(b,k){
34817 b.size = (k == 0) ? 'sm' : 'xs';
34818 b.x = (k == 0) ? 2 : 1;
34819 b.y = (k == 0) ? 2 : 1;
34821 b.el.position('absolute');
34823 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34825 b.el.setWidth(width);
34827 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34829 b.el.setHeight(height);
34833 var positions = [];
34836 x : maxX - this.unitWidth * 2 - this.gutter,
34841 x : maxX - this.unitWidth,
34842 y : minY + (this.unitWidth + this.gutter) * 2
34846 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34850 Roo.each(eItems, function(b,k){
34852 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34858 getVerticalOneBoxColPositions : function(x, y, box)
34862 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34864 if(box[0].size == 'md-left'){
34868 if(box[0].size == 'md-right'){
34873 x : x + (this.unitWidth + this.gutter) * rand,
34880 getVerticalTwoBoxColPositions : function(x, y, box)
34884 if(box[0].size == 'xs'){
34888 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34892 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34906 x : x + (this.unitWidth + this.gutter) * 2,
34907 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34914 getVerticalThreeBoxColPositions : function(x, y, box)
34918 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34926 x : x + (this.unitWidth + this.gutter) * 1,
34931 x : x + (this.unitWidth + this.gutter) * 2,
34939 if(box[0].size == 'xs' && box[1].size == 'xs'){
34948 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34952 x : x + (this.unitWidth + this.gutter) * 1,
34966 x : x + (this.unitWidth + this.gutter) * 2,
34971 x : x + (this.unitWidth + this.gutter) * 2,
34972 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34979 getVerticalFourBoxColPositions : function(x, y, box)
34983 if(box[0].size == 'xs'){
34992 y : y + (this.unitHeight + this.gutter) * 1
34997 y : y + (this.unitHeight + this.gutter) * 2
35001 x : x + (this.unitWidth + this.gutter) * 1,
35015 x : x + (this.unitWidth + this.gutter) * 2,
35020 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35021 y : y + (this.unitHeight + this.gutter) * 1
35025 x : x + (this.unitWidth + this.gutter) * 2,
35026 y : y + (this.unitWidth + this.gutter) * 2
35033 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35037 if(box[0].size == 'md-left'){
35039 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35046 if(box[0].size == 'md-right'){
35048 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35049 y : minY + (this.unitWidth + this.gutter) * 1
35055 var rand = Math.floor(Math.random() * (4 - box[0].y));
35058 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35059 y : minY + (this.unitWidth + this.gutter) * rand
35066 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35070 if(box[0].size == 'xs'){
35073 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35078 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35079 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093 y : minY + (this.unitWidth + this.gutter) * 2
35100 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35104 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35107 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35112 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35113 y : minY + (this.unitWidth + this.gutter) * 1
35117 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35118 y : minY + (this.unitWidth + this.gutter) * 2
35125 if(box[0].size == 'xs' && box[1].size == 'xs'){
35128 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35133 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35138 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35139 y : minY + (this.unitWidth + this.gutter) * 1
35147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35152 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153 y : minY + (this.unitWidth + this.gutter) * 2
35157 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158 y : minY + (this.unitWidth + this.gutter) * 2
35165 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35169 if(box[0].size == 'xs'){
35172 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35177 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35182 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),
35187 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35188 y : minY + (this.unitWidth + this.gutter) * 1
35196 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35201 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35202 y : minY + (this.unitWidth + this.gutter) * 2
35206 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35207 y : minY + (this.unitWidth + this.gutter) * 2
35211 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),
35212 y : minY + (this.unitWidth + this.gutter) * 2
35220 * remove a Masonry Brick
35221 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35223 removeBrick : function(brick_id)
35229 for (var i = 0; i<this.bricks.length; i++) {
35230 if (this.bricks[i].id == brick_id) {
35231 this.bricks.splice(i,1);
35232 this.el.dom.removeChild(Roo.get(brick_id).dom);
35239 * adds a Masonry Brick
35240 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35242 addBrick : function(cfg)
35244 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35245 //this.register(cn);
35246 cn.parentId = this.id;
35247 cn.render(this.el);
35252 * register a Masonry Brick
35253 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35256 register : function(brick)
35258 this.bricks.push(brick);
35259 brick.masonryId = this.id;
35263 * clear all the Masonry Brick
35265 clearAll : function()
35268 //this.getChildContainer().dom.innerHTML = "";
35269 this.el.dom.innerHTML = '';
35272 getSelected : function()
35274 if (!this.selectedBrick) {
35278 return this.selectedBrick;
35282 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35286 * register a Masonry Layout
35287 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35290 register : function(layout)
35292 this.groups[layout.id] = layout;
35295 * fetch a Masonry Layout based on the masonry layout ID
35296 * @param {string} the masonry layout to add
35297 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35300 get: function(layout_id) {
35301 if (typeof(this.groups[layout_id]) == 'undefined') {
35304 return this.groups[layout_id] ;
35316 * http://masonry.desandro.com
35318 * The idea is to render all the bricks based on vertical width...
35320 * The original code extends 'outlayer' - we might need to use that....
35326 * @class Roo.bootstrap.LayoutMasonryAuto
35327 * @extends Roo.bootstrap.Component
35328 * Bootstrap Layout Masonry class
35331 * Create a new Element
35332 * @param {Object} config The config object
35335 Roo.bootstrap.LayoutMasonryAuto = function(config){
35336 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35339 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35342 * @cfg {Boolean} isFitWidth - resize the width..
35344 isFitWidth : false, // options..
35346 * @cfg {Boolean} isOriginLeft = left align?
35348 isOriginLeft : true,
35350 * @cfg {Boolean} isOriginTop = top align?
35352 isOriginTop : false,
35354 * @cfg {Boolean} isLayoutInstant = no animation?
35356 isLayoutInstant : false, // needed?
35358 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35360 isResizingContainer : true,
35362 * @cfg {Number} columnWidth width of the columns
35368 * @cfg {Number} maxCols maximum number of columns
35373 * @cfg {Number} padHeight padding below box..
35379 * @cfg {Boolean} isAutoInitial defalut true
35382 isAutoInitial : true,
35388 initialColumnWidth : 0,
35389 currentSize : null,
35391 colYs : null, // array.
35398 bricks: null, //CompositeElement
35399 cols : 0, // array?
35400 // element : null, // wrapped now this.el
35401 _isLayoutInited : null,
35404 getAutoCreate : function(){
35408 cls: 'blog-masonary-wrapper ' + this.cls,
35410 cls : 'mas-boxes masonary'
35417 getChildContainer: function( )
35419 if (this.boxesEl) {
35420 return this.boxesEl;
35423 this.boxesEl = this.el.select('.mas-boxes').first();
35425 return this.boxesEl;
35429 initEvents : function()
35433 if(this.isAutoInitial){
35434 Roo.log('hook children rendered');
35435 this.on('childrenrendered', function() {
35436 Roo.log('children rendered');
35443 initial : function()
35445 this.reloadItems();
35447 this.currentSize = this.el.getBox(true);
35449 /// was window resize... - let's see if this works..
35450 Roo.EventManager.onWindowResize(this.resize, this);
35452 if(!this.isAutoInitial){
35457 this.layout.defer(500,this);
35460 reloadItems: function()
35462 this.bricks = this.el.select('.masonry-brick', true);
35464 this.bricks.each(function(b) {
35465 //Roo.log(b.getSize());
35466 if (!b.attr('originalwidth')) {
35467 b.attr('originalwidth', b.getSize().width);
35472 Roo.log(this.bricks.elements.length);
35475 resize : function()
35478 var cs = this.el.getBox(true);
35480 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35481 Roo.log("no change in with or X");
35484 this.currentSize = cs;
35488 layout : function()
35491 this._resetLayout();
35492 //this._manageStamps();
35494 // don't animate first layout
35495 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35496 this.layoutItems( isInstant );
35498 // flag for initalized
35499 this._isLayoutInited = true;
35502 layoutItems : function( isInstant )
35504 //var items = this._getItemsForLayout( this.items );
35505 // original code supports filtering layout items.. we just ignore it..
35507 this._layoutItems( this.bricks , isInstant );
35509 this._postLayout();
35511 _layoutItems : function ( items , isInstant)
35513 //this.fireEvent( 'layout', this, items );
35516 if ( !items || !items.elements.length ) {
35517 // no items, emit event with empty array
35522 items.each(function(item) {
35523 Roo.log("layout item");
35525 // get x/y object from method
35526 var position = this._getItemLayoutPosition( item );
35528 position.item = item;
35529 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35530 queue.push( position );
35533 this._processLayoutQueue( queue );
35535 /** Sets position of item in DOM
35536 * @param {Element} item
35537 * @param {Number} x - horizontal position
35538 * @param {Number} y - vertical position
35539 * @param {Boolean} isInstant - disables transitions
35541 _processLayoutQueue : function( queue )
35543 for ( var i=0, len = queue.length; i < len; i++ ) {
35544 var obj = queue[i];
35545 obj.item.position('absolute');
35546 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35552 * Any logic you want to do after each layout,
35553 * i.e. size the container
35555 _postLayout : function()
35557 this.resizeContainer();
35560 resizeContainer : function()
35562 if ( !this.isResizingContainer ) {
35565 var size = this._getContainerSize();
35567 this.el.setSize(size.width,size.height);
35568 this.boxesEl.setSize(size.width,size.height);
35574 _resetLayout : function()
35576 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35577 this.colWidth = this.el.getWidth();
35578 //this.gutter = this.el.getWidth();
35580 this.measureColumns();
35586 this.colYs.push( 0 );
35592 measureColumns : function()
35594 this.getContainerWidth();
35595 // if columnWidth is 0, default to outerWidth of first item
35596 if ( !this.columnWidth ) {
35597 var firstItem = this.bricks.first();
35598 Roo.log(firstItem);
35599 this.columnWidth = this.containerWidth;
35600 if (firstItem && firstItem.attr('originalwidth') ) {
35601 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35603 // columnWidth fall back to item of first element
35604 Roo.log("set column width?");
35605 this.initialColumnWidth = this.columnWidth ;
35607 // if first elem has no width, default to size of container
35612 if (this.initialColumnWidth) {
35613 this.columnWidth = this.initialColumnWidth;
35618 // column width is fixed at the top - however if container width get's smaller we should
35621 // this bit calcs how man columns..
35623 var columnWidth = this.columnWidth += this.gutter;
35625 // calculate columns
35626 var containerWidth = this.containerWidth + this.gutter;
35628 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35629 // fix rounding errors, typically with gutters
35630 var excess = columnWidth - containerWidth % columnWidth;
35633 // if overshoot is less than a pixel, round up, otherwise floor it
35634 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35635 cols = Math[ mathMethod ]( cols );
35636 this.cols = Math.max( cols, 1 );
35637 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35639 // padding positioning..
35640 var totalColWidth = this.cols * this.columnWidth;
35641 var padavail = this.containerWidth - totalColWidth;
35642 // so for 2 columns - we need 3 'pads'
35644 var padNeeded = (1+this.cols) * this.padWidth;
35646 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35648 this.columnWidth += padExtra
35649 //this.padWidth = Math.floor(padavail / ( this.cols));
35651 // adjust colum width so that padding is fixed??
35653 // we have 3 columns ... total = width * 3
35654 // we have X left over... that should be used by
35656 //if (this.expandC) {
35664 getContainerWidth : function()
35666 /* // container is parent if fit width
35667 var container = this.isFitWidth ? this.element.parentNode : this.element;
35668 // check that this.size and size are there
35669 // IE8 triggers resize on body size change, so they might not be
35671 var size = getSize( container ); //FIXME
35672 this.containerWidth = size && size.innerWidth; //FIXME
35675 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35679 _getItemLayoutPosition : function( item ) // what is item?
35681 // we resize the item to our columnWidth..
35683 item.setWidth(this.columnWidth);
35684 item.autoBoxAdjust = false;
35686 var sz = item.getSize();
35688 // how many columns does this brick span
35689 var remainder = this.containerWidth % this.columnWidth;
35691 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35692 // round if off by 1 pixel, otherwise use ceil
35693 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35694 colSpan = Math.min( colSpan, this.cols );
35696 // normally this should be '1' as we dont' currently allow multi width columns..
35698 var colGroup = this._getColGroup( colSpan );
35699 // get the minimum Y value from the columns
35700 var minimumY = Math.min.apply( Math, colGroup );
35701 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35703 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35705 // position the brick
35707 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35708 y: this.currentSize.y + minimumY + this.padHeight
35712 // apply setHeight to necessary columns
35713 var setHeight = minimumY + sz.height + this.padHeight;
35714 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35716 var setSpan = this.cols + 1 - colGroup.length;
35717 for ( var i = 0; i < setSpan; i++ ) {
35718 this.colYs[ shortColIndex + i ] = setHeight ;
35725 * @param {Number} colSpan - number of columns the element spans
35726 * @returns {Array} colGroup
35728 _getColGroup : function( colSpan )
35730 if ( colSpan < 2 ) {
35731 // if brick spans only one column, use all the column Ys
35736 // how many different places could this brick fit horizontally
35737 var groupCount = this.cols + 1 - colSpan;
35738 // for each group potential horizontal position
35739 for ( var i = 0; i < groupCount; i++ ) {
35740 // make an array of colY values for that one group
35741 var groupColYs = this.colYs.slice( i, i + colSpan );
35742 // and get the max value of the array
35743 colGroup[i] = Math.max.apply( Math, groupColYs );
35748 _manageStamp : function( stamp )
35750 var stampSize = stamp.getSize();
35751 var offset = stamp.getBox();
35752 // get the columns that this stamp affects
35753 var firstX = this.isOriginLeft ? offset.x : offset.right;
35754 var lastX = firstX + stampSize.width;
35755 var firstCol = Math.floor( firstX / this.columnWidth );
35756 firstCol = Math.max( 0, firstCol );
35758 var lastCol = Math.floor( lastX / this.columnWidth );
35759 // lastCol should not go over if multiple of columnWidth #425
35760 lastCol -= lastX % this.columnWidth ? 0 : 1;
35761 lastCol = Math.min( this.cols - 1, lastCol );
35763 // set colYs to bottom of the stamp
35764 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35767 for ( var i = firstCol; i <= lastCol; i++ ) {
35768 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35773 _getContainerSize : function()
35775 this.maxY = Math.max.apply( Math, this.colYs );
35780 if ( this.isFitWidth ) {
35781 size.width = this._getContainerFitWidth();
35787 _getContainerFitWidth : function()
35789 var unusedCols = 0;
35790 // count unused columns
35793 if ( this.colYs[i] !== 0 ) {
35798 // fit container to columns that have been used
35799 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35802 needsResizeLayout : function()
35804 var previousWidth = this.containerWidth;
35805 this.getContainerWidth();
35806 return previousWidth !== this.containerWidth;
35821 * @class Roo.bootstrap.MasonryBrick
35822 * @extends Roo.bootstrap.Component
35823 * Bootstrap MasonryBrick class
35826 * Create a new MasonryBrick
35827 * @param {Object} config The config object
35830 Roo.bootstrap.MasonryBrick = function(config){
35832 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35834 Roo.bootstrap.MasonryBrick.register(this);
35840 * When a MasonryBrick is clcik
35841 * @param {Roo.bootstrap.MasonryBrick} this
35842 * @param {Roo.EventObject} e
35848 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35851 * @cfg {String} title
35855 * @cfg {String} html
35859 * @cfg {String} bgimage
35863 * @cfg {String} videourl
35867 * @cfg {String} cls
35871 * @cfg {String} href
35875 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35880 * @cfg {String} placetitle (center|bottom)
35885 * @cfg {Boolean} isFitContainer defalut true
35887 isFitContainer : true,
35890 * @cfg {Boolean} preventDefault defalut false
35892 preventDefault : false,
35895 * @cfg {Boolean} inverse defalut false
35897 maskInverse : false,
35899 getAutoCreate : function()
35901 if(!this.isFitContainer){
35902 return this.getSplitAutoCreate();
35905 var cls = 'masonry-brick masonry-brick-full';
35907 if(this.href.length){
35908 cls += ' masonry-brick-link';
35911 if(this.bgimage.length){
35912 cls += ' masonry-brick-image';
35915 if(this.maskInverse){
35916 cls += ' mask-inverse';
35919 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35920 cls += ' enable-mask';
35924 cls += ' masonry-' + this.size + '-brick';
35927 if(this.placetitle.length){
35929 switch (this.placetitle) {
35931 cls += ' masonry-center-title';
35934 cls += ' masonry-bottom-title';
35941 if(!this.html.length && !this.bgimage.length){
35942 cls += ' masonry-center-title';
35945 if(!this.html.length && this.bgimage.length){
35946 cls += ' masonry-bottom-title';
35951 cls += ' ' + this.cls;
35955 tag: (this.href.length) ? 'a' : 'div',
35960 cls: 'masonry-brick-mask'
35964 cls: 'masonry-brick-paragraph',
35970 if(this.href.length){
35971 cfg.href = this.href;
35974 var cn = cfg.cn[1].cn;
35976 if(this.title.length){
35979 cls: 'masonry-brick-title',
35984 if(this.html.length){
35987 cls: 'masonry-brick-text',
35992 if (!this.title.length && !this.html.length) {
35993 cfg.cn[1].cls += ' hide';
35996 if(this.bgimage.length){
35999 cls: 'masonry-brick-image-view',
36004 if(this.videourl.length){
36005 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36006 // youtube support only?
36009 cls: 'masonry-brick-image-view',
36012 allowfullscreen : true
36020 getSplitAutoCreate : function()
36022 var cls = 'masonry-brick masonry-brick-split';
36024 if(this.href.length){
36025 cls += ' masonry-brick-link';
36028 if(this.bgimage.length){
36029 cls += ' masonry-brick-image';
36033 cls += ' masonry-' + this.size + '-brick';
36036 switch (this.placetitle) {
36038 cls += ' masonry-center-title';
36041 cls += ' masonry-bottom-title';
36044 if(!this.bgimage.length){
36045 cls += ' masonry-center-title';
36048 if(this.bgimage.length){
36049 cls += ' masonry-bottom-title';
36055 cls += ' ' + this.cls;
36059 tag: (this.href.length) ? 'a' : 'div',
36064 cls: 'masonry-brick-split-head',
36068 cls: 'masonry-brick-paragraph',
36075 cls: 'masonry-brick-split-body',
36081 if(this.href.length){
36082 cfg.href = this.href;
36085 if(this.title.length){
36086 cfg.cn[0].cn[0].cn.push({
36088 cls: 'masonry-brick-title',
36093 if(this.html.length){
36094 cfg.cn[1].cn.push({
36096 cls: 'masonry-brick-text',
36101 if(this.bgimage.length){
36102 cfg.cn[0].cn.push({
36104 cls: 'masonry-brick-image-view',
36109 if(this.videourl.length){
36110 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36111 // youtube support only?
36112 cfg.cn[0].cn.cn.push({
36114 cls: 'masonry-brick-image-view',
36117 allowfullscreen : true
36124 initEvents: function()
36126 switch (this.size) {
36159 this.el.on('touchstart', this.onTouchStart, this);
36160 this.el.on('touchmove', this.onTouchMove, this);
36161 this.el.on('touchend', this.onTouchEnd, this);
36162 this.el.on('contextmenu', this.onContextMenu, this);
36164 this.el.on('mouseenter' ,this.enter, this);
36165 this.el.on('mouseleave', this.leave, this);
36166 this.el.on('click', this.onClick, this);
36169 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36170 this.parent().bricks.push(this);
36175 onClick: function(e, el)
36177 var time = this.endTimer - this.startTimer;
36178 // Roo.log(e.preventDefault());
36181 e.preventDefault();
36186 if(!this.preventDefault){
36190 e.preventDefault();
36192 if (this.activeClass != '') {
36193 this.selectBrick();
36196 this.fireEvent('click', this, e);
36199 enter: function(e, el)
36201 e.preventDefault();
36203 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36207 if(this.bgimage.length && this.html.length){
36208 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36212 leave: function(e, el)
36214 e.preventDefault();
36216 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36220 if(this.bgimage.length && this.html.length){
36221 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36225 onTouchStart: function(e, el)
36227 // e.preventDefault();
36229 this.touchmoved = false;
36231 if(!this.isFitContainer){
36235 if(!this.bgimage.length || !this.html.length){
36239 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36241 this.timer = new Date().getTime();
36245 onTouchMove: function(e, el)
36247 this.touchmoved = true;
36250 onContextMenu : function(e,el)
36252 e.preventDefault();
36253 e.stopPropagation();
36257 onTouchEnd: function(e, el)
36259 // e.preventDefault();
36261 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36268 if(!this.bgimage.length || !this.html.length){
36270 if(this.href.length){
36271 window.location.href = this.href;
36277 if(!this.isFitContainer){
36281 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36283 window.location.href = this.href;
36286 //selection on single brick only
36287 selectBrick : function() {
36289 if (!this.parentId) {
36293 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36294 var index = m.selectedBrick.indexOf(this.id);
36297 m.selectedBrick.splice(index,1);
36298 this.el.removeClass(this.activeClass);
36302 for(var i = 0; i < m.selectedBrick.length; i++) {
36303 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36304 b.el.removeClass(b.activeClass);
36307 m.selectedBrick = [];
36309 m.selectedBrick.push(this.id);
36310 this.el.addClass(this.activeClass);
36314 isSelected : function(){
36315 return this.el.hasClass(this.activeClass);
36320 Roo.apply(Roo.bootstrap.MasonryBrick, {
36323 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36325 * register a Masonry Brick
36326 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36329 register : function(brick)
36331 //this.groups[brick.id] = brick;
36332 this.groups.add(brick.id, brick);
36335 * fetch a masonry brick based on the masonry brick ID
36336 * @param {string} the masonry brick to add
36337 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36340 get: function(brick_id)
36342 // if (typeof(this.groups[brick_id]) == 'undefined') {
36345 // return this.groups[brick_id] ;
36347 if(this.groups.key(brick_id)) {
36348 return this.groups.key(brick_id);
36366 * @class Roo.bootstrap.Brick
36367 * @extends Roo.bootstrap.Component
36368 * Bootstrap Brick class
36371 * Create a new Brick
36372 * @param {Object} config The config object
36375 Roo.bootstrap.Brick = function(config){
36376 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36382 * When a Brick is click
36383 * @param {Roo.bootstrap.Brick} this
36384 * @param {Roo.EventObject} e
36390 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36393 * @cfg {String} title
36397 * @cfg {String} html
36401 * @cfg {String} bgimage
36405 * @cfg {String} cls
36409 * @cfg {String} href
36413 * @cfg {String} video
36417 * @cfg {Boolean} square
36421 getAutoCreate : function()
36423 var cls = 'roo-brick';
36425 if(this.href.length){
36426 cls += ' roo-brick-link';
36429 if(this.bgimage.length){
36430 cls += ' roo-brick-image';
36433 if(!this.html.length && !this.bgimage.length){
36434 cls += ' roo-brick-center-title';
36437 if(!this.html.length && this.bgimage.length){
36438 cls += ' roo-brick-bottom-title';
36442 cls += ' ' + this.cls;
36446 tag: (this.href.length) ? 'a' : 'div',
36451 cls: 'roo-brick-paragraph',
36457 if(this.href.length){
36458 cfg.href = this.href;
36461 var cn = cfg.cn[0].cn;
36463 if(this.title.length){
36466 cls: 'roo-brick-title',
36471 if(this.html.length){
36474 cls: 'roo-brick-text',
36481 if(this.bgimage.length){
36484 cls: 'roo-brick-image-view',
36492 initEvents: function()
36494 if(this.title.length || this.html.length){
36495 this.el.on('mouseenter' ,this.enter, this);
36496 this.el.on('mouseleave', this.leave, this);
36499 Roo.EventManager.onWindowResize(this.resize, this);
36501 if(this.bgimage.length){
36502 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36503 this.imageEl.on('load', this.onImageLoad, this);
36510 onImageLoad : function()
36515 resize : function()
36517 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36519 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36521 if(this.bgimage.length){
36522 var image = this.el.select('.roo-brick-image-view', true).first();
36524 image.setWidth(paragraph.getWidth());
36527 image.setHeight(paragraph.getWidth());
36530 this.el.setHeight(image.getHeight());
36531 paragraph.setHeight(image.getHeight());
36537 enter: function(e, el)
36539 e.preventDefault();
36541 if(this.bgimage.length){
36542 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36543 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36547 leave: function(e, el)
36549 e.preventDefault();
36551 if(this.bgimage.length){
36552 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36553 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36568 * @class Roo.bootstrap.form.NumberField
36569 * @extends Roo.bootstrap.form.Input
36570 * Bootstrap NumberField class
36576 * Create a new NumberField
36577 * @param {Object} config The config object
36580 Roo.bootstrap.form.NumberField = function(config){
36581 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36584 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36587 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36589 allowDecimals : true,
36591 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36593 decimalSeparator : ".",
36595 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36597 decimalPrecision : 2,
36599 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36601 allowNegative : true,
36604 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36608 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36610 minValue : Number.NEGATIVE_INFINITY,
36612 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36614 maxValue : Number.MAX_VALUE,
36616 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36618 minText : "The minimum value for this field is {0}",
36620 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36622 maxText : "The maximum value for this field is {0}",
36624 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36625 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36627 nanText : "{0} is not a valid number",
36629 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36631 thousandsDelimiter : false,
36633 * @cfg {String} valueAlign alignment of value
36635 valueAlign : "left",
36637 getAutoCreate : function()
36639 var hiddenInput = {
36643 cls: 'hidden-number-input'
36647 hiddenInput.name = this.name;
36652 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36654 this.name = hiddenInput.name;
36656 if(cfg.cn.length > 0) {
36657 cfg.cn.push(hiddenInput);
36664 initEvents : function()
36666 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36668 var allowed = "0123456789";
36670 if(this.allowDecimals){
36671 allowed += this.decimalSeparator;
36674 if(this.allowNegative){
36678 if(this.thousandsDelimiter) {
36682 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36684 var keyPress = function(e){
36686 var k = e.getKey();
36688 var c = e.getCharCode();
36691 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36692 allowed.indexOf(String.fromCharCode(c)) === -1
36698 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36702 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36707 this.el.on("keypress", keyPress, this);
36710 validateValue : function(value)
36713 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36717 var num = this.parseValue(value);
36720 this.markInvalid(String.format(this.nanText, value));
36724 if(num < this.minValue){
36725 this.markInvalid(String.format(this.minText, this.minValue));
36729 if(num > this.maxValue){
36730 this.markInvalid(String.format(this.maxText, this.maxValue));
36737 getValue : function()
36739 var v = this.hiddenEl().getValue();
36741 return this.fixPrecision(this.parseValue(v));
36744 parseValue : function(value)
36746 if(this.thousandsDelimiter) {
36748 r = new RegExp(",", "g");
36749 value = value.replace(r, "");
36752 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36753 return isNaN(value) ? '' : value;
36756 fixPrecision : function(value)
36758 if(this.thousandsDelimiter) {
36760 r = new RegExp(",", "g");
36761 value = value.replace(r, "");
36764 var nan = isNaN(value);
36766 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36767 return nan ? '' : value;
36769 return parseFloat(value).toFixed(this.decimalPrecision);
36772 setValue : function(v)
36774 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36780 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36782 this.inputEl().dom.value = (v == '') ? '' :
36783 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36785 if(!this.allowZero && v === '0') {
36786 this.hiddenEl().dom.value = '';
36787 this.inputEl().dom.value = '';
36794 decimalPrecisionFcn : function(v)
36796 return Math.floor(v);
36799 beforeBlur : function()
36801 var v = this.parseValue(this.getRawValue());
36803 if(v || v === 0 || v === ''){
36808 hiddenEl : function()
36810 return this.el.select('input.hidden-number-input',true).first();
36822 * @class Roo.bootstrap.DocumentSlider
36823 * @extends Roo.bootstrap.Component
36824 * Bootstrap DocumentSlider class
36827 * Create a new DocumentViewer
36828 * @param {Object} config The config object
36831 Roo.bootstrap.DocumentSlider = function(config){
36832 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36839 * Fire after initEvent
36840 * @param {Roo.bootstrap.DocumentSlider} this
36845 * Fire after update
36846 * @param {Roo.bootstrap.DocumentSlider} this
36852 * @param {Roo.bootstrap.DocumentSlider} this
36858 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36864 getAutoCreate : function()
36868 cls : 'roo-document-slider',
36872 cls : 'roo-document-slider-header',
36876 cls : 'roo-document-slider-header-title'
36882 cls : 'roo-document-slider-body',
36886 cls : 'roo-document-slider-prev',
36890 cls : 'fa fa-chevron-left'
36896 cls : 'roo-document-slider-thumb',
36900 cls : 'roo-document-slider-image'
36906 cls : 'roo-document-slider-next',
36910 cls : 'fa fa-chevron-right'
36922 initEvents : function()
36924 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36925 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36927 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36928 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36930 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36931 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36933 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36934 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36936 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36937 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36939 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36940 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36942 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36943 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36945 this.thumbEl.on('click', this.onClick, this);
36947 this.prevIndicator.on('click', this.prev, this);
36949 this.nextIndicator.on('click', this.next, this);
36953 initial : function()
36955 if(this.files.length){
36956 this.indicator = 1;
36960 this.fireEvent('initial', this);
36963 update : function()
36965 this.imageEl.attr('src', this.files[this.indicator - 1]);
36967 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36969 this.prevIndicator.show();
36971 if(this.indicator == 1){
36972 this.prevIndicator.hide();
36975 this.nextIndicator.show();
36977 if(this.indicator == this.files.length){
36978 this.nextIndicator.hide();
36981 this.thumbEl.scrollTo('top');
36983 this.fireEvent('update', this);
36986 onClick : function(e)
36988 e.preventDefault();
36990 this.fireEvent('click', this);
36995 e.preventDefault();
36997 this.indicator = Math.max(1, this.indicator - 1);
37004 e.preventDefault();
37006 this.indicator = Math.min(this.files.length, this.indicator + 1);
37020 * @class Roo.bootstrap.form.RadioSet
37021 * @extends Roo.bootstrap.form.Input
37022 * @children Roo.bootstrap.form.Radio
37023 * Bootstrap RadioSet class
37024 * @cfg {String} indicatorpos (left|right) default left
37025 * @cfg {Boolean} inline (true|false) inline the element (default true)
37026 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37028 * Create a new RadioSet
37029 * @param {Object} config The config object
37032 Roo.bootstrap.form.RadioSet = function(config){
37034 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37038 Roo.bootstrap.form.RadioSet.register(this);
37043 * Fires when the element is checked or unchecked.
37044 * @param {Roo.bootstrap.form.RadioSet} this This radio
37045 * @param {Roo.bootstrap.form.Radio} item The checked item
37050 * Fires when the element is click.
37051 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37052 * @param {Roo.bootstrap.form.Radio} item The checked item
37053 * @param {Roo.EventObject} e The event object
37060 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37068 indicatorpos : 'left',
37070 getAutoCreate : function()
37074 cls : 'roo-radio-set-label',
37078 html : this.fieldLabel
37082 if (Roo.bootstrap.version == 3) {
37085 if(this.indicatorpos == 'left'){
37088 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37089 tooltip : 'This field is required'
37094 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37095 tooltip : 'This field is required'
37101 cls : 'roo-radio-set-items'
37104 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37106 if (align === 'left' && this.fieldLabel.length) {
37109 cls : "roo-radio-set-right",
37115 if(this.labelWidth > 12){
37116 label.style = "width: " + this.labelWidth + 'px';
37119 if(this.labelWidth < 13 && this.labelmd == 0){
37120 this.labelmd = this.labelWidth;
37123 if(this.labellg > 0){
37124 label.cls += ' col-lg-' + this.labellg;
37125 items.cls += ' col-lg-' + (12 - this.labellg);
37128 if(this.labelmd > 0){
37129 label.cls += ' col-md-' + this.labelmd;
37130 items.cls += ' col-md-' + (12 - this.labelmd);
37133 if(this.labelsm > 0){
37134 label.cls += ' col-sm-' + this.labelsm;
37135 items.cls += ' col-sm-' + (12 - this.labelsm);
37138 if(this.labelxs > 0){
37139 label.cls += ' col-xs-' + this.labelxs;
37140 items.cls += ' col-xs-' + (12 - this.labelxs);
37146 cls : 'roo-radio-set',
37150 cls : 'roo-radio-set-input',
37153 value : this.value ? this.value : ''
37160 if(this.weight.length){
37161 cfg.cls += ' roo-radio-' + this.weight;
37165 cfg.cls += ' roo-radio-set-inline';
37169 ['xs','sm','md','lg'].map(function(size){
37170 if (settings[size]) {
37171 cfg.cls += ' col-' + size + '-' + settings[size];
37179 initEvents : function()
37181 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37182 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37184 if(!this.fieldLabel.length){
37185 this.labelEl.hide();
37188 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37189 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37191 this.indicator = this.indicatorEl();
37193 if(this.indicator){
37194 this.indicator.addClass('invisible');
37197 this.originalValue = this.getValue();
37201 inputEl: function ()
37203 return this.el.select('.roo-radio-set-input', true).first();
37206 getChildContainer : function()
37208 return this.itemsEl;
37211 register : function(item)
37213 this.radioes.push(item);
37217 validate : function()
37219 if(this.getVisibilityEl().hasClass('hidden')){
37225 Roo.each(this.radioes, function(i){
37234 if(this.allowBlank) {
37238 if(this.disabled || valid){
37243 this.markInvalid();
37248 markValid : function()
37250 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37251 this.indicatorEl().removeClass('visible');
37252 this.indicatorEl().addClass('invisible');
37256 if (Roo.bootstrap.version == 3) {
37257 this.el.removeClass([this.invalidClass, this.validClass]);
37258 this.el.addClass(this.validClass);
37260 this.el.removeClass(['is-invalid','is-valid']);
37261 this.el.addClass(['is-valid']);
37263 this.fireEvent('valid', this);
37266 markInvalid : function(msg)
37268 if(this.allowBlank || this.disabled){
37272 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37273 this.indicatorEl().removeClass('invisible');
37274 this.indicatorEl().addClass('visible');
37276 if (Roo.bootstrap.version == 3) {
37277 this.el.removeClass([this.invalidClass, this.validClass]);
37278 this.el.addClass(this.invalidClass);
37280 this.el.removeClass(['is-invalid','is-valid']);
37281 this.el.addClass(['is-invalid']);
37284 this.fireEvent('invalid', this, msg);
37288 setValue : function(v, suppressEvent)
37290 if(this.value === v){
37297 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37300 Roo.each(this.radioes, function(i){
37302 i.el.removeClass('checked');
37305 Roo.each(this.radioes, function(i){
37307 if(i.value === v || i.value.toString() === v.toString()){
37309 i.el.addClass('checked');
37311 if(suppressEvent !== true){
37312 this.fireEvent('check', this, i);
37323 clearInvalid : function(){
37325 if(!this.el || this.preventMark){
37329 this.el.removeClass([this.invalidClass]);
37331 this.fireEvent('valid', this);
37336 Roo.apply(Roo.bootstrap.form.RadioSet, {
37340 register : function(set)
37342 this.groups[set.name] = set;
37345 get: function(name)
37347 if (typeof(this.groups[name]) == 'undefined') {
37351 return this.groups[name] ;
37357 * Ext JS Library 1.1.1
37358 * Copyright(c) 2006-2007, Ext JS, LLC.
37360 * Originally Released Under LGPL - original licence link has changed is not relivant.
37363 * <script type="text/javascript">
37368 * @class Roo.bootstrap.SplitBar
37369 * @extends Roo.util.Observable
37370 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37374 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37375 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37376 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37377 split.minSize = 100;
37378 split.maxSize = 600;
37379 split.animate = true;
37380 split.on('moved', splitterMoved);
37383 * Create a new SplitBar
37384 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37385 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37386 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37387 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37388 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37389 position of the SplitBar).
37391 Roo.bootstrap.SplitBar = function(cfg){
37396 // dragElement : elm
37397 // resizingElement: el,
37399 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37400 // placement : Roo.bootstrap.SplitBar.LEFT ,
37401 // existingProxy ???
37404 this.el = Roo.get(cfg.dragElement, true);
37405 this.el.dom.unselectable = "on";
37407 this.resizingEl = Roo.get(cfg.resizingElement, true);
37411 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37412 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37415 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37418 * The minimum size of the resizing element. (Defaults to 0)
37424 * The maximum size of the resizing element. (Defaults to 2000)
37427 this.maxSize = 2000;
37430 * Whether to animate the transition to the new size
37433 this.animate = false;
37436 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37439 this.useShim = false;
37444 if(!cfg.existingProxy){
37446 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37448 this.proxy = Roo.get(cfg.existingProxy).dom;
37451 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37454 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37457 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37460 this.dragSpecs = {};
37463 * @private The adapter to use to positon and resize elements
37465 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37466 this.adapter.init(this);
37468 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37470 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37471 this.el.addClass("roo-splitbar-h");
37474 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37475 this.el.addClass("roo-splitbar-v");
37481 * Fires when the splitter is moved (alias for {@link #event-moved})
37482 * @param {Roo.bootstrap.SplitBar} this
37483 * @param {Number} newSize the new width or height
37488 * Fires when the splitter is moved
37489 * @param {Roo.bootstrap.SplitBar} this
37490 * @param {Number} newSize the new width or height
37494 * @event beforeresize
37495 * Fires before the splitter is dragged
37496 * @param {Roo.bootstrap.SplitBar} this
37498 "beforeresize" : true,
37500 "beforeapply" : true
37503 Roo.util.Observable.call(this);
37506 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37507 onStartProxyDrag : function(x, y){
37508 this.fireEvent("beforeresize", this);
37510 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37512 o.enableDisplayMode("block");
37513 // all splitbars share the same overlay
37514 Roo.bootstrap.SplitBar.prototype.overlay = o;
37516 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37517 this.overlay.show();
37518 Roo.get(this.proxy).setDisplayed("block");
37519 var size = this.adapter.getElementSize(this);
37520 this.activeMinSize = this.getMinimumSize();;
37521 this.activeMaxSize = this.getMaximumSize();;
37522 var c1 = size - this.activeMinSize;
37523 var c2 = Math.max(this.activeMaxSize - size, 0);
37524 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37525 this.dd.resetConstraints();
37526 this.dd.setXConstraint(
37527 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37528 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37530 this.dd.setYConstraint(0, 0);
37532 this.dd.resetConstraints();
37533 this.dd.setXConstraint(0, 0);
37534 this.dd.setYConstraint(
37535 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37536 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37539 this.dragSpecs.startSize = size;
37540 this.dragSpecs.startPoint = [x, y];
37541 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37545 * @private Called after the drag operation by the DDProxy
37547 onEndProxyDrag : function(e){
37548 Roo.get(this.proxy).setDisplayed(false);
37549 var endPoint = Roo.lib.Event.getXY(e);
37551 this.overlay.hide();
37554 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37555 newSize = this.dragSpecs.startSize +
37556 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37557 endPoint[0] - this.dragSpecs.startPoint[0] :
37558 this.dragSpecs.startPoint[0] - endPoint[0]
37561 newSize = this.dragSpecs.startSize +
37562 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37563 endPoint[1] - this.dragSpecs.startPoint[1] :
37564 this.dragSpecs.startPoint[1] - endPoint[1]
37567 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37568 if(newSize != this.dragSpecs.startSize){
37569 if(this.fireEvent('beforeapply', this, newSize) !== false){
37570 this.adapter.setElementSize(this, newSize);
37571 this.fireEvent("moved", this, newSize);
37572 this.fireEvent("resize", this, newSize);
37578 * Get the adapter this SplitBar uses
37579 * @return The adapter object
37581 getAdapter : function(){
37582 return this.adapter;
37586 * Set the adapter this SplitBar uses
37587 * @param {Object} adapter A SplitBar adapter object
37589 setAdapter : function(adapter){
37590 this.adapter = adapter;
37591 this.adapter.init(this);
37595 * Gets the minimum size for the resizing element
37596 * @return {Number} The minimum size
37598 getMinimumSize : function(){
37599 return this.minSize;
37603 * Sets the minimum size for the resizing element
37604 * @param {Number} minSize The minimum size
37606 setMinimumSize : function(minSize){
37607 this.minSize = minSize;
37611 * Gets the maximum size for the resizing element
37612 * @return {Number} The maximum size
37614 getMaximumSize : function(){
37615 return this.maxSize;
37619 * Sets the maximum size for the resizing element
37620 * @param {Number} maxSize The maximum size
37622 setMaximumSize : function(maxSize){
37623 this.maxSize = maxSize;
37627 * Sets the initialize size for the resizing element
37628 * @param {Number} size The initial size
37630 setCurrentSize : function(size){
37631 var oldAnimate = this.animate;
37632 this.animate = false;
37633 this.adapter.setElementSize(this, size);
37634 this.animate = oldAnimate;
37638 * Destroy this splitbar.
37639 * @param {Boolean} removeEl True to remove the element
37641 destroy : function(removeEl){
37643 this.shim.remove();
37646 this.proxy.parentNode.removeChild(this.proxy);
37654 * @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.
37656 Roo.bootstrap.SplitBar.createProxy = function(dir){
37657 var proxy = new Roo.Element(document.createElement("div"));
37658 proxy.unselectable();
37659 var cls = 'roo-splitbar-proxy';
37660 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37661 document.body.appendChild(proxy.dom);
37666 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37667 * Default Adapter. It assumes the splitter and resizing element are not positioned
37668 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37670 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37673 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37674 // do nothing for now
37675 init : function(s){
37679 * Called before drag operations to get the current size of the resizing element.
37680 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37682 getElementSize : function(s){
37683 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37684 return s.resizingEl.getWidth();
37686 return s.resizingEl.getHeight();
37691 * Called after drag operations to set the size of the resizing element.
37692 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37693 * @param {Number} newSize The new size to set
37694 * @param {Function} onComplete A function to be invoked when resizing is complete
37696 setElementSize : function(s, newSize, onComplete){
37697 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37699 s.resizingEl.setWidth(newSize);
37701 onComplete(s, newSize);
37704 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37709 s.resizingEl.setHeight(newSize);
37711 onComplete(s, newSize);
37714 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37721 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37722 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37723 * Adapter that moves the splitter element to align with the resized sizing element.
37724 * Used with an absolute positioned SplitBar.
37725 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37726 * document.body, make sure you assign an id to the body element.
37728 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37729 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37730 this.container = Roo.get(container);
37733 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37734 init : function(s){
37735 this.basic.init(s);
37738 getElementSize : function(s){
37739 return this.basic.getElementSize(s);
37742 setElementSize : function(s, newSize, onComplete){
37743 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37746 moveSplitter : function(s){
37747 var yes = Roo.bootstrap.SplitBar;
37748 switch(s.placement){
37750 s.el.setX(s.resizingEl.getRight());
37753 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37756 s.el.setY(s.resizingEl.getBottom());
37759 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37766 * Orientation constant - Create a vertical SplitBar
37770 Roo.bootstrap.SplitBar.VERTICAL = 1;
37773 * Orientation constant - Create a horizontal SplitBar
37777 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37780 * Placement constant - The resizing element is to the left of the splitter element
37784 Roo.bootstrap.SplitBar.LEFT = 1;
37787 * Placement constant - The resizing element is to the right of the splitter element
37791 Roo.bootstrap.SplitBar.RIGHT = 2;
37794 * Placement constant - The resizing element is positioned above the splitter element
37798 Roo.bootstrap.SplitBar.TOP = 3;
37801 * Placement constant - The resizing element is positioned under splitter element
37805 Roo.bootstrap.SplitBar.BOTTOM = 4;
37808 * Ext JS Library 1.1.1
37809 * Copyright(c) 2006-2007, Ext JS, LLC.
37811 * Originally Released Under LGPL - original licence link has changed is not relivant.
37814 * <script type="text/javascript">
37818 * @class Roo.bootstrap.layout.Manager
37819 * @extends Roo.bootstrap.Component
37821 * Base class for layout managers.
37823 Roo.bootstrap.layout.Manager = function(config)
37825 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37831 /** false to disable window resize monitoring @type Boolean */
37832 this.monitorWindowResize = true;
37837 * Fires when a layout is performed.
37838 * @param {Roo.LayoutManager} this
37842 * @event regionresized
37843 * Fires when the user resizes a region.
37844 * @param {Roo.LayoutRegion} region The resized region
37845 * @param {Number} newSize The new size (width for east/west, height for north/south)
37847 "regionresized" : true,
37849 * @event regioncollapsed
37850 * Fires when a region is collapsed.
37851 * @param {Roo.LayoutRegion} region The collapsed region
37853 "regioncollapsed" : true,
37855 * @event regionexpanded
37856 * Fires when a region is expanded.
37857 * @param {Roo.LayoutRegion} region The expanded region
37859 "regionexpanded" : true
37861 this.updating = false;
37864 this.el = Roo.get(config.el);
37870 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37875 monitorWindowResize : true,
37881 onRender : function(ct, position)
37884 this.el = Roo.get(ct);
37887 //this.fireEvent('render',this);
37891 initEvents: function()
37895 // ie scrollbar fix
37896 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37897 document.body.scroll = "no";
37898 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37899 this.el.position('relative');
37901 this.id = this.el.id;
37902 this.el.addClass("roo-layout-container");
37903 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37904 if(this.el.dom != document.body ) {
37905 this.el.on('resize', this.layout,this);
37906 this.el.on('show', this.layout,this);
37912 * Returns true if this layout is currently being updated
37913 * @return {Boolean}
37915 isUpdating : function(){
37916 return this.updating;
37920 * Suspend the LayoutManager from doing auto-layouts while
37921 * making multiple add or remove calls
37923 beginUpdate : function(){
37924 this.updating = true;
37928 * Restore auto-layouts and optionally disable the manager from performing a layout
37929 * @param {Boolean} noLayout true to disable a layout update
37931 endUpdate : function(noLayout){
37932 this.updating = false;
37938 layout: function(){
37942 onRegionResized : function(region, newSize){
37943 this.fireEvent("regionresized", region, newSize);
37947 onRegionCollapsed : function(region){
37948 this.fireEvent("regioncollapsed", region);
37951 onRegionExpanded : function(region){
37952 this.fireEvent("regionexpanded", region);
37956 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37957 * performs box-model adjustments.
37958 * @return {Object} The size as an object {width: (the width), height: (the height)}
37960 getViewSize : function()
37963 if(this.el.dom != document.body){
37964 size = this.el.getSize();
37966 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37968 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37969 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37974 * Returns the Element this layout is bound to.
37975 * @return {Roo.Element}
37977 getEl : function(){
37982 * Returns the specified region.
37983 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37984 * @return {Roo.LayoutRegion}
37986 getRegion : function(target){
37987 return this.regions[target.toLowerCase()];
37990 onWindowResize : function(){
37991 if(this.monitorWindowResize){
37998 * Ext JS Library 1.1.1
37999 * Copyright(c) 2006-2007, Ext JS, LLC.
38001 * Originally Released Under LGPL - original licence link has changed is not relivant.
38004 * <script type="text/javascript">
38007 * @class Roo.bootstrap.layout.Border
38008 * @extends Roo.bootstrap.layout.Manager
38010 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38011 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38012 * please see: examples/bootstrap/nested.html<br><br>
38014 <b>The container the layout is rendered into can be either the body element or any other element.
38015 If it is not the body element, the container needs to either be an absolute positioned element,
38016 or you will need to add "position:relative" to the css of the container. You will also need to specify
38017 the container size if it is not the body element.</b>
38020 * Create a new Border
38021 * @param {Object} config Configuration options
38023 Roo.bootstrap.layout.Border = function(config){
38024 config = config || {};
38025 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38029 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38030 if(config[region]){
38031 config[region].region = region;
38032 this.addRegion(config[region]);
38038 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38040 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38043 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38046 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38049 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38052 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38055 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38061 parent : false, // this might point to a 'nest' or a ???
38064 * Creates and adds a new region if it doesn't already exist.
38065 * @param {String} target The target region key (north, south, east, west or center).
38066 * @param {Object} config The regions config object
38067 * @return {BorderLayoutRegion} The new region
38069 addRegion : function(config)
38071 if(!this.regions[config.region]){
38072 var r = this.factory(config);
38073 this.bindRegion(r);
38075 return this.regions[config.region];
38079 bindRegion : function(r){
38080 this.regions[r.config.region] = r;
38082 r.on("visibilitychange", this.layout, this);
38083 r.on("paneladded", this.layout, this);
38084 r.on("panelremoved", this.layout, this);
38085 r.on("invalidated", this.layout, this);
38086 r.on("resized", this.onRegionResized, this);
38087 r.on("collapsed", this.onRegionCollapsed, this);
38088 r.on("expanded", this.onRegionExpanded, this);
38092 * Performs a layout update.
38094 layout : function()
38096 if(this.updating) {
38100 // render all the rebions if they have not been done alreayd?
38101 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38102 if(this.regions[region] && !this.regions[region].bodyEl){
38103 this.regions[region].onRender(this.el)
38107 var size = this.getViewSize();
38108 var w = size.width;
38109 var h = size.height;
38114 //var x = 0, y = 0;
38116 var rs = this.regions;
38117 var north = rs["north"];
38118 var south = rs["south"];
38119 var west = rs["west"];
38120 var east = rs["east"];
38121 var center = rs["center"];
38122 //if(this.hideOnLayout){ // not supported anymore
38123 //c.el.setStyle("display", "none");
38125 if(north && north.isVisible()){
38126 var b = north.getBox();
38127 var m = north.getMargins();
38128 b.width = w - (m.left+m.right);
38131 centerY = b.height + b.y + m.bottom;
38132 centerH -= centerY;
38133 north.updateBox(this.safeBox(b));
38135 if(south && south.isVisible()){
38136 var b = south.getBox();
38137 var m = south.getMargins();
38138 b.width = w - (m.left+m.right);
38140 var totalHeight = (b.height + m.top + m.bottom);
38141 b.y = h - totalHeight + m.top;
38142 centerH -= totalHeight;
38143 south.updateBox(this.safeBox(b));
38145 if(west && west.isVisible()){
38146 var b = west.getBox();
38147 var m = west.getMargins();
38148 b.height = centerH - (m.top+m.bottom);
38150 b.y = centerY + m.top;
38151 var totalWidth = (b.width + m.left + m.right);
38152 centerX += totalWidth;
38153 centerW -= totalWidth;
38154 west.updateBox(this.safeBox(b));
38156 if(east && east.isVisible()){
38157 var b = east.getBox();
38158 var m = east.getMargins();
38159 b.height = centerH - (m.top+m.bottom);
38160 var totalWidth = (b.width + m.left + m.right);
38161 b.x = w - totalWidth + m.left;
38162 b.y = centerY + m.top;
38163 centerW -= totalWidth;
38164 east.updateBox(this.safeBox(b));
38167 var m = center.getMargins();
38169 x: centerX + m.left,
38170 y: centerY + m.top,
38171 width: centerW - (m.left+m.right),
38172 height: centerH - (m.top+m.bottom)
38174 //if(this.hideOnLayout){
38175 //center.el.setStyle("display", "block");
38177 center.updateBox(this.safeBox(centerBox));
38180 this.fireEvent("layout", this);
38184 safeBox : function(box){
38185 box.width = Math.max(0, box.width);
38186 box.height = Math.max(0, box.height);
38191 * Adds a ContentPanel (or subclass) to this layout.
38192 * @param {String} target The target region key (north, south, east, west or center).
38193 * @param {Roo.ContentPanel} panel The panel to add
38194 * @return {Roo.ContentPanel} The added panel
38196 add : function(target, panel){
38198 target = target.toLowerCase();
38199 return this.regions[target].add(panel);
38203 * Remove a ContentPanel (or subclass) to this layout.
38204 * @param {String} target The target region key (north, south, east, west or center).
38205 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38206 * @return {Roo.ContentPanel} The removed panel
38208 remove : function(target, panel){
38209 target = target.toLowerCase();
38210 return this.regions[target].remove(panel);
38214 * Searches all regions for a panel with the specified id
38215 * @param {String} panelId
38216 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38218 findPanel : function(panelId){
38219 var rs = this.regions;
38220 for(var target in rs){
38221 if(typeof rs[target] != "function"){
38222 var p = rs[target].getPanel(panelId);
38232 * Searches all regions for a panel with the specified id and activates (shows) it.
38233 * @param {String/ContentPanel} panelId The panels id or the panel itself
38234 * @return {Roo.ContentPanel} The shown panel or null
38236 showPanel : function(panelId) {
38237 var rs = this.regions;
38238 for(var target in rs){
38239 var r = rs[target];
38240 if(typeof r != "function"){
38241 if(r.hasPanel(panelId)){
38242 return r.showPanel(panelId);
38250 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38251 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38254 restoreState : function(provider){
38256 provider = Roo.state.Manager;
38258 var sm = new Roo.LayoutStateManager();
38259 sm.init(this, provider);
38265 * Adds a xtype elements to the layout.
38269 xtype : 'ContentPanel',
38276 xtype : 'NestedLayoutPanel',
38282 items : [ ... list of content panels or nested layout panels.. ]
38286 * @param {Object} cfg Xtype definition of item to add.
38288 addxtype : function(cfg)
38290 // basically accepts a pannel...
38291 // can accept a layout region..!?!?
38292 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38295 // theory? children can only be panels??
38297 //if (!cfg.xtype.match(/Panel$/)) {
38302 if (typeof(cfg.region) == 'undefined') {
38303 Roo.log("Failed to add Panel, region was not set");
38307 var region = cfg.region;
38313 xitems = cfg.items;
38318 if ( region == 'center') {
38319 Roo.log("Center: " + cfg.title);
38325 case 'Content': // ContentPanel (el, cfg)
38326 case 'Scroll': // ContentPanel (el, cfg)
38328 cfg.autoCreate = cfg.autoCreate || true;
38329 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38331 // var el = this.el.createChild();
38332 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38335 this.add(region, ret);
38339 case 'TreePanel': // our new panel!
38340 cfg.el = this.el.createChild();
38341 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38342 this.add(region, ret);
38347 // create a new Layout (which is a Border Layout...
38349 var clayout = cfg.layout;
38350 clayout.el = this.el.createChild();
38351 clayout.items = clayout.items || [];
38355 // replace this exitems with the clayout ones..
38356 xitems = clayout.items;
38358 // force background off if it's in center...
38359 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38360 cfg.background = false;
38362 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38365 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38366 //console.log('adding nested layout panel ' + cfg.toSource());
38367 this.add(region, ret);
38368 nb = {}; /// find first...
38373 // needs grid and region
38375 //var el = this.getRegion(region).el.createChild();
38377 *var el = this.el.createChild();
38378 // create the grid first...
38379 cfg.grid.container = el;
38380 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38383 if (region == 'center' && this.active ) {
38384 cfg.background = false;
38387 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38389 this.add(region, ret);
38391 if (cfg.background) {
38392 // render grid on panel activation (if panel background)
38393 ret.on('activate', function(gp) {
38394 if (!gp.grid.rendered) {
38395 // gp.grid.render(el);
38399 // cfg.grid.render(el);
38405 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38406 // it was the old xcomponent building that caused this before.
38407 // espeically if border is the top element in the tree.
38417 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38419 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38420 this.add(region, ret);
38424 throw "Can not add '" + cfg.xtype + "' to Border";
38430 this.beginUpdate();
38434 Roo.each(xitems, function(i) {
38435 region = nb && i.region ? i.region : false;
38437 var add = ret.addxtype(i);
38440 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38441 if (!i.background) {
38442 abn[region] = nb[region] ;
38449 // make the last non-background panel active..
38450 //if (nb) { Roo.log(abn); }
38453 for(var r in abn) {
38454 region = this.getRegion(r);
38456 // tried using nb[r], but it does not work..
38458 region.showPanel(abn[r]);
38469 factory : function(cfg)
38472 var validRegions = Roo.bootstrap.layout.Border.regions;
38474 var target = cfg.region;
38477 var r = Roo.bootstrap.layout;
38481 return new r.North(cfg);
38483 return new r.South(cfg);
38485 return new r.East(cfg);
38487 return new r.West(cfg);
38489 return new r.Center(cfg);
38491 throw 'Layout region "'+target+'" not supported.';
38498 * Ext JS Library 1.1.1
38499 * Copyright(c) 2006-2007, Ext JS, LLC.
38501 * Originally Released Under LGPL - original licence link has changed is not relivant.
38504 * <script type="text/javascript">
38508 * @class Roo.bootstrap.layout.Basic
38509 * @extends Roo.util.Observable
38510 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38511 * and does not have a titlebar, tabs or any other features. All it does is size and position
38512 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38513 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38514 * @cfg {string} region the region that it inhabits..
38515 * @cfg {bool} skipConfig skip config?
38519 Roo.bootstrap.layout.Basic = function(config){
38521 this.mgr = config.mgr;
38523 this.position = config.region;
38525 var skipConfig = config.skipConfig;
38529 * @scope Roo.BasicLayoutRegion
38533 * @event beforeremove
38534 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38535 * @param {Roo.LayoutRegion} this
38536 * @param {Roo.ContentPanel} panel The panel
38537 * @param {Object} e The cancel event object
38539 "beforeremove" : true,
38541 * @event invalidated
38542 * Fires when the layout for this region is changed.
38543 * @param {Roo.LayoutRegion} this
38545 "invalidated" : true,
38547 * @event visibilitychange
38548 * Fires when this region is shown or hidden
38549 * @param {Roo.LayoutRegion} this
38550 * @param {Boolean} visibility true or false
38552 "visibilitychange" : true,
38554 * @event paneladded
38555 * Fires when a panel is added.
38556 * @param {Roo.LayoutRegion} this
38557 * @param {Roo.ContentPanel} panel The panel
38559 "paneladded" : true,
38561 * @event panelremoved
38562 * Fires when a panel is removed.
38563 * @param {Roo.LayoutRegion} this
38564 * @param {Roo.ContentPanel} panel The panel
38566 "panelremoved" : true,
38568 * @event beforecollapse
38569 * Fires when this region before collapse.
38570 * @param {Roo.LayoutRegion} this
38572 "beforecollapse" : true,
38575 * Fires when this region is collapsed.
38576 * @param {Roo.LayoutRegion} this
38578 "collapsed" : true,
38581 * Fires when this region is expanded.
38582 * @param {Roo.LayoutRegion} this
38587 * Fires when this region is slid into view.
38588 * @param {Roo.LayoutRegion} this
38590 "slideshow" : true,
38593 * Fires when this region slides out of view.
38594 * @param {Roo.LayoutRegion} this
38596 "slidehide" : true,
38598 * @event panelactivated
38599 * Fires when a panel is activated.
38600 * @param {Roo.LayoutRegion} this
38601 * @param {Roo.ContentPanel} panel The activated panel
38603 "panelactivated" : true,
38606 * Fires when the user resizes this region.
38607 * @param {Roo.LayoutRegion} this
38608 * @param {Number} newSize The new size (width for east/west, height for north/south)
38612 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38613 this.panels = new Roo.util.MixedCollection();
38614 this.panels.getKey = this.getPanelId.createDelegate(this);
38616 this.activePanel = null;
38617 // ensure listeners are added...
38619 if (config.listeners || config.events) {
38620 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38621 listeners : config.listeners || {},
38622 events : config.events || {}
38626 if(skipConfig !== true){
38627 this.applyConfig(config);
38631 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38633 getPanelId : function(p){
38637 applyConfig : function(config){
38638 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38639 this.config = config;
38644 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38645 * the width, for horizontal (north, south) the height.
38646 * @param {Number} newSize The new width or height
38648 resizeTo : function(newSize){
38649 var el = this.el ? this.el :
38650 (this.activePanel ? this.activePanel.getEl() : null);
38652 switch(this.position){
38655 el.setWidth(newSize);
38656 this.fireEvent("resized", this, newSize);
38660 el.setHeight(newSize);
38661 this.fireEvent("resized", this, newSize);
38667 getBox : function(){
38668 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38671 getMargins : function(){
38672 return this.margins;
38675 updateBox : function(box){
38677 var el = this.activePanel.getEl();
38678 el.dom.style.left = box.x + "px";
38679 el.dom.style.top = box.y + "px";
38680 this.activePanel.setSize(box.width, box.height);
38684 * Returns the container element for this region.
38685 * @return {Roo.Element}
38687 getEl : function(){
38688 return this.activePanel;
38692 * Returns true if this region is currently visible.
38693 * @return {Boolean}
38695 isVisible : function(){
38696 return this.activePanel ? true : false;
38699 setActivePanel : function(panel){
38700 panel = this.getPanel(panel);
38701 if(this.activePanel && this.activePanel != panel){
38702 this.activePanel.setActiveState(false);
38703 this.activePanel.getEl().setLeftTop(-10000,-10000);
38705 this.activePanel = panel;
38706 panel.setActiveState(true);
38708 panel.setSize(this.box.width, this.box.height);
38710 this.fireEvent("panelactivated", this, panel);
38711 this.fireEvent("invalidated");
38715 * Show the specified panel.
38716 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38717 * @return {Roo.ContentPanel} The shown panel or null
38719 showPanel : function(panel){
38720 panel = this.getPanel(panel);
38722 this.setActivePanel(panel);
38728 * Get the active panel for this region.
38729 * @return {Roo.ContentPanel} The active panel or null
38731 getActivePanel : function(){
38732 return this.activePanel;
38736 * Add the passed ContentPanel(s)
38737 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38738 * @return {Roo.ContentPanel} The panel added (if only one was added)
38740 add : function(panel){
38741 if(arguments.length > 1){
38742 for(var i = 0, len = arguments.length; i < len; i++) {
38743 this.add(arguments[i]);
38747 if(this.hasPanel(panel)){
38748 this.showPanel(panel);
38751 var el = panel.getEl();
38752 if(el.dom.parentNode != this.mgr.el.dom){
38753 this.mgr.el.dom.appendChild(el.dom);
38755 if(panel.setRegion){
38756 panel.setRegion(this);
38758 this.panels.add(panel);
38759 el.setStyle("position", "absolute");
38760 if(!panel.background){
38761 this.setActivePanel(panel);
38762 if(this.config.initialSize && this.panels.getCount()==1){
38763 this.resizeTo(this.config.initialSize);
38766 this.fireEvent("paneladded", this, panel);
38771 * Returns true if the panel is in this region.
38772 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38773 * @return {Boolean}
38775 hasPanel : function(panel){
38776 if(typeof panel == "object"){ // must be panel obj
38777 panel = panel.getId();
38779 return this.getPanel(panel) ? true : false;
38783 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38784 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38785 * @param {Boolean} preservePanel Overrides the config preservePanel option
38786 * @return {Roo.ContentPanel} The panel that was removed
38788 remove : function(panel, preservePanel){
38789 panel = this.getPanel(panel);
38794 this.fireEvent("beforeremove", this, panel, e);
38795 if(e.cancel === true){
38798 var panelId = panel.getId();
38799 this.panels.removeKey(panelId);
38804 * Returns the panel specified or null if it's not in this region.
38805 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38806 * @return {Roo.ContentPanel}
38808 getPanel : function(id){
38809 if(typeof id == "object"){ // must be panel obj
38812 return this.panels.get(id);
38816 * Returns this regions position (north/south/east/west/center).
38819 getPosition: function(){
38820 return this.position;
38824 * Ext JS Library 1.1.1
38825 * Copyright(c) 2006-2007, Ext JS, LLC.
38827 * Originally Released Under LGPL - original licence link has changed is not relivant.
38830 * <script type="text/javascript">
38834 * @class Roo.bootstrap.layout.Region
38835 * @extends Roo.bootstrap.layout.Basic
38836 * This class represents a region in a layout manager.
38838 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38839 * @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})
38840 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38841 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38842 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38843 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38844 * @cfg {String} title The title for the region (overrides panel titles)
38845 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38846 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38847 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38848 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38849 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38850 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38851 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38852 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38853 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38854 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38856 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38857 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38858 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38859 * @cfg {Number} width For East/West panels
38860 * @cfg {Number} height For North/South panels
38861 * @cfg {Boolean} split To show the splitter
38862 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38864 * @cfg {string} cls Extra CSS classes to add to region
38866 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38867 * @cfg {string} region the region that it inhabits..
38870 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38871 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38873 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38874 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38875 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38877 Roo.bootstrap.layout.Region = function(config)
38879 this.applyConfig(config);
38881 var mgr = config.mgr;
38882 var pos = config.region;
38883 config.skipConfig = true;
38884 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38887 this.onRender(mgr.el);
38890 this.visible = true;
38891 this.collapsed = false;
38892 this.unrendered_panels = [];
38895 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38897 position: '', // set by wrapper (eg. north/south etc..)
38898 unrendered_panels : null, // unrendered panels.
38900 tabPosition : false,
38902 mgr: false, // points to 'Border'
38905 createBody : function(){
38906 /** This region's body element
38907 * @type Roo.Element */
38908 this.bodyEl = this.el.createChild({
38910 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38914 onRender: function(ctr, pos)
38916 var dh = Roo.DomHelper;
38917 /** This region's container element
38918 * @type Roo.Element */
38919 this.el = dh.append(ctr.dom, {
38921 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38923 /** This region's title element
38924 * @type Roo.Element */
38926 this.titleEl = dh.append(this.el.dom, {
38928 unselectable: "on",
38929 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38931 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38932 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38936 this.titleEl.enableDisplayMode();
38937 /** This region's title text element
38938 * @type HTMLElement */
38939 this.titleTextEl = this.titleEl.dom.firstChild;
38940 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38942 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38943 this.closeBtn.enableDisplayMode();
38944 this.closeBtn.on("click", this.closeClicked, this);
38945 this.closeBtn.hide();
38947 this.createBody(this.config);
38948 if(this.config.hideWhenEmpty){
38950 this.on("paneladded", this.validateVisibility, this);
38951 this.on("panelremoved", this.validateVisibility, this);
38953 if(this.autoScroll){
38954 this.bodyEl.setStyle("overflow", "auto");
38956 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38958 //if(c.titlebar !== false){
38959 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38960 this.titleEl.hide();
38962 this.titleEl.show();
38963 if(this.config.title){
38964 this.titleTextEl.innerHTML = this.config.title;
38968 if(this.config.collapsed){
38969 this.collapse(true);
38971 if(this.config.hidden){
38975 if (this.unrendered_panels && this.unrendered_panels.length) {
38976 for (var i =0;i< this.unrendered_panels.length; i++) {
38977 this.add(this.unrendered_panels[i]);
38979 this.unrendered_panels = null;
38985 applyConfig : function(c)
38988 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38989 var dh = Roo.DomHelper;
38990 if(c.titlebar !== false){
38991 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38992 this.collapseBtn.on("click", this.collapse, this);
38993 this.collapseBtn.enableDisplayMode();
38995 if(c.showPin === true || this.showPin){
38996 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38997 this.stickBtn.enableDisplayMode();
38998 this.stickBtn.on("click", this.expand, this);
38999 this.stickBtn.hide();
39004 /** This region's collapsed element
39005 * @type Roo.Element */
39008 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39009 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39012 if(c.floatable !== false){
39013 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39014 this.collapsedEl.on("click", this.collapseClick, this);
39017 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39018 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39019 id: "message", unselectable: "on", style:{"float":"left"}});
39020 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39022 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39023 this.expandBtn.on("click", this.expand, this);
39027 if(this.collapseBtn){
39028 this.collapseBtn.setVisible(c.collapsible == true);
39031 this.cmargins = c.cmargins || this.cmargins ||
39032 (this.position == "west" || this.position == "east" ?
39033 {top: 0, left: 2, right:2, bottom: 0} :
39034 {top: 2, left: 0, right:0, bottom: 2});
39036 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39039 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39041 this.autoScroll = c.autoScroll || false;
39046 this.duration = c.duration || .30;
39047 this.slideDuration = c.slideDuration || .45;
39052 * Returns true if this region is currently visible.
39053 * @return {Boolean}
39055 isVisible : function(){
39056 return this.visible;
39060 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39061 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39063 //setCollapsedTitle : function(title){
39064 // title = title || " ";
39065 // if(this.collapsedTitleTextEl){
39066 // this.collapsedTitleTextEl.innerHTML = title;
39070 getBox : function(){
39072 // if(!this.collapsed){
39073 b = this.el.getBox(false, true);
39075 // b = this.collapsedEl.getBox(false, true);
39080 getMargins : function(){
39081 return this.margins;
39082 //return this.collapsed ? this.cmargins : this.margins;
39085 highlight : function(){
39086 this.el.addClass("x-layout-panel-dragover");
39089 unhighlight : function(){
39090 this.el.removeClass("x-layout-panel-dragover");
39093 updateBox : function(box)
39095 if (!this.bodyEl) {
39096 return; // not rendered yet..
39100 if(!this.collapsed){
39101 this.el.dom.style.left = box.x + "px";
39102 this.el.dom.style.top = box.y + "px";
39103 this.updateBody(box.width, box.height);
39105 this.collapsedEl.dom.style.left = box.x + "px";
39106 this.collapsedEl.dom.style.top = box.y + "px";
39107 this.collapsedEl.setSize(box.width, box.height);
39110 this.tabs.autoSizeTabs();
39114 updateBody : function(w, h)
39117 this.el.setWidth(w);
39118 w -= this.el.getBorderWidth("rl");
39119 if(this.config.adjustments){
39120 w += this.config.adjustments[0];
39123 if(h !== null && h > 0){
39124 this.el.setHeight(h);
39125 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39126 h -= this.el.getBorderWidth("tb");
39127 if(this.config.adjustments){
39128 h += this.config.adjustments[1];
39130 this.bodyEl.setHeight(h);
39132 h = this.tabs.syncHeight(h);
39135 if(this.panelSize){
39136 w = w !== null ? w : this.panelSize.width;
39137 h = h !== null ? h : this.panelSize.height;
39139 if(this.activePanel){
39140 var el = this.activePanel.getEl();
39141 w = w !== null ? w : el.getWidth();
39142 h = h !== null ? h : el.getHeight();
39143 this.panelSize = {width: w, height: h};
39144 this.activePanel.setSize(w, h);
39146 if(Roo.isIE && this.tabs){
39147 this.tabs.el.repaint();
39152 * Returns the container element for this region.
39153 * @return {Roo.Element}
39155 getEl : function(){
39160 * Hides this region.
39163 //if(!this.collapsed){
39164 this.el.dom.style.left = "-2000px";
39167 // this.collapsedEl.dom.style.left = "-2000px";
39168 // this.collapsedEl.hide();
39170 this.visible = false;
39171 this.fireEvent("visibilitychange", this, false);
39175 * Shows this region if it was previously hidden.
39178 //if(!this.collapsed){
39181 // this.collapsedEl.show();
39183 this.visible = true;
39184 this.fireEvent("visibilitychange", this, true);
39187 closeClicked : function(){
39188 if(this.activePanel){
39189 this.remove(this.activePanel);
39193 collapseClick : function(e){
39195 e.stopPropagation();
39198 e.stopPropagation();
39204 * Collapses this region.
39205 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39208 collapse : function(skipAnim, skipCheck = false){
39209 if(this.collapsed) {
39213 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39215 this.collapsed = true;
39217 this.split.el.hide();
39219 if(this.config.animate && skipAnim !== true){
39220 this.fireEvent("invalidated", this);
39221 this.animateCollapse();
39223 this.el.setLocation(-20000,-20000);
39225 this.collapsedEl.show();
39226 this.fireEvent("collapsed", this);
39227 this.fireEvent("invalidated", this);
39233 animateCollapse : function(){
39238 * Expands this region if it was previously collapsed.
39239 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39240 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39243 expand : function(e, skipAnim){
39245 e.stopPropagation();
39247 if(!this.collapsed || this.el.hasActiveFx()) {
39251 this.afterSlideIn();
39254 this.collapsed = false;
39255 if(this.config.animate && skipAnim !== true){
39256 this.animateExpand();
39260 this.split.el.show();
39262 this.collapsedEl.setLocation(-2000,-2000);
39263 this.collapsedEl.hide();
39264 this.fireEvent("invalidated", this);
39265 this.fireEvent("expanded", this);
39269 animateExpand : function(){
39273 initTabs : function()
39275 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39277 var ts = new Roo.bootstrap.panel.Tabs({
39278 el: this.bodyEl.dom,
39280 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39281 disableTooltips: this.config.disableTabTips,
39282 toolbar : this.config.toolbar
39285 if(this.config.hideTabs){
39286 ts.stripWrap.setDisplayed(false);
39289 ts.resizeTabs = this.config.resizeTabs === true;
39290 ts.minTabWidth = this.config.minTabWidth || 40;
39291 ts.maxTabWidth = this.config.maxTabWidth || 250;
39292 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39293 ts.monitorResize = false;
39294 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39295 ts.bodyEl.addClass('roo-layout-tabs-body');
39296 this.panels.each(this.initPanelAsTab, this);
39299 initPanelAsTab : function(panel){
39300 var ti = this.tabs.addTab(
39304 this.config.closeOnTab && panel.isClosable(),
39307 if(panel.tabTip !== undefined){
39308 ti.setTooltip(panel.tabTip);
39310 ti.on("activate", function(){
39311 this.setActivePanel(panel);
39314 if(this.config.closeOnTab){
39315 ti.on("beforeclose", function(t, e){
39317 this.remove(panel);
39321 panel.tabItem = ti;
39326 updatePanelTitle : function(panel, title)
39328 if(this.activePanel == panel){
39329 this.updateTitle(title);
39332 var ti = this.tabs.getTab(panel.getEl().id);
39334 if(panel.tabTip !== undefined){
39335 ti.setTooltip(panel.tabTip);
39340 updateTitle : function(title){
39341 if(this.titleTextEl && !this.config.title){
39342 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39346 setActivePanel : function(panel)
39348 panel = this.getPanel(panel);
39349 if(this.activePanel && this.activePanel != panel){
39350 if(this.activePanel.setActiveState(false) === false){
39354 this.activePanel = panel;
39355 panel.setActiveState(true);
39356 if(this.panelSize){
39357 panel.setSize(this.panelSize.width, this.panelSize.height);
39360 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39362 this.updateTitle(panel.getTitle());
39364 this.fireEvent("invalidated", this);
39366 this.fireEvent("panelactivated", this, panel);
39370 * Shows the specified panel.
39371 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39372 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39374 showPanel : function(panel)
39376 panel = this.getPanel(panel);
39379 var tab = this.tabs.getTab(panel.getEl().id);
39380 if(tab.isHidden()){
39381 this.tabs.unhideTab(tab.id);
39385 this.setActivePanel(panel);
39392 * Get the active panel for this region.
39393 * @return {Roo.ContentPanel} The active panel or null
39395 getActivePanel : function(){
39396 return this.activePanel;
39399 validateVisibility : function(){
39400 if(this.panels.getCount() < 1){
39401 this.updateTitle(" ");
39402 this.closeBtn.hide();
39405 if(!this.isVisible()){
39412 * Adds the passed ContentPanel(s) to this region.
39413 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39414 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39416 add : function(panel)
39418 if(arguments.length > 1){
39419 for(var i = 0, len = arguments.length; i < len; i++) {
39420 this.add(arguments[i]);
39425 // if we have not been rendered yet, then we can not really do much of this..
39426 if (!this.bodyEl) {
39427 this.unrendered_panels.push(panel);
39434 if(this.hasPanel(panel)){
39435 this.showPanel(panel);
39438 panel.setRegion(this);
39439 this.panels.add(panel);
39440 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39441 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39442 // and hide them... ???
39443 this.bodyEl.dom.appendChild(panel.getEl().dom);
39444 if(panel.background !== true){
39445 this.setActivePanel(panel);
39447 this.fireEvent("paneladded", this, panel);
39454 this.initPanelAsTab(panel);
39458 if(panel.background !== true){
39459 this.tabs.activate(panel.getEl().id);
39461 this.fireEvent("paneladded", this, panel);
39466 * Hides the tab for the specified panel.
39467 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39469 hidePanel : function(panel){
39470 if(this.tabs && (panel = this.getPanel(panel))){
39471 this.tabs.hideTab(panel.getEl().id);
39476 * Unhides the tab for a previously hidden panel.
39477 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39479 unhidePanel : function(panel){
39480 if(this.tabs && (panel = this.getPanel(panel))){
39481 this.tabs.unhideTab(panel.getEl().id);
39485 clearPanels : function(){
39486 while(this.panels.getCount() > 0){
39487 this.remove(this.panels.first());
39492 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39493 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39494 * @param {Boolean} preservePanel Overrides the config preservePanel option
39495 * @return {Roo.ContentPanel} The panel that was removed
39497 remove : function(panel, preservePanel)
39499 panel = this.getPanel(panel);
39504 this.fireEvent("beforeremove", this, panel, e);
39505 if(e.cancel === true){
39508 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39509 var panelId = panel.getId();
39510 this.panels.removeKey(panelId);
39512 document.body.appendChild(panel.getEl().dom);
39515 this.tabs.removeTab(panel.getEl().id);
39516 }else if (!preservePanel){
39517 this.bodyEl.dom.removeChild(panel.getEl().dom);
39519 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39520 var p = this.panels.first();
39521 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39522 tempEl.appendChild(p.getEl().dom);
39523 this.bodyEl.update("");
39524 this.bodyEl.dom.appendChild(p.getEl().dom);
39526 this.updateTitle(p.getTitle());
39528 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39529 this.setActivePanel(p);
39531 panel.setRegion(null);
39532 if(this.activePanel == panel){
39533 this.activePanel = null;
39535 if(this.config.autoDestroy !== false && preservePanel !== true){
39536 try{panel.destroy();}catch(e){}
39538 this.fireEvent("panelremoved", this, panel);
39543 * Returns the TabPanel component used by this region
39544 * @return {Roo.TabPanel}
39546 getTabs : function(){
39550 createTool : function(parentEl, className){
39551 var btn = Roo.DomHelper.append(parentEl, {
39553 cls: "x-layout-tools-button",
39556 cls: "roo-layout-tools-button-inner " + className,
39560 btn.addClassOnOver("roo-layout-tools-button-over");
39565 * Ext JS Library 1.1.1
39566 * Copyright(c) 2006-2007, Ext JS, LLC.
39568 * Originally Released Under LGPL - original licence link has changed is not relivant.
39571 * <script type="text/javascript">
39577 * @class Roo.SplitLayoutRegion
39578 * @extends Roo.LayoutRegion
39579 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39581 Roo.bootstrap.layout.Split = function(config){
39582 this.cursor = config.cursor;
39583 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39586 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39588 splitTip : "Drag to resize.",
39589 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39590 useSplitTips : false,
39592 applyConfig : function(config){
39593 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39596 onRender : function(ctr,pos) {
39598 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39599 if(!this.config.split){
39604 var splitEl = Roo.DomHelper.append(ctr.dom, {
39606 id: this.el.id + "-split",
39607 cls: "roo-layout-split roo-layout-split-"+this.position,
39610 /** The SplitBar for this region
39611 * @type Roo.SplitBar */
39612 // does not exist yet...
39613 Roo.log([this.position, this.orientation]);
39615 this.split = new Roo.bootstrap.SplitBar({
39616 dragElement : splitEl,
39617 resizingElement: this.el,
39618 orientation : this.orientation
39621 this.split.on("moved", this.onSplitMove, this);
39622 this.split.useShim = this.config.useShim === true;
39623 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39624 if(this.useSplitTips){
39625 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39627 //if(config.collapsible){
39628 // this.split.el.on("dblclick", this.collapse, this);
39631 if(typeof this.config.minSize != "undefined"){
39632 this.split.minSize = this.config.minSize;
39634 if(typeof this.config.maxSize != "undefined"){
39635 this.split.maxSize = this.config.maxSize;
39637 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39638 this.hideSplitter();
39643 getHMaxSize : function(){
39644 var cmax = this.config.maxSize || 10000;
39645 var center = this.mgr.getRegion("center");
39646 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39649 getVMaxSize : function(){
39650 var cmax = this.config.maxSize || 10000;
39651 var center = this.mgr.getRegion("center");
39652 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39655 onSplitMove : function(split, newSize){
39656 this.fireEvent("resized", this, newSize);
39660 * Returns the {@link Roo.SplitBar} for this region.
39661 * @return {Roo.SplitBar}
39663 getSplitBar : function(){
39668 this.hideSplitter();
39669 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39672 hideSplitter : function(){
39674 this.split.el.setLocation(-2000,-2000);
39675 this.split.el.hide();
39681 this.split.el.show();
39683 Roo.bootstrap.layout.Split.superclass.show.call(this);
39686 beforeSlide: function(){
39687 if(Roo.isGecko){// firefox overflow auto bug workaround
39688 this.bodyEl.clip();
39690 this.tabs.bodyEl.clip();
39692 if(this.activePanel){
39693 this.activePanel.getEl().clip();
39695 if(this.activePanel.beforeSlide){
39696 this.activePanel.beforeSlide();
39702 afterSlide : function(){
39703 if(Roo.isGecko){// firefox overflow auto bug workaround
39704 this.bodyEl.unclip();
39706 this.tabs.bodyEl.unclip();
39708 if(this.activePanel){
39709 this.activePanel.getEl().unclip();
39710 if(this.activePanel.afterSlide){
39711 this.activePanel.afterSlide();
39717 initAutoHide : function(){
39718 if(this.autoHide !== false){
39719 if(!this.autoHideHd){
39720 var st = new Roo.util.DelayedTask(this.slideIn, this);
39721 this.autoHideHd = {
39722 "mouseout": function(e){
39723 if(!e.within(this.el, true)){
39727 "mouseover" : function(e){
39733 this.el.on(this.autoHideHd);
39737 clearAutoHide : function(){
39738 if(this.autoHide !== false){
39739 this.el.un("mouseout", this.autoHideHd.mouseout);
39740 this.el.un("mouseover", this.autoHideHd.mouseover);
39744 clearMonitor : function(){
39745 Roo.get(document).un("click", this.slideInIf, this);
39748 // these names are backwards but not changed for compat
39749 slideOut : function(){
39750 if(this.isSlid || this.el.hasActiveFx()){
39753 this.isSlid = true;
39754 if(this.collapseBtn){
39755 this.collapseBtn.hide();
39757 this.closeBtnState = this.closeBtn.getStyle('display');
39758 this.closeBtn.hide();
39760 this.stickBtn.show();
39763 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39764 this.beforeSlide();
39765 this.el.setStyle("z-index", 10001);
39766 this.el.slideIn(this.getSlideAnchor(), {
39767 callback: function(){
39769 this.initAutoHide();
39770 Roo.get(document).on("click", this.slideInIf, this);
39771 this.fireEvent("slideshow", this);
39778 afterSlideIn : function(){
39779 this.clearAutoHide();
39780 this.isSlid = false;
39781 this.clearMonitor();
39782 this.el.setStyle("z-index", "");
39783 if(this.collapseBtn){
39784 this.collapseBtn.show();
39786 this.closeBtn.setStyle('display', this.closeBtnState);
39788 this.stickBtn.hide();
39790 this.fireEvent("slidehide", this);
39793 slideIn : function(cb){
39794 if(!this.isSlid || this.el.hasActiveFx()){
39798 this.isSlid = false;
39799 this.beforeSlide();
39800 this.el.slideOut(this.getSlideAnchor(), {
39801 callback: function(){
39802 this.el.setLeftTop(-10000, -10000);
39804 this.afterSlideIn();
39812 slideInIf : function(e){
39813 if(!e.within(this.el)){
39818 animateCollapse : function(){
39819 this.beforeSlide();
39820 this.el.setStyle("z-index", 20000);
39821 var anchor = this.getSlideAnchor();
39822 this.el.slideOut(anchor, {
39823 callback : function(){
39824 this.el.setStyle("z-index", "");
39825 this.collapsedEl.slideIn(anchor, {duration:.3});
39827 this.el.setLocation(-10000,-10000);
39829 this.fireEvent("collapsed", this);
39836 animateExpand : function(){
39837 this.beforeSlide();
39838 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39839 this.el.setStyle("z-index", 20000);
39840 this.collapsedEl.hide({
39843 this.el.slideIn(this.getSlideAnchor(), {
39844 callback : function(){
39845 this.el.setStyle("z-index", "");
39848 this.split.el.show();
39850 this.fireEvent("invalidated", this);
39851 this.fireEvent("expanded", this);
39879 getAnchor : function(){
39880 return this.anchors[this.position];
39883 getCollapseAnchor : function(){
39884 return this.canchors[this.position];
39887 getSlideAnchor : function(){
39888 return this.sanchors[this.position];
39891 getAlignAdj : function(){
39892 var cm = this.cmargins;
39893 switch(this.position){
39909 getExpandAdj : function(){
39910 var c = this.collapsedEl, cm = this.cmargins;
39911 switch(this.position){
39913 return [-(cm.right+c.getWidth()+cm.left), 0];
39916 return [cm.right+c.getWidth()+cm.left, 0];
39919 return [0, -(cm.top+cm.bottom+c.getHeight())];
39922 return [0, cm.top+cm.bottom+c.getHeight()];
39928 * Ext JS Library 1.1.1
39929 * Copyright(c) 2006-2007, Ext JS, LLC.
39931 * Originally Released Under LGPL - original licence link has changed is not relivant.
39934 * <script type="text/javascript">
39937 * These classes are private internal classes
39939 Roo.bootstrap.layout.Center = function(config){
39940 config.region = "center";
39941 Roo.bootstrap.layout.Region.call(this, config);
39942 this.visible = true;
39943 this.minWidth = config.minWidth || 20;
39944 this.minHeight = config.minHeight || 20;
39947 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39949 // center panel can't be hidden
39953 // center panel can't be hidden
39956 getMinWidth: function(){
39957 return this.minWidth;
39960 getMinHeight: function(){
39961 return this.minHeight;
39975 Roo.bootstrap.layout.North = function(config)
39977 config.region = 'north';
39978 config.cursor = 'n-resize';
39980 Roo.bootstrap.layout.Split.call(this, config);
39984 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39985 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39986 this.split.el.addClass("roo-layout-split-v");
39988 //var size = config.initialSize || config.height;
39989 //if(this.el && typeof size != "undefined"){
39990 // this.el.setHeight(size);
39993 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39995 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39998 onRender : function(ctr, pos)
40000 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40001 var size = this.config.initialSize || this.config.height;
40002 if(this.el && typeof size != "undefined"){
40003 this.el.setHeight(size);
40008 getBox : function(){
40009 if(this.collapsed){
40010 return this.collapsedEl.getBox();
40012 var box = this.el.getBox();
40014 box.height += this.split.el.getHeight();
40019 updateBox : function(box){
40020 if(this.split && !this.collapsed){
40021 box.height -= this.split.el.getHeight();
40022 this.split.el.setLeft(box.x);
40023 this.split.el.setTop(box.y+box.height);
40024 this.split.el.setWidth(box.width);
40026 if(this.collapsed){
40027 this.updateBody(box.width, null);
40029 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40037 Roo.bootstrap.layout.South = function(config){
40038 config.region = 'south';
40039 config.cursor = 's-resize';
40040 Roo.bootstrap.layout.Split.call(this, config);
40042 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40043 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40044 this.split.el.addClass("roo-layout-split-v");
40049 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40050 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40052 onRender : function(ctr, pos)
40054 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40055 var size = this.config.initialSize || this.config.height;
40056 if(this.el && typeof size != "undefined"){
40057 this.el.setHeight(size);
40062 getBox : function(){
40063 if(this.collapsed){
40064 return this.collapsedEl.getBox();
40066 var box = this.el.getBox();
40068 var sh = this.split.el.getHeight();
40075 updateBox : function(box){
40076 if(this.split && !this.collapsed){
40077 var sh = this.split.el.getHeight();
40080 this.split.el.setLeft(box.x);
40081 this.split.el.setTop(box.y-sh);
40082 this.split.el.setWidth(box.width);
40084 if(this.collapsed){
40085 this.updateBody(box.width, null);
40087 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40091 Roo.bootstrap.layout.East = function(config){
40092 config.region = "east";
40093 config.cursor = "e-resize";
40094 Roo.bootstrap.layout.Split.call(this, config);
40096 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40097 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40098 this.split.el.addClass("roo-layout-split-h");
40102 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40103 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40105 onRender : function(ctr, pos)
40107 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40108 var size = this.config.initialSize || this.config.width;
40109 if(this.el && typeof size != "undefined"){
40110 this.el.setWidth(size);
40115 getBox : function(){
40116 if(this.collapsed){
40117 return this.collapsedEl.getBox();
40119 var box = this.el.getBox();
40121 var sw = this.split.el.getWidth();
40128 updateBox : function(box){
40129 if(this.split && !this.collapsed){
40130 var sw = this.split.el.getWidth();
40132 this.split.el.setLeft(box.x);
40133 this.split.el.setTop(box.y);
40134 this.split.el.setHeight(box.height);
40137 if(this.collapsed){
40138 this.updateBody(null, box.height);
40140 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40144 Roo.bootstrap.layout.West = function(config){
40145 config.region = "west";
40146 config.cursor = "w-resize";
40148 Roo.bootstrap.layout.Split.call(this, config);
40150 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40151 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40152 this.split.el.addClass("roo-layout-split-h");
40156 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40157 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40159 onRender: function(ctr, pos)
40161 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40162 var size = this.config.initialSize || this.config.width;
40163 if(typeof size != "undefined"){
40164 this.el.setWidth(size);
40168 getBox : function(){
40169 if(this.collapsed){
40170 return this.collapsedEl.getBox();
40172 var box = this.el.getBox();
40173 if (box.width == 0) {
40174 box.width = this.config.width; // kludge?
40177 box.width += this.split.el.getWidth();
40182 updateBox : function(box){
40183 if(this.split && !this.collapsed){
40184 var sw = this.split.el.getWidth();
40186 this.split.el.setLeft(box.x+box.width);
40187 this.split.el.setTop(box.y);
40188 this.split.el.setHeight(box.height);
40190 if(this.collapsed){
40191 this.updateBody(null, box.height);
40193 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40197 * Ext JS Library 1.1.1
40198 * Copyright(c) 2006-2007, Ext JS, LLC.
40200 * Originally Released Under LGPL - original licence link has changed is not relivant.
40203 * <script type="text/javascript">
40206 * @class Roo.bootstrap.paenl.Content
40207 * @extends Roo.util.Observable
40209 * @children Roo.bootstrap.Component
40210 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40211 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40212 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40213 * @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
40214 * @cfg {Boolean} closable True if the panel can be closed/removed
40215 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40216 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40217 * @cfg {Toolbar} toolbar A toolbar for this panel
40218 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40219 * @cfg {String} title The title for this panel
40220 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40221 * @cfg {String} url Calls {@link #setUrl} with this value
40222 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40223 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40224 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40225 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40226 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40227 * @cfg {Boolean} badges render the badges
40228 * @cfg {String} cls extra classes to use
40229 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40232 * Create a new ContentPanel.
40233 * @param {String/Object} config A string to set only the title or a config object
40236 Roo.bootstrap.panel.Content = function( config){
40238 this.tpl = config.tpl || false;
40240 var el = config.el;
40241 var content = config.content;
40243 if(config.autoCreate){ // xtype is available if this is called from factory
40246 this.el = Roo.get(el);
40247 if(!this.el && config && config.autoCreate){
40248 if(typeof config.autoCreate == "object"){
40249 if(!config.autoCreate.id){
40250 config.autoCreate.id = config.id||el;
40252 this.el = Roo.DomHelper.append(document.body,
40253 config.autoCreate, true);
40257 cls: (config.cls || '') +
40258 (config.background ? ' bg-' + config.background : '') +
40259 " roo-layout-inactive-content",
40262 if (config.iframe) {
40266 style : 'border: 0px',
40267 src : 'about:blank'
40273 elcfg.html = config.html;
40277 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40278 if (config.iframe) {
40279 this.iframeEl = this.el.select('iframe',true).first();
40284 this.closable = false;
40285 this.loaded = false;
40286 this.active = false;
40289 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40291 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40293 this.wrapEl = this.el; //this.el.wrap();
40295 if (config.toolbar.items) {
40296 ti = config.toolbar.items ;
40297 delete config.toolbar.items ;
40301 this.toolbar.render(this.wrapEl, 'before');
40302 for(var i =0;i < ti.length;i++) {
40303 // Roo.log(['add child', items[i]]);
40304 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40306 this.toolbar.items = nitems;
40307 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40308 delete config.toolbar;
40312 // xtype created footer. - not sure if will work as we normally have to render first..
40313 if (this.footer && !this.footer.el && this.footer.xtype) {
40314 if (!this.wrapEl) {
40315 this.wrapEl = this.el.wrap();
40318 this.footer.container = this.wrapEl.createChild();
40320 this.footer = Roo.factory(this.footer, Roo);
40325 if(typeof config == "string"){
40326 this.title = config;
40328 Roo.apply(this, config);
40332 this.resizeEl = Roo.get(this.resizeEl, true);
40334 this.resizeEl = this.el;
40336 // handle view.xtype
40344 * Fires when this panel is activated.
40345 * @param {Roo.ContentPanel} this
40349 * @event deactivate
40350 * Fires when this panel is activated.
40351 * @param {Roo.ContentPanel} this
40353 "deactivate" : true,
40357 * Fires when this panel is resized if fitToFrame is true.
40358 * @param {Roo.ContentPanel} this
40359 * @param {Number} width The width after any component adjustments
40360 * @param {Number} height The height after any component adjustments
40366 * Fires when this tab is created
40367 * @param {Roo.ContentPanel} this
40373 * Fires when this content is scrolled
40374 * @param {Roo.ContentPanel} this
40375 * @param {Event} scrollEvent
40386 if(this.autoScroll && !this.iframe){
40387 this.resizeEl.setStyle("overflow", "auto");
40388 this.resizeEl.on('scroll', this.onScroll, this);
40390 // fix randome scrolling
40391 //this.el.on('scroll', function() {
40392 // Roo.log('fix random scolling');
40393 // this.scrollTo('top',0);
40396 content = content || this.content;
40398 this.setContent(content);
40400 if(config && config.url){
40401 this.setUrl(this.url, this.params, this.loadOnce);
40406 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40408 if (this.view && typeof(this.view.xtype) != 'undefined') {
40409 this.view.el = this.el.appendChild(document.createElement("div"));
40410 this.view = Roo.factory(this.view);
40411 this.view.render && this.view.render(false, '');
40415 this.fireEvent('render', this);
40418 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40428 /* Resize Element - use this to work out scroll etc. */
40431 setRegion : function(region){
40432 this.region = region;
40433 this.setActiveClass(region && !this.background);
40437 setActiveClass: function(state)
40440 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40441 this.el.setStyle('position','relative');
40443 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40444 this.el.setStyle('position', 'absolute');
40449 * Returns the toolbar for this Panel if one was configured.
40450 * @return {Roo.Toolbar}
40452 getToolbar : function(){
40453 return this.toolbar;
40456 setActiveState : function(active)
40458 this.active = active;
40459 this.setActiveClass(active);
40461 if(this.fireEvent("deactivate", this) === false){
40466 this.fireEvent("activate", this);
40470 * Updates this panel's element (not for iframe)
40471 * @param {String} content The new content
40472 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40474 setContent : function(content, loadScripts){
40479 this.el.update(content, loadScripts);
40482 ignoreResize : function(w, h){
40483 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40486 this.lastSize = {width: w, height: h};
40491 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40492 * @return {Roo.UpdateManager} The UpdateManager
40494 getUpdateManager : function(){
40498 return this.el.getUpdateManager();
40501 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40502 * Does not work with IFRAME contents
40503 * @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:
40506 url: "your-url.php",
40507 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40508 callback: yourFunction,
40509 scope: yourObject, //(optional scope)
40512 text: "Loading...",
40518 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40519 * 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.
40520 * @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}
40521 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40522 * @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.
40523 * @return {Roo.ContentPanel} this
40531 var um = this.el.getUpdateManager();
40532 um.update.apply(um, arguments);
40538 * 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.
40539 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40540 * @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)
40541 * @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)
40542 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40544 setUrl : function(url, params, loadOnce){
40546 this.iframeEl.dom.src = url;
40550 if(this.refreshDelegate){
40551 this.removeListener("activate", this.refreshDelegate);
40553 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40554 this.on("activate", this.refreshDelegate);
40555 return this.el.getUpdateManager();
40558 _handleRefresh : function(url, params, loadOnce){
40559 if(!loadOnce || !this.loaded){
40560 var updater = this.el.getUpdateManager();
40561 updater.update(url, params, this._setLoaded.createDelegate(this));
40565 _setLoaded : function(){
40566 this.loaded = true;
40570 * Returns this panel's id
40573 getId : function(){
40578 * Returns this panel's element - used by regiosn to add.
40579 * @return {Roo.Element}
40581 getEl : function(){
40582 return this.wrapEl || this.el;
40587 adjustForComponents : function(width, height)
40589 //Roo.log('adjustForComponents ');
40590 if(this.resizeEl != this.el){
40591 width -= this.el.getFrameWidth('lr');
40592 height -= this.el.getFrameWidth('tb');
40595 var te = this.toolbar.getEl();
40596 te.setWidth(width);
40597 height -= te.getHeight();
40600 var te = this.footer.getEl();
40601 te.setWidth(width);
40602 height -= te.getHeight();
40606 if(this.adjustments){
40607 width += this.adjustments[0];
40608 height += this.adjustments[1];
40610 return {"width": width, "height": height};
40613 setSize : function(width, height){
40614 if(this.fitToFrame && !this.ignoreResize(width, height)){
40615 if(this.fitContainer && this.resizeEl != this.el){
40616 this.el.setSize(width, height);
40618 var size = this.adjustForComponents(width, height);
40620 this.iframeEl.setSize(width,height);
40623 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40624 this.fireEvent('resize', this, size.width, size.height);
40631 * Returns this panel's title
40634 getTitle : function(){
40636 if (typeof(this.title) != 'object') {
40641 for (var k in this.title) {
40642 if (!this.title.hasOwnProperty(k)) {
40646 if (k.indexOf('-') >= 0) {
40647 var s = k.split('-');
40648 for (var i = 0; i<s.length; i++) {
40649 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40652 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40659 * Set this panel's title
40660 * @param {String} title
40662 setTitle : function(title){
40663 this.title = title;
40665 this.region.updatePanelTitle(this, title);
40670 * Returns true is this panel was configured to be closable
40671 * @return {Boolean}
40673 isClosable : function(){
40674 return this.closable;
40677 beforeSlide : function(){
40679 this.resizeEl.clip();
40682 afterSlide : function(){
40684 this.resizeEl.unclip();
40688 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40689 * Will fail silently if the {@link #setUrl} method has not been called.
40690 * This does not activate the panel, just updates its content.
40692 refresh : function(){
40693 if(this.refreshDelegate){
40694 this.loaded = false;
40695 this.refreshDelegate();
40700 * Destroys this panel
40702 destroy : function(){
40703 this.el.removeAllListeners();
40704 var tempEl = document.createElement("span");
40705 tempEl.appendChild(this.el.dom);
40706 tempEl.innerHTML = "";
40712 * form - if the content panel contains a form - this is a reference to it.
40713 * @type {Roo.form.Form}
40717 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40718 * This contains a reference to it.
40724 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40734 * @param {Object} cfg Xtype definition of item to add.
40738 getChildContainer: function () {
40739 return this.getEl();
40743 onScroll : function(e)
40745 this.fireEvent('scroll', this, e);
40750 var ret = new Roo.factory(cfg);
40755 if (cfg.xtype.match(/^Form$/)) {
40758 //if (this.footer) {
40759 // el = this.footer.container.insertSibling(false, 'before');
40761 el = this.el.createChild();
40764 this.form = new Roo.form.Form(cfg);
40767 if ( this.form.allItems.length) {
40768 this.form.render(el.dom);
40772 // should only have one of theses..
40773 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40774 // views.. should not be just added - used named prop 'view''
40776 cfg.el = this.el.appendChild(document.createElement("div"));
40779 var ret = new Roo.factory(cfg);
40781 ret.render && ret.render(false, ''); // render blank..
40791 * @class Roo.bootstrap.panel.Grid
40792 * @extends Roo.bootstrap.panel.Content
40794 * Create a new GridPanel.
40795 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40796 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40797 * @param {Object} config A the config object
40803 Roo.bootstrap.panel.Grid = function(config)
40807 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40808 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40810 config.el = this.wrapper;
40811 //this.el = this.wrapper;
40813 if (config.container) {
40814 // ctor'ed from a Border/panel.grid
40817 this.wrapper.setStyle("overflow", "hidden");
40818 this.wrapper.addClass('roo-grid-container');
40823 if(config.toolbar){
40824 var tool_el = this.wrapper.createChild();
40825 this.toolbar = Roo.factory(config.toolbar);
40827 if (config.toolbar.items) {
40828 ti = config.toolbar.items ;
40829 delete config.toolbar.items ;
40833 this.toolbar.render(tool_el);
40834 for(var i =0;i < ti.length;i++) {
40835 // Roo.log(['add child', items[i]]);
40836 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40838 this.toolbar.items = nitems;
40840 delete config.toolbar;
40843 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40844 config.grid.scrollBody = true;;
40845 config.grid.monitorWindowResize = false; // turn off autosizing
40846 config.grid.autoHeight = false;
40847 config.grid.autoWidth = false;
40849 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40851 if (config.background) {
40852 // render grid on panel activation (if panel background)
40853 this.on('activate', function(gp) {
40854 if (!gp.grid.rendered) {
40855 gp.grid.render(this.wrapper);
40856 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40861 this.grid.render(this.wrapper);
40862 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40865 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40866 // ??? needed ??? config.el = this.wrapper;
40871 // xtype created footer. - not sure if will work as we normally have to render first..
40872 if (this.footer && !this.footer.el && this.footer.xtype) {
40874 var ctr = this.grid.getView().getFooterPanel(true);
40875 this.footer.dataSource = this.grid.dataSource;
40876 this.footer = Roo.factory(this.footer, Roo);
40877 this.footer.render(ctr);
40887 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40888 getId : function(){
40889 return this.grid.id;
40893 * Returns the grid for this panel
40894 * @return {Roo.bootstrap.Table}
40896 getGrid : function(){
40900 setSize : function(width, height){
40901 if(!this.ignoreResize(width, height)){
40902 var grid = this.grid;
40903 var size = this.adjustForComponents(width, height);
40904 // tfoot is not a footer?
40907 var gridel = grid.getGridEl();
40908 gridel.setSize(size.width, size.height);
40910 var tbd = grid.getGridEl().select('tbody', true).first();
40911 var thd = grid.getGridEl().select('thead',true).first();
40912 var tbf= grid.getGridEl().select('tfoot', true).first();
40915 size.height -= tbf.getHeight();
40918 size.height -= thd.getHeight();
40921 tbd.setSize(size.width, size.height );
40922 // this is for the account management tab -seems to work there.
40923 var thd = grid.getGridEl().select('thead',true).first();
40925 // tbd.setSize(size.width, size.height - thd.getHeight());
40934 beforeSlide : function(){
40935 this.grid.getView().scroller.clip();
40938 afterSlide : function(){
40939 this.grid.getView().scroller.unclip();
40942 destroy : function(){
40943 this.grid.destroy();
40945 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40950 * @class Roo.bootstrap.panel.Nest
40951 * @extends Roo.bootstrap.panel.Content
40953 * Create a new Panel, that can contain a layout.Border.
40956 * @param {String/Object} config A string to set only the title or a config object
40958 Roo.bootstrap.panel.Nest = function(config)
40960 // construct with only one argument..
40961 /* FIXME - implement nicer consturctors
40962 if (layout.layout) {
40964 layout = config.layout;
40965 delete config.layout;
40967 if (layout.xtype && !layout.getEl) {
40968 // then layout needs constructing..
40969 layout = Roo.factory(layout, Roo);
40973 config.el = config.layout.getEl();
40975 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40977 config.layout.monitorWindowResize = false; // turn off autosizing
40978 this.layout = config.layout;
40979 this.layout.getEl().addClass("roo-layout-nested-layout");
40980 this.layout.parent = this;
40987 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40989 * @cfg {Roo.BorderLayout} layout The layout for this panel
40993 setSize : function(width, height){
40994 if(!this.ignoreResize(width, height)){
40995 var size = this.adjustForComponents(width, height);
40996 var el = this.layout.getEl();
40997 if (size.height < 1) {
40998 el.setWidth(size.width);
41000 el.setSize(size.width, size.height);
41002 var touch = el.dom.offsetWidth;
41003 this.layout.layout();
41004 // ie requires a double layout on the first pass
41005 if(Roo.isIE && !this.initialized){
41006 this.initialized = true;
41007 this.layout.layout();
41012 // activate all subpanels if not currently active..
41014 setActiveState : function(active){
41015 this.active = active;
41016 this.setActiveClass(active);
41019 this.fireEvent("deactivate", this);
41023 this.fireEvent("activate", this);
41024 // not sure if this should happen before or after..
41025 if (!this.layout) {
41026 return; // should not happen..
41029 for (var r in this.layout.regions) {
41030 reg = this.layout.getRegion(r);
41031 if (reg.getActivePanel()) {
41032 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41033 reg.setActivePanel(reg.getActivePanel());
41036 if (!reg.panels.length) {
41039 reg.showPanel(reg.getPanel(0));
41048 * Returns the nested BorderLayout for this panel
41049 * @return {Roo.BorderLayout}
41051 getLayout : function(){
41052 return this.layout;
41056 * Adds a xtype elements to the layout of the nested panel
41060 xtype : 'ContentPanel',
41067 xtype : 'NestedLayoutPanel',
41073 items : [ ... list of content panels or nested layout panels.. ]
41077 * @param {Object} cfg Xtype definition of item to add.
41079 addxtype : function(cfg) {
41080 return this.layout.addxtype(cfg);
41085 * Ext JS Library 1.1.1
41086 * Copyright(c) 2006-2007, Ext JS, LLC.
41088 * Originally Released Under LGPL - original licence link has changed is not relivant.
41091 * <script type="text/javascript">
41094 * @class Roo.TabPanel
41095 * @extends Roo.util.Observable
41096 * A lightweight tab container.
41100 // basic tabs 1, built from existing content
41101 var tabs = new Roo.TabPanel("tabs1");
41102 tabs.addTab("script", "View Script");
41103 tabs.addTab("markup", "View Markup");
41104 tabs.activate("script");
41106 // more advanced tabs, built from javascript
41107 var jtabs = new Roo.TabPanel("jtabs");
41108 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41110 // set up the UpdateManager
41111 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41112 var updater = tab2.getUpdateManager();
41113 updater.setDefaultUrl("ajax1.htm");
41114 tab2.on('activate', updater.refresh, updater, true);
41116 // Use setUrl for Ajax loading
41117 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41118 tab3.setUrl("ajax2.htm", null, true);
41121 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41124 jtabs.activate("jtabs-1");
41127 * Create a new TabPanel.
41128 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41129 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41131 Roo.bootstrap.panel.Tabs = function(config){
41133 * The container element for this TabPanel.
41134 * @type Roo.Element
41136 this.el = Roo.get(config.el);
41139 if(typeof config == "boolean"){
41140 this.tabPosition = config ? "bottom" : "top";
41142 Roo.apply(this, config);
41146 if(this.tabPosition == "bottom"){
41147 // if tabs are at the bottom = create the body first.
41148 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41149 this.el.addClass("roo-tabs-bottom");
41151 // next create the tabs holders
41153 if (this.tabPosition == "west"){
41155 var reg = this.region; // fake it..
41157 if (!reg.mgr.parent) {
41160 reg = reg.mgr.parent.region;
41162 Roo.log("got nest?");
41164 if (reg.mgr.getRegion('west')) {
41165 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41166 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41167 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41168 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41169 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41177 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41178 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41179 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41180 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41185 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41188 // finally - if tabs are at the top, then create the body last..
41189 if(this.tabPosition != "bottom"){
41190 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41191 * @type Roo.Element
41193 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41194 this.el.addClass("roo-tabs-top");
41198 this.bodyEl.setStyle("position", "relative");
41200 this.active = null;
41201 this.activateDelegate = this.activate.createDelegate(this);
41206 * Fires when the active tab changes
41207 * @param {Roo.TabPanel} this
41208 * @param {Roo.TabPanelItem} activePanel The new active tab
41212 * @event beforetabchange
41213 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41214 * @param {Roo.TabPanel} this
41215 * @param {Object} e Set cancel to true on this object to cancel the tab change
41216 * @param {Roo.TabPanelItem} tab The tab being changed to
41218 "beforetabchange" : true
41221 Roo.EventManager.onWindowResize(this.onResize, this);
41222 this.cpad = this.el.getPadding("lr");
41223 this.hiddenCount = 0;
41226 // toolbar on the tabbar support...
41227 if (this.toolbar) {
41228 alert("no toolbar support yet");
41229 this.toolbar = false;
41231 var tcfg = this.toolbar;
41232 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41233 this.toolbar = new Roo.Toolbar(tcfg);
41234 if (Roo.isSafari) {
41235 var tbl = tcfg.container.child('table', true);
41236 tbl.setAttribute('width', '100%');
41244 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41247 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41249 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41251 tabPosition : "top",
41253 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41255 currentTabWidth : 0,
41257 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41261 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41265 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41267 preferredTabWidth : 175,
41269 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41271 resizeTabs : false,
41273 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41275 monitorResize : true,
41277 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41279 toolbar : false, // set by caller..
41281 region : false, /// set by caller
41283 disableTooltips : true, // not used yet...
41286 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41287 * @param {String} id The id of the div to use <b>or create</b>
41288 * @param {String} text The text for the tab
41289 * @param {String} content (optional) Content to put in the TabPanelItem body
41290 * @param {Boolean} closable (optional) True to create a close icon on the tab
41291 * @return {Roo.TabPanelItem} The created TabPanelItem
41293 addTab : function(id, text, content, closable, tpl)
41295 var item = new Roo.bootstrap.panel.TabItem({
41299 closable : closable,
41302 this.addTabItem(item);
41304 item.setContent(content);
41310 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41311 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41312 * @return {Roo.TabPanelItem}
41314 getTab : function(id){
41315 return this.items[id];
41319 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41320 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41322 hideTab : function(id){
41323 var t = this.items[id];
41326 this.hiddenCount++;
41327 this.autoSizeTabs();
41332 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41333 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41335 unhideTab : function(id){
41336 var t = this.items[id];
41338 t.setHidden(false);
41339 this.hiddenCount--;
41340 this.autoSizeTabs();
41345 * Adds an existing {@link Roo.TabPanelItem}.
41346 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41348 addTabItem : function(item)
41350 this.items[item.id] = item;
41351 this.items.push(item);
41352 this.autoSizeTabs();
41353 // if(this.resizeTabs){
41354 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41355 // this.autoSizeTabs();
41357 // item.autoSize();
41362 * Removes a {@link Roo.TabPanelItem}.
41363 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41365 removeTab : function(id){
41366 var items = this.items;
41367 var tab = items[id];
41368 if(!tab) { return; }
41369 var index = items.indexOf(tab);
41370 if(this.active == tab && items.length > 1){
41371 var newTab = this.getNextAvailable(index);
41376 this.stripEl.dom.removeChild(tab.pnode.dom);
41377 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41378 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41380 items.splice(index, 1);
41381 delete this.items[tab.id];
41382 tab.fireEvent("close", tab);
41383 tab.purgeListeners();
41384 this.autoSizeTabs();
41387 getNextAvailable : function(start){
41388 var items = this.items;
41390 // look for a next tab that will slide over to
41391 // replace the one being removed
41392 while(index < items.length){
41393 var item = items[++index];
41394 if(item && !item.isHidden()){
41398 // if one isn't found select the previous tab (on the left)
41401 var item = items[--index];
41402 if(item && !item.isHidden()){
41410 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41411 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41413 disableTab : function(id){
41414 var tab = this.items[id];
41415 if(tab && this.active != tab){
41421 * Enables a {@link Roo.TabPanelItem} that is disabled.
41422 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41424 enableTab : function(id){
41425 var tab = this.items[id];
41430 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41431 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41432 * @return {Roo.TabPanelItem} The TabPanelItem.
41434 activate : function(id)
41436 //Roo.log('activite:' + id);
41438 var tab = this.items[id];
41442 if(tab == this.active || tab.disabled){
41446 this.fireEvent("beforetabchange", this, e, tab);
41447 if(e.cancel !== true && !tab.disabled){
41449 this.active.hide();
41451 this.active = this.items[id];
41452 this.active.show();
41453 this.fireEvent("tabchange", this, this.active);
41459 * Gets the active {@link Roo.TabPanelItem}.
41460 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41462 getActiveTab : function(){
41463 return this.active;
41467 * Updates the tab body element to fit the height of the container element
41468 * for overflow scrolling
41469 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41471 syncHeight : function(targetHeight){
41472 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41473 var bm = this.bodyEl.getMargins();
41474 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41475 this.bodyEl.setHeight(newHeight);
41479 onResize : function(){
41480 if(this.monitorResize){
41481 this.autoSizeTabs();
41486 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41488 beginUpdate : function(){
41489 this.updating = true;
41493 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41495 endUpdate : function(){
41496 this.updating = false;
41497 this.autoSizeTabs();
41501 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41503 autoSizeTabs : function()
41505 var count = this.items.length;
41506 var vcount = count - this.hiddenCount;
41509 this.stripEl.hide();
41511 this.stripEl.show();
41514 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41519 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41520 var availWidth = Math.floor(w / vcount);
41521 var b = this.stripBody;
41522 if(b.getWidth() > w){
41523 var tabs = this.items;
41524 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41525 if(availWidth < this.minTabWidth){
41526 /*if(!this.sleft){ // incomplete scrolling code
41527 this.createScrollButtons();
41530 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41533 if(this.currentTabWidth < this.preferredTabWidth){
41534 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41540 * Returns the number of tabs in this TabPanel.
41543 getCount : function(){
41544 return this.items.length;
41548 * Resizes all the tabs to the passed width
41549 * @param {Number} The new width
41551 setTabWidth : function(width){
41552 this.currentTabWidth = width;
41553 for(var i = 0, len = this.items.length; i < len; i++) {
41554 if(!this.items[i].isHidden()) {
41555 this.items[i].setWidth(width);
41561 * Destroys this TabPanel
41562 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41564 destroy : function(removeEl){
41565 Roo.EventManager.removeResizeListener(this.onResize, this);
41566 for(var i = 0, len = this.items.length; i < len; i++){
41567 this.items[i].purgeListeners();
41569 if(removeEl === true){
41570 this.el.update("");
41575 createStrip : function(container)
41577 var strip = document.createElement("nav");
41578 strip.className = Roo.bootstrap.version == 4 ?
41579 "navbar-light bg-light" :
41580 "navbar navbar-default"; //"x-tabs-wrap";
41581 container.appendChild(strip);
41585 createStripList : function(strip)
41587 // div wrapper for retard IE
41588 // returns the "tr" element.
41589 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41590 //'<div class="x-tabs-strip-wrap">'+
41591 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41592 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41593 return strip.firstChild; //.firstChild.firstChild.firstChild;
41595 createBody : function(container)
41597 var body = document.createElement("div");
41598 Roo.id(body, "tab-body");
41599 //Roo.fly(body).addClass("x-tabs-body");
41600 Roo.fly(body).addClass("tab-content");
41601 container.appendChild(body);
41604 createItemBody :function(bodyEl, id){
41605 var body = Roo.getDom(id);
41607 body = document.createElement("div");
41610 //Roo.fly(body).addClass("x-tabs-item-body");
41611 Roo.fly(body).addClass("tab-pane");
41612 bodyEl.insertBefore(body, bodyEl.firstChild);
41616 createStripElements : function(stripEl, text, closable, tpl)
41618 var td = document.createElement("li"); // was td..
41619 td.className = 'nav-item';
41621 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41624 stripEl.appendChild(td);
41626 td.className = "x-tabs-closable";
41627 if(!this.closeTpl){
41628 this.closeTpl = new Roo.Template(
41629 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41630 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41631 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41634 var el = this.closeTpl.overwrite(td, {"text": text});
41635 var close = el.getElementsByTagName("div")[0];
41636 var inner = el.getElementsByTagName("em")[0];
41637 return {"el": el, "close": close, "inner": inner};
41640 // not sure what this is..
41641 // if(!this.tabTpl){
41642 //this.tabTpl = new Roo.Template(
41643 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41646 // this.tabTpl = new Roo.Template(
41647 // '<a href="#">' +
41648 // '<span unselectable="on"' +
41649 // (this.disableTooltips ? '' : ' title="{text}"') +
41650 // ' >{text}</span></a>'
41656 var template = tpl || this.tabTpl || false;
41659 template = new Roo.Template(
41660 Roo.bootstrap.version == 4 ?
41662 '<a class="nav-link" href="#" unselectable="on"' +
41663 (this.disableTooltips ? '' : ' title="{text}"') +
41666 '<a class="nav-link" href="#">' +
41667 '<span unselectable="on"' +
41668 (this.disableTooltips ? '' : ' title="{text}"') +
41669 ' >{text}</span></a>'
41674 switch (typeof(template)) {
41678 template = new Roo.Template(template);
41684 var el = template.overwrite(td, {"text": text});
41686 var inner = el.getElementsByTagName("span")[0];
41688 return {"el": el, "inner": inner};
41696 * @class Roo.TabPanelItem
41697 * @extends Roo.util.Observable
41698 * Represents an individual item (tab plus body) in a TabPanel.
41699 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41700 * @param {String} id The id of this TabPanelItem
41701 * @param {String} text The text for the tab of this TabPanelItem
41702 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41704 Roo.bootstrap.panel.TabItem = function(config){
41706 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41707 * @type Roo.TabPanel
41709 this.tabPanel = config.panel;
41711 * The id for this TabPanelItem
41714 this.id = config.id;
41716 this.disabled = false;
41718 this.text = config.text;
41720 this.loaded = false;
41721 this.closable = config.closable;
41724 * The body element for this TabPanelItem.
41725 * @type Roo.Element
41727 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41728 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41729 this.bodyEl.setStyle("display", "block");
41730 this.bodyEl.setStyle("zoom", "1");
41731 //this.hideAction();
41733 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41735 this.el = Roo.get(els.el);
41736 this.inner = Roo.get(els.inner, true);
41737 this.textEl = Roo.bootstrap.version == 4 ?
41738 this.el : Roo.get(this.el.dom.firstChild, true);
41740 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41741 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41744 // this.el.on("mousedown", this.onTabMouseDown, this);
41745 this.el.on("click", this.onTabClick, this);
41747 if(config.closable){
41748 var c = Roo.get(els.close, true);
41749 c.dom.title = this.closeText;
41750 c.addClassOnOver("close-over");
41751 c.on("click", this.closeClick, this);
41757 * Fires when this tab becomes the active tab.
41758 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41759 * @param {Roo.TabPanelItem} this
41763 * @event beforeclose
41764 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41765 * @param {Roo.TabPanelItem} this
41766 * @param {Object} e Set cancel to true on this object to cancel the close.
41768 "beforeclose": true,
41771 * Fires when this tab is closed.
41772 * @param {Roo.TabPanelItem} this
41776 * @event deactivate
41777 * Fires when this tab is no longer the active tab.
41778 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41779 * @param {Roo.TabPanelItem} this
41781 "deactivate" : true
41783 this.hidden = false;
41785 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41788 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41790 purgeListeners : function(){
41791 Roo.util.Observable.prototype.purgeListeners.call(this);
41792 this.el.removeAllListeners();
41795 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41798 this.status_node.addClass("active");
41801 this.tabPanel.stripWrap.repaint();
41803 this.fireEvent("activate", this.tabPanel, this);
41807 * Returns true if this tab is the active tab.
41808 * @return {Boolean}
41810 isActive : function(){
41811 return this.tabPanel.getActiveTab() == this;
41815 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41818 this.status_node.removeClass("active");
41820 this.fireEvent("deactivate", this.tabPanel, this);
41823 hideAction : function(){
41824 this.bodyEl.hide();
41825 this.bodyEl.setStyle("position", "absolute");
41826 this.bodyEl.setLeft("-20000px");
41827 this.bodyEl.setTop("-20000px");
41830 showAction : function(){
41831 this.bodyEl.setStyle("position", "relative");
41832 this.bodyEl.setTop("");
41833 this.bodyEl.setLeft("");
41834 this.bodyEl.show();
41838 * Set the tooltip for the tab.
41839 * @param {String} tooltip The tab's tooltip
41841 setTooltip : function(text){
41842 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41843 this.textEl.dom.qtip = text;
41844 this.textEl.dom.removeAttribute('title');
41846 this.textEl.dom.title = text;
41850 onTabClick : function(e){
41851 e.preventDefault();
41852 this.tabPanel.activate(this.id);
41855 onTabMouseDown : function(e){
41856 e.preventDefault();
41857 this.tabPanel.activate(this.id);
41860 getWidth : function(){
41861 return this.inner.getWidth();
41864 setWidth : function(width){
41865 var iwidth = width - this.linode.getPadding("lr");
41866 this.inner.setWidth(iwidth);
41867 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41868 this.linode.setWidth(width);
41872 * Show or hide the tab
41873 * @param {Boolean} hidden True to hide or false to show.
41875 setHidden : function(hidden){
41876 this.hidden = hidden;
41877 this.linode.setStyle("display", hidden ? "none" : "");
41881 * Returns true if this tab is "hidden"
41882 * @return {Boolean}
41884 isHidden : function(){
41885 return this.hidden;
41889 * Returns the text for this tab
41892 getText : function(){
41896 autoSize : function(){
41897 //this.el.beginMeasure();
41898 this.textEl.setWidth(1);
41900 * #2804 [new] Tabs in Roojs
41901 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41903 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41904 //this.el.endMeasure();
41908 * Sets the text for the tab (Note: this also sets the tooltip text)
41909 * @param {String} text The tab's text and tooltip
41911 setText : function(text){
41913 this.textEl.update(text);
41914 this.setTooltip(text);
41915 //if(!this.tabPanel.resizeTabs){
41916 // this.autoSize();
41920 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41922 activate : function(){
41923 this.tabPanel.activate(this.id);
41927 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41929 disable : function(){
41930 if(this.tabPanel.active != this){
41931 this.disabled = true;
41932 this.status_node.addClass("disabled");
41937 * Enables this TabPanelItem if it was previously disabled.
41939 enable : function(){
41940 this.disabled = false;
41941 this.status_node.removeClass("disabled");
41945 * Sets the content for this TabPanelItem.
41946 * @param {String} content The content
41947 * @param {Boolean} loadScripts true to look for and load scripts
41949 setContent : function(content, loadScripts){
41950 this.bodyEl.update(content, loadScripts);
41954 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41955 * @return {Roo.UpdateManager} The UpdateManager
41957 getUpdateManager : function(){
41958 return this.bodyEl.getUpdateManager();
41962 * Set a URL to be used to load the content for this TabPanelItem.
41963 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41964 * @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)
41965 * @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)
41966 * @return {Roo.UpdateManager} The UpdateManager
41968 setUrl : function(url, params, loadOnce){
41969 if(this.refreshDelegate){
41970 this.un('activate', this.refreshDelegate);
41972 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41973 this.on("activate", this.refreshDelegate);
41974 return this.bodyEl.getUpdateManager();
41978 _handleRefresh : function(url, params, loadOnce){
41979 if(!loadOnce || !this.loaded){
41980 var updater = this.bodyEl.getUpdateManager();
41981 updater.update(url, params, this._setLoaded.createDelegate(this));
41986 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41987 * Will fail silently if the setUrl method has not been called.
41988 * This does not activate the panel, just updates its content.
41990 refresh : function(){
41991 if(this.refreshDelegate){
41992 this.loaded = false;
41993 this.refreshDelegate();
41998 _setLoaded : function(){
41999 this.loaded = true;
42003 closeClick : function(e){
42006 this.fireEvent("beforeclose", this, o);
42007 if(o.cancel !== true){
42008 this.tabPanel.removeTab(this.id);
42012 * The text displayed in the tooltip for the close icon.
42015 closeText : "Close this tab"
42018 * This script refer to:
42019 * Title: International Telephone Input
42020 * Author: Jack O'Connor
42021 * Code version: v12.1.12
42022 * Availability: https://github.com/jackocnr/intl-tel-input.git
42025 Roo.bootstrap.form.PhoneInputData = function() {
42028 "Afghanistan (افغانستان)",
42033 "Albania (Shqipëri)",
42038 "Algeria (الجزائر)",
42063 "Antigua and Barbuda",
42073 "Armenia (Հայաստան)",
42089 "Austria (Österreich)",
42094 "Azerbaijan (Azərbaycan)",
42104 "Bahrain (البحرين)",
42109 "Bangladesh (বাংলাদেশ)",
42119 "Belarus (Беларусь)",
42124 "Belgium (België)",
42154 "Bosnia and Herzegovina (Босна и Херцеговина)",
42169 "British Indian Ocean Territory",
42174 "British Virgin Islands",
42184 "Bulgaria (България)",
42194 "Burundi (Uburundi)",
42199 "Cambodia (កម្ពុជា)",
42204 "Cameroon (Cameroun)",
42213 ["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"]
42216 "Cape Verde (Kabu Verdi)",
42221 "Caribbean Netherlands",
42232 "Central African Republic (République centrafricaine)",
42252 "Christmas Island",
42258 "Cocos (Keeling) Islands",
42269 "Comoros (جزر القمر)",
42274 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42279 "Congo (Republic) (Congo-Brazzaville)",
42299 "Croatia (Hrvatska)",
42320 "Czech Republic (Česká republika)",
42325 "Denmark (Danmark)",
42340 "Dominican Republic (República Dominicana)",
42344 ["809", "829", "849"]
42362 "Equatorial Guinea (Guinea Ecuatorial)",
42382 "Falkland Islands (Islas Malvinas)",
42387 "Faroe Islands (Føroyar)",
42408 "French Guiana (Guyane française)",
42413 "French Polynesia (Polynésie française)",
42428 "Georgia (საქართველო)",
42433 "Germany (Deutschland)",
42453 "Greenland (Kalaallit Nunaat)",
42490 "Guinea-Bissau (Guiné Bissau)",
42515 "Hungary (Magyarország)",
42520 "Iceland (Ísland)",
42540 "Iraq (العراق)",
42556 "Israel (ישראל)",
42583 "Jordan (الأردن)",
42588 "Kazakhstan (Казахстан)",
42609 "Kuwait (الكويت)",
42614 "Kyrgyzstan (Кыргызстан)",
42624 "Latvia (Latvija)",
42629 "Lebanon (لبنان)",
42644 "Libya (ليبيا)",
42654 "Lithuania (Lietuva)",
42669 "Macedonia (FYROM) (Македонија)",
42674 "Madagascar (Madagasikara)",
42704 "Marshall Islands",
42714 "Mauritania (موريتانيا)",
42719 "Mauritius (Moris)",
42740 "Moldova (Republica Moldova)",
42750 "Mongolia (Монгол)",
42755 "Montenegro (Crna Gora)",
42765 "Morocco (المغرب)",
42771 "Mozambique (Moçambique)",
42776 "Myanmar (Burma) (မြန်မာ)",
42781 "Namibia (Namibië)",
42796 "Netherlands (Nederland)",
42801 "New Caledonia (Nouvelle-Calédonie)",
42836 "North Korea (조선 민주주의 인민 공화국)",
42841 "Northern Mariana Islands",
42857 "Pakistan (پاکستان)",
42867 "Palestine (فلسطين)",
42877 "Papua New Guinea",
42919 "Réunion (La Réunion)",
42925 "Romania (România)",
42941 "Saint Barthélemy",
42952 "Saint Kitts and Nevis",
42962 "Saint Martin (Saint-Martin (partie française))",
42968 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42973 "Saint Vincent and the Grenadines",
42988 "São Tomé and Príncipe (São Tomé e Príncipe)",
42993 "Saudi Arabia (المملكة العربية السعودية)",
42998 "Senegal (Sénégal)",
43028 "Slovakia (Slovensko)",
43033 "Slovenia (Slovenija)",
43043 "Somalia (Soomaaliya)",
43053 "South Korea (대한민국)",
43058 "South Sudan (جنوب السودان)",
43068 "Sri Lanka (ශ්රී ලංකාව)",
43073 "Sudan (السودان)",
43083 "Svalbard and Jan Mayen",
43094 "Sweden (Sverige)",
43099 "Switzerland (Schweiz)",
43104 "Syria (سوريا)",
43149 "Trinidad and Tobago",
43154 "Tunisia (تونس)",
43159 "Turkey (Türkiye)",
43169 "Turks and Caicos Islands",
43179 "U.S. Virgin Islands",
43189 "Ukraine (Україна)",
43194 "United Arab Emirates (الإمارات العربية المتحدة)",
43216 "Uzbekistan (Oʻzbekiston)",
43226 "Vatican City (Città del Vaticano)",
43237 "Vietnam (Việt Nam)",
43242 "Wallis and Futuna (Wallis-et-Futuna)",
43247 "Western Sahara (الصحراء الغربية)",
43253 "Yemen (اليمن)",
43277 * This script refer to:
43278 * Title: International Telephone Input
43279 * Author: Jack O'Connor
43280 * Code version: v12.1.12
43281 * Availability: https://github.com/jackocnr/intl-tel-input.git
43285 * @class Roo.bootstrap.form.PhoneInput
43286 * @extends Roo.bootstrap.form.TriggerField
43287 * An input with International dial-code selection
43289 * @cfg {String} defaultDialCode default '+852'
43290 * @cfg {Array} preferedCountries default []
43293 * Create a new PhoneInput.
43294 * @param {Object} config Configuration options
43297 Roo.bootstrap.form.PhoneInput = function(config) {
43298 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43301 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43303 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43305 listWidth: undefined,
43307 selectedClass: 'active',
43309 invalidClass : "has-warning",
43311 validClass: 'has-success',
43313 allowed: '0123456789',
43318 * @cfg {String} defaultDialCode The default dial code when initializing the input
43320 defaultDialCode: '+852',
43323 * @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
43325 preferedCountries: false,
43327 getAutoCreate : function()
43329 var data = Roo.bootstrap.form.PhoneInputData();
43330 var align = this.labelAlign || this.parentLabelAlign();
43333 this.allCountries = [];
43334 this.dialCodeMapping = [];
43336 for (var i = 0; i < data.length; i++) {
43338 this.allCountries[i] = {
43342 priority: c[3] || 0,
43343 areaCodes: c[4] || null
43345 this.dialCodeMapping[c[2]] = {
43348 priority: c[3] || 0,
43349 areaCodes: c[4] || null
43361 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43362 maxlength: this.max_length,
43363 cls : 'form-control tel-input',
43364 autocomplete: 'new-password'
43367 var hiddenInput = {
43370 cls: 'hidden-tel-input'
43374 hiddenInput.name = this.name;
43377 if (this.disabled) {
43378 input.disabled = true;
43381 var flag_container = {
43398 cls: this.hasFeedback ? 'has-feedback' : '',
43404 cls: 'dial-code-holder',
43411 cls: 'roo-select2-container input-group',
43418 if (this.fieldLabel.length) {
43421 tooltip: 'This field is required'
43427 cls: 'control-label',
43433 html: this.fieldLabel
43436 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43442 if(this.indicatorpos == 'right') {
43443 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43450 if(align == 'left') {
43458 if(this.labelWidth > 12){
43459 label.style = "width: " + this.labelWidth + 'px';
43461 if(this.labelWidth < 13 && this.labelmd == 0){
43462 this.labelmd = this.labelWidth;
43464 if(this.labellg > 0){
43465 label.cls += ' col-lg-' + this.labellg;
43466 input.cls += ' col-lg-' + (12 - this.labellg);
43468 if(this.labelmd > 0){
43469 label.cls += ' col-md-' + this.labelmd;
43470 container.cls += ' col-md-' + (12 - this.labelmd);
43472 if(this.labelsm > 0){
43473 label.cls += ' col-sm-' + this.labelsm;
43474 container.cls += ' col-sm-' + (12 - this.labelsm);
43476 if(this.labelxs > 0){
43477 label.cls += ' col-xs-' + this.labelxs;
43478 container.cls += ' col-xs-' + (12 - this.labelxs);
43488 var settings = this;
43490 ['xs','sm','md','lg'].map(function(size){
43491 if (settings[size]) {
43492 cfg.cls += ' col-' + size + '-' + settings[size];
43496 this.store = new Roo.data.Store({
43497 proxy : new Roo.data.MemoryProxy({}),
43498 reader : new Roo.data.JsonReader({
43509 'name' : 'dialCode',
43513 'name' : 'priority',
43517 'name' : 'areaCodes',
43524 if(!this.preferedCountries) {
43525 this.preferedCountries = [
43532 var p = this.preferedCountries.reverse();
43535 for (var i = 0; i < p.length; i++) {
43536 for (var j = 0; j < this.allCountries.length; j++) {
43537 if(this.allCountries[j].iso2 == p[i]) {
43538 var t = this.allCountries[j];
43539 this.allCountries.splice(j,1);
43540 this.allCountries.unshift(t);
43546 this.store.proxy.data = {
43548 data: this.allCountries
43554 initEvents : function()
43557 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43559 this.indicator = this.indicatorEl();
43560 this.flag = this.flagEl();
43561 this.dialCodeHolder = this.dialCodeHolderEl();
43563 this.trigger = this.el.select('div.flag-box',true).first();
43564 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43569 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43570 _this.list.setWidth(lw);
43573 this.list.on('mouseover', this.onViewOver, this);
43574 this.list.on('mousemove', this.onViewMove, this);
43575 this.inputEl().on("keyup", this.onKeyUp, this);
43576 this.inputEl().on("keypress", this.onKeyPress, this);
43578 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43580 this.view = new Roo.View(this.list, this.tpl, {
43581 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43584 this.view.on('click', this.onViewClick, this);
43585 this.setValue(this.defaultDialCode);
43588 onTriggerClick : function(e)
43590 Roo.log('trigger click');
43595 if(this.isExpanded()){
43597 this.hasFocus = false;
43599 this.store.load({});
43600 this.hasFocus = true;
43605 isExpanded : function()
43607 return this.list.isVisible();
43610 collapse : function()
43612 if(!this.isExpanded()){
43616 Roo.get(document).un('mousedown', this.collapseIf, this);
43617 Roo.get(document).un('mousewheel', this.collapseIf, this);
43618 this.fireEvent('collapse', this);
43622 expand : function()
43626 if(this.isExpanded() || !this.hasFocus){
43630 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43631 this.list.setWidth(lw);
43634 this.restrictHeight();
43636 Roo.get(document).on('mousedown', this.collapseIf, this);
43637 Roo.get(document).on('mousewheel', this.collapseIf, this);
43639 this.fireEvent('expand', this);
43642 restrictHeight : function()
43644 this.list.alignTo(this.inputEl(), this.listAlign);
43645 this.list.alignTo(this.inputEl(), this.listAlign);
43648 onViewOver : function(e, t)
43650 if(this.inKeyMode){
43653 var item = this.view.findItemFromChild(t);
43656 var index = this.view.indexOf(item);
43657 this.select(index, false);
43662 onViewClick : function(view, doFocus, el, e)
43664 var index = this.view.getSelectedIndexes()[0];
43666 var r = this.store.getAt(index);
43669 this.onSelect(r, index);
43671 if(doFocus !== false && !this.blockFocus){
43672 this.inputEl().focus();
43676 onViewMove : function(e, t)
43678 this.inKeyMode = false;
43681 select : function(index, scrollIntoView)
43683 this.selectedIndex = index;
43684 this.view.select(index);
43685 if(scrollIntoView !== false){
43686 var el = this.view.getNode(index);
43688 this.list.scrollChildIntoView(el, false);
43693 createList : function()
43695 this.list = Roo.get(document.body).createChild({
43697 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43698 style: 'display:none'
43701 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43704 collapseIf : function(e)
43706 var in_combo = e.within(this.el);
43707 var in_list = e.within(this.list);
43708 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43710 if (in_combo || in_list || is_list) {
43716 onSelect : function(record, index)
43718 if(this.fireEvent('beforeselect', this, record, index) !== false){
43720 this.setFlagClass(record.data.iso2);
43721 this.setDialCode(record.data.dialCode);
43722 this.hasFocus = false;
43724 this.fireEvent('select', this, record, index);
43728 flagEl : function()
43730 var flag = this.el.select('div.flag',true).first();
43737 dialCodeHolderEl : function()
43739 var d = this.el.select('input.dial-code-holder',true).first();
43746 setDialCode : function(v)
43748 this.dialCodeHolder.dom.value = '+'+v;
43751 setFlagClass : function(n)
43753 this.flag.dom.className = 'flag '+n;
43756 getValue : function()
43758 var v = this.inputEl().getValue();
43759 if(this.dialCodeHolder) {
43760 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43765 setValue : function(v)
43767 var d = this.getDialCode(v);
43769 //invalid dial code
43770 if(v.length == 0 || !d || d.length == 0) {
43772 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43773 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43779 this.setFlagClass(this.dialCodeMapping[d].iso2);
43780 this.setDialCode(d);
43781 this.inputEl().dom.value = v.replace('+'+d,'');
43782 this.hiddenEl().dom.value = this.getValue();
43787 getDialCode : function(v)
43791 if (v.length == 0) {
43792 return this.dialCodeHolder.dom.value;
43796 if (v.charAt(0) != "+") {
43799 var numericChars = "";
43800 for (var i = 1; i < v.length; i++) {
43801 var c = v.charAt(i);
43804 if (this.dialCodeMapping[numericChars]) {
43805 dialCode = v.substr(1, i);
43807 if (numericChars.length == 4) {
43817 this.setValue(this.defaultDialCode);
43821 hiddenEl : function()
43823 return this.el.select('input.hidden-tel-input',true).first();
43826 // after setting val
43827 onKeyUp : function(e){
43828 this.setValue(this.getValue());
43831 onKeyPress : function(e){
43832 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43839 * @class Roo.bootstrap.form.MoneyField
43840 * @extends Roo.bootstrap.form.ComboBox
43841 * Bootstrap MoneyField class
43844 * Create a new MoneyField.
43845 * @param {Object} config Configuration options
43848 Roo.bootstrap.form.MoneyField = function(config) {
43850 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43854 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43857 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43859 allowDecimals : true,
43861 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43863 decimalSeparator : ".",
43865 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43867 decimalPrecision : 0,
43869 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43871 allowNegative : true,
43873 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43877 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43879 minValue : Number.NEGATIVE_INFINITY,
43881 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43883 maxValue : Number.MAX_VALUE,
43885 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43887 minText : "The minimum value for this field is {0}",
43889 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43891 maxText : "The maximum value for this field is {0}",
43893 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43894 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43896 nanText : "{0} is not a valid number",
43898 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43902 * @cfg {String} defaults currency of the MoneyField
43903 * value should be in lkey
43905 defaultCurrency : false,
43907 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43909 thousandsDelimiter : false,
43911 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43920 * @cfg {Roo.data.Store} store Store to lookup currency??
43924 getAutoCreate : function()
43926 var align = this.labelAlign || this.parentLabelAlign();
43938 cls : 'form-control roo-money-amount-input',
43939 autocomplete: 'new-password'
43942 var hiddenInput = {
43946 cls: 'hidden-number-input'
43949 if(this.max_length) {
43950 input.maxlength = this.max_length;
43954 hiddenInput.name = this.name;
43957 if (this.disabled) {
43958 input.disabled = true;
43961 var clg = 12 - this.inputlg;
43962 var cmd = 12 - this.inputmd;
43963 var csm = 12 - this.inputsm;
43964 var cxs = 12 - this.inputxs;
43968 cls : 'row roo-money-field',
43972 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43976 cls: 'roo-select2-container input-group',
43980 cls : 'form-control roo-money-currency-input',
43981 autocomplete: 'new-password',
43983 name : this.currencyName
43987 cls : 'input-group-addon',
44001 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44005 cls: this.hasFeedback ? 'has-feedback' : '',
44016 if (this.fieldLabel.length) {
44019 tooltip: 'This field is required'
44025 cls: 'control-label',
44031 html: this.fieldLabel
44034 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44040 if(this.indicatorpos == 'right') {
44041 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44048 if(align == 'left') {
44056 if(this.labelWidth > 12){
44057 label.style = "width: " + this.labelWidth + 'px';
44059 if(this.labelWidth < 13 && this.labelmd == 0){
44060 this.labelmd = this.labelWidth;
44062 if(this.labellg > 0){
44063 label.cls += ' col-lg-' + this.labellg;
44064 input.cls += ' col-lg-' + (12 - this.labellg);
44066 if(this.labelmd > 0){
44067 label.cls += ' col-md-' + this.labelmd;
44068 container.cls += ' col-md-' + (12 - this.labelmd);
44070 if(this.labelsm > 0){
44071 label.cls += ' col-sm-' + this.labelsm;
44072 container.cls += ' col-sm-' + (12 - this.labelsm);
44074 if(this.labelxs > 0){
44075 label.cls += ' col-xs-' + this.labelxs;
44076 container.cls += ' col-xs-' + (12 - this.labelxs);
44087 var settings = this;
44089 ['xs','sm','md','lg'].map(function(size){
44090 if (settings[size]) {
44091 cfg.cls += ' col-' + size + '-' + settings[size];
44098 initEvents : function()
44100 this.indicator = this.indicatorEl();
44102 this.initCurrencyEvent();
44104 this.initNumberEvent();
44107 initCurrencyEvent : function()
44110 throw "can not find store for combo";
44113 this.store = Roo.factory(this.store, Roo.data);
44114 this.store.parent = this;
44118 this.triggerEl = this.el.select('.input-group-addon', true).first();
44120 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44125 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44126 _this.list.setWidth(lw);
44129 this.list.on('mouseover', this.onViewOver, this);
44130 this.list.on('mousemove', this.onViewMove, this);
44131 this.list.on('scroll', this.onViewScroll, this);
44134 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44137 this.view = new Roo.View(this.list, this.tpl, {
44138 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44141 this.view.on('click', this.onViewClick, this);
44143 this.store.on('beforeload', this.onBeforeLoad, this);
44144 this.store.on('load', this.onLoad, this);
44145 this.store.on('loadexception', this.onLoadException, this);
44147 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44148 "up" : function(e){
44149 this.inKeyMode = true;
44153 "down" : function(e){
44154 if(!this.isExpanded()){
44155 this.onTriggerClick();
44157 this.inKeyMode = true;
44162 "enter" : function(e){
44165 if(this.fireEvent("specialkey", this, e)){
44166 this.onViewClick(false);
44172 "esc" : function(e){
44176 "tab" : function(e){
44179 if(this.fireEvent("specialkey", this, e)){
44180 this.onViewClick(false);
44188 doRelay : function(foo, bar, hname){
44189 if(hname == 'down' || this.scope.isExpanded()){
44190 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44198 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44202 initNumberEvent : function(e)
44204 this.inputEl().on("keydown" , this.fireKey, this);
44205 this.inputEl().on("focus", this.onFocus, this);
44206 this.inputEl().on("blur", this.onBlur, this);
44208 this.inputEl().relayEvent('keyup', this);
44210 if(this.indicator){
44211 this.indicator.addClass('invisible');
44214 this.originalValue = this.getValue();
44216 if(this.validationEvent == 'keyup'){
44217 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44218 this.inputEl().on('keyup', this.filterValidation, this);
44220 else if(this.validationEvent !== false){
44221 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44224 if(this.selectOnFocus){
44225 this.on("focus", this.preFocus, this);
44228 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44229 this.inputEl().on("keypress", this.filterKeys, this);
44231 this.inputEl().relayEvent('keypress', this);
44234 var allowed = "0123456789";
44236 if(this.allowDecimals){
44237 allowed += this.decimalSeparator;
44240 if(this.allowNegative){
44244 if(this.thousandsDelimiter) {
44248 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44250 var keyPress = function(e){
44252 var k = e.getKey();
44254 var c = e.getCharCode();
44257 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44258 allowed.indexOf(String.fromCharCode(c)) === -1
44264 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44268 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44273 this.inputEl().on("keypress", keyPress, this);
44277 onTriggerClick : function(e)
44284 this.loadNext = false;
44286 if(this.isExpanded()){
44291 this.hasFocus = true;
44293 if(this.triggerAction == 'all') {
44294 this.doQuery(this.allQuery, true);
44298 this.doQuery(this.getRawValue());
44301 getCurrency : function()
44303 var v = this.currencyEl().getValue();
44308 restrictHeight : function()
44310 this.list.alignTo(this.currencyEl(), this.listAlign);
44311 this.list.alignTo(this.currencyEl(), this.listAlign);
44314 onViewClick : function(view, doFocus, el, e)
44316 var index = this.view.getSelectedIndexes()[0];
44318 var r = this.store.getAt(index);
44321 this.onSelect(r, index);
44325 onSelect : function(record, index){
44327 if(this.fireEvent('beforeselect', this, record, index) !== false){
44329 this.setFromCurrencyData(index > -1 ? record.data : false);
44333 this.fireEvent('select', this, record, index);
44337 setFromCurrencyData : function(o)
44341 this.lastCurrency = o;
44343 if (this.currencyField) {
44344 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44346 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44349 this.lastSelectionText = currency;
44351 //setting default currency
44352 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44353 this.setCurrency(this.defaultCurrency);
44357 this.setCurrency(currency);
44360 setFromData : function(o)
44364 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44366 this.setFromCurrencyData(c);
44371 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44373 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44376 this.setValue(value);
44380 setCurrency : function(v)
44382 this.currencyValue = v;
44385 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44390 setValue : function(v)
44392 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44398 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44400 this.inputEl().dom.value = (v == '') ? '' :
44401 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44403 if(!this.allowZero && v === '0') {
44404 this.hiddenEl().dom.value = '';
44405 this.inputEl().dom.value = '';
44412 getRawValue : function()
44414 var v = this.inputEl().getValue();
44419 getValue : function()
44421 return this.fixPrecision(this.parseValue(this.getRawValue()));
44424 parseValue : function(value)
44426 if(this.thousandsDelimiter) {
44428 r = new RegExp(",", "g");
44429 value = value.replace(r, "");
44432 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44433 return isNaN(value) ? '' : value;
44437 fixPrecision : function(value)
44439 if(this.thousandsDelimiter) {
44441 r = new RegExp(",", "g");
44442 value = value.replace(r, "");
44445 var nan = isNaN(value);
44447 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44448 return nan ? '' : value;
44450 return parseFloat(value).toFixed(this.decimalPrecision);
44453 decimalPrecisionFcn : function(v)
44455 return Math.floor(v);
44458 validateValue : function(value)
44460 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44464 var num = this.parseValue(value);
44467 this.markInvalid(String.format(this.nanText, value));
44471 if(num < this.minValue){
44472 this.markInvalid(String.format(this.minText, this.minValue));
44476 if(num > this.maxValue){
44477 this.markInvalid(String.format(this.maxText, this.maxValue));
44484 validate : function()
44486 if(this.disabled || this.allowBlank){
44491 var currency = this.getCurrency();
44493 if(this.validateValue(this.getRawValue()) && currency.length){
44498 this.markInvalid();
44502 getName: function()
44507 beforeBlur : function()
44513 var v = this.parseValue(this.getRawValue());
44520 onBlur : function()
44524 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44525 //this.el.removeClass(this.focusClass);
44528 this.hasFocus = false;
44530 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44534 var v = this.getValue();
44536 if(String(v) !== String(this.startValue)){
44537 this.fireEvent('change', this, v, this.startValue);
44540 this.fireEvent("blur", this);
44543 inputEl : function()
44545 return this.el.select('.roo-money-amount-input', true).first();
44548 currencyEl : function()
44550 return this.el.select('.roo-money-currency-input', true).first();
44553 hiddenEl : function()
44555 return this.el.select('input.hidden-number-input',true).first();
44559 * @class Roo.bootstrap.BezierSignature
44560 * @extends Roo.bootstrap.Component
44561 * Bootstrap BezierSignature class
44562 * This script refer to:
44563 * Title: Signature Pad
44565 * Availability: https://github.com/szimek/signature_pad
44568 * Create a new BezierSignature
44569 * @param {Object} config The config object
44572 Roo.bootstrap.BezierSignature = function(config){
44573 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44579 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44586 mouse_btn_down: true,
44589 * @cfg {int} canvas height
44591 canvas_height: '200px',
44594 * @cfg {float|function} Radius of a single dot.
44599 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44604 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44609 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44614 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44619 * @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.
44621 bg_color: 'rgba(0, 0, 0, 0)',
44624 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44626 dot_color: 'black',
44629 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44631 velocity_filter_weight: 0.7,
44634 * @cfg {function} Callback when stroke begin.
44639 * @cfg {function} Callback when stroke end.
44643 getAutoCreate : function()
44645 var cls = 'roo-signature column';
44648 cls += ' ' + this.cls;
44658 for(var i = 0; i < col_sizes.length; i++) {
44659 if(this[col_sizes[i]]) {
44660 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44670 cls: 'roo-signature-body',
44674 cls: 'roo-signature-body-canvas',
44675 height: this.canvas_height,
44676 width: this.canvas_width
44683 style: 'display: none'
44691 initEvents: function()
44693 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44695 var canvas = this.canvasEl();
44697 // mouse && touch event swapping...
44698 canvas.dom.style.touchAction = 'none';
44699 canvas.dom.style.msTouchAction = 'none';
44701 this.mouse_btn_down = false;
44702 canvas.on('mousedown', this._handleMouseDown, this);
44703 canvas.on('mousemove', this._handleMouseMove, this);
44704 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44706 if (window.PointerEvent) {
44707 canvas.on('pointerdown', this._handleMouseDown, this);
44708 canvas.on('pointermove', this._handleMouseMove, this);
44709 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44712 if ('ontouchstart' in window) {
44713 canvas.on('touchstart', this._handleTouchStart, this);
44714 canvas.on('touchmove', this._handleTouchMove, this);
44715 canvas.on('touchend', this._handleTouchEnd, this);
44718 Roo.EventManager.onWindowResize(this.resize, this, true);
44720 // file input event
44721 this.fileEl().on('change', this.uploadImage, this);
44728 resize: function(){
44730 var canvas = this.canvasEl().dom;
44731 var ctx = this.canvasElCtx();
44732 var img_data = false;
44734 if(canvas.width > 0) {
44735 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44737 // setting canvas width will clean img data
44740 var style = window.getComputedStyle ?
44741 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44743 var padding_left = parseInt(style.paddingLeft) || 0;
44744 var padding_right = parseInt(style.paddingRight) || 0;
44746 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44749 ctx.putImageData(img_data, 0, 0);
44753 _handleMouseDown: function(e)
44755 if (e.browserEvent.which === 1) {
44756 this.mouse_btn_down = true;
44757 this.strokeBegin(e);
44761 _handleMouseMove: function (e)
44763 if (this.mouse_btn_down) {
44764 this.strokeMoveUpdate(e);
44768 _handleMouseUp: function (e)
44770 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44771 this.mouse_btn_down = false;
44776 _handleTouchStart: function (e) {
44778 e.preventDefault();
44779 if (e.browserEvent.targetTouches.length === 1) {
44780 // var touch = e.browserEvent.changedTouches[0];
44781 // this.strokeBegin(touch);
44783 this.strokeBegin(e); // assume e catching the correct xy...
44787 _handleTouchMove: function (e) {
44788 e.preventDefault();
44789 // var touch = event.targetTouches[0];
44790 // _this._strokeMoveUpdate(touch);
44791 this.strokeMoveUpdate(e);
44794 _handleTouchEnd: function (e) {
44795 var wasCanvasTouched = e.target === this.canvasEl().dom;
44796 if (wasCanvasTouched) {
44797 e.preventDefault();
44798 // var touch = event.changedTouches[0];
44799 // _this._strokeEnd(touch);
44804 reset: function () {
44805 this._lastPoints = [];
44806 this._lastVelocity = 0;
44807 this._lastWidth = (this.min_width + this.max_width) / 2;
44808 this.canvasElCtx().fillStyle = this.dot_color;
44811 strokeMoveUpdate: function(e)
44813 this.strokeUpdate(e);
44815 if (this.throttle) {
44816 this.throttleStroke(this.strokeUpdate, this.throttle);
44819 this.strokeUpdate(e);
44823 strokeBegin: function(e)
44825 var newPointGroup = {
44826 color: this.dot_color,
44830 if (typeof this.onBegin === 'function') {
44834 this.curve_data.push(newPointGroup);
44836 this.strokeUpdate(e);
44839 strokeUpdate: function(e)
44841 var rect = this.canvasEl().dom.getBoundingClientRect();
44842 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44843 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44844 var lastPoints = lastPointGroup.points;
44845 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44846 var isLastPointTooClose = lastPoint
44847 ? point.distanceTo(lastPoint) <= this.min_distance
44849 var color = lastPointGroup.color;
44850 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44851 var curve = this.addPoint(point);
44853 this.drawDot({color: color, point: point});
44856 this.drawCurve({color: color, curve: curve});
44866 strokeEnd: function(e)
44868 this.strokeUpdate(e);
44869 if (typeof this.onEnd === 'function') {
44874 addPoint: function (point) {
44875 var _lastPoints = this._lastPoints;
44876 _lastPoints.push(point);
44877 if (_lastPoints.length > 2) {
44878 if (_lastPoints.length === 3) {
44879 _lastPoints.unshift(_lastPoints[0]);
44881 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44882 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44883 _lastPoints.shift();
44889 calculateCurveWidths: function (startPoint, endPoint) {
44890 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44891 (1 - this.velocity_filter_weight) * this._lastVelocity;
44893 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44896 start: this._lastWidth
44899 this._lastVelocity = velocity;
44900 this._lastWidth = newWidth;
44904 drawDot: function (_a) {
44905 var color = _a.color, point = _a.point;
44906 var ctx = this.canvasElCtx();
44907 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44909 this.drawCurveSegment(point.x, point.y, width);
44911 ctx.fillStyle = color;
44915 drawCurve: function (_a) {
44916 var color = _a.color, curve = _a.curve;
44917 var ctx = this.canvasElCtx();
44918 var widthDelta = curve.endWidth - curve.startWidth;
44919 var drawSteps = Math.floor(curve.length()) * 2;
44921 ctx.fillStyle = color;
44922 for (var i = 0; i < drawSteps; i += 1) {
44923 var t = i / drawSteps;
44929 var x = uuu * curve.startPoint.x;
44930 x += 3 * uu * t * curve.control1.x;
44931 x += 3 * u * tt * curve.control2.x;
44932 x += ttt * curve.endPoint.x;
44933 var y = uuu * curve.startPoint.y;
44934 y += 3 * uu * t * curve.control1.y;
44935 y += 3 * u * tt * curve.control2.y;
44936 y += ttt * curve.endPoint.y;
44937 var width = curve.startWidth + ttt * widthDelta;
44938 this.drawCurveSegment(x, y, width);
44944 drawCurveSegment: function (x, y, width) {
44945 var ctx = this.canvasElCtx();
44947 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44948 this.is_empty = false;
44953 var ctx = this.canvasElCtx();
44954 var canvas = this.canvasEl().dom;
44955 ctx.fillStyle = this.bg_color;
44956 ctx.clearRect(0, 0, canvas.width, canvas.height);
44957 ctx.fillRect(0, 0, canvas.width, canvas.height);
44958 this.curve_data = [];
44960 this.is_empty = true;
44965 return this.el.select('input',true).first();
44968 canvasEl: function()
44970 return this.el.select('canvas',true).first();
44973 canvasElCtx: function()
44975 return this.el.select('canvas',true).first().dom.getContext('2d');
44978 getImage: function(type)
44980 if(this.is_empty) {
44985 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44988 drawFromImage: function(img_src)
44990 var img = new Image();
44992 img.onload = function(){
44993 this.canvasElCtx().drawImage(img, 0, 0);
44998 this.is_empty = false;
45001 selectImage: function()
45003 this.fileEl().dom.click();
45006 uploadImage: function(e)
45008 var reader = new FileReader();
45010 reader.onload = function(e){
45011 var img = new Image();
45012 img.onload = function(){
45014 this.canvasElCtx().drawImage(img, 0, 0);
45016 img.src = e.target.result;
45019 reader.readAsDataURL(e.target.files[0]);
45022 // Bezier Point Constructor
45023 Point: (function () {
45024 function Point(x, y, time) {
45027 this.time = time || Date.now();
45029 Point.prototype.distanceTo = function (start) {
45030 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45032 Point.prototype.equals = function (other) {
45033 return this.x === other.x && this.y === other.y && this.time === other.time;
45035 Point.prototype.velocityFrom = function (start) {
45036 return this.time !== start.time
45037 ? this.distanceTo(start) / (this.time - start.time)
45044 // Bezier Constructor
45045 Bezier: (function () {
45046 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45047 this.startPoint = startPoint;
45048 this.control2 = control2;
45049 this.control1 = control1;
45050 this.endPoint = endPoint;
45051 this.startWidth = startWidth;
45052 this.endWidth = endWidth;
45054 Bezier.fromPoints = function (points, widths, scope) {
45055 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45056 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45057 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45059 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45060 var dx1 = s1.x - s2.x;
45061 var dy1 = s1.y - s2.y;
45062 var dx2 = s2.x - s3.x;
45063 var dy2 = s2.y - s3.y;
45064 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45065 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45066 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45067 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45068 var dxm = m1.x - m2.x;
45069 var dym = m1.y - m2.y;
45070 var k = l2 / (l1 + l2);
45071 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45072 var tx = s2.x - cm.x;
45073 var ty = s2.y - cm.y;
45075 c1: new scope.Point(m1.x + tx, m1.y + ty),
45076 c2: new scope.Point(m2.x + tx, m2.y + ty)
45079 Bezier.prototype.length = function () {
45084 for (var i = 0; i <= steps; i += 1) {
45086 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45087 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45089 var xdiff = cx - px;
45090 var ydiff = cy - py;
45091 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45098 Bezier.prototype.point = function (t, start, c1, c2, end) {
45099 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45100 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45101 + (3.0 * c2 * (1.0 - t) * t * t)
45102 + (end * t * t * t);
45107 throttleStroke: function(fn, wait) {
45108 if (wait === void 0) { wait = 250; }
45110 var timeout = null;
45114 var later = function () {
45115 previous = Date.now();
45117 result = fn.apply(storedContext, storedArgs);
45119 storedContext = null;
45123 return function wrapper() {
45125 for (var _i = 0; _i < arguments.length; _i++) {
45126 args[_i] = arguments[_i];
45128 var now = Date.now();
45129 var remaining = wait - (now - previous);
45130 storedContext = this;
45132 if (remaining <= 0 || remaining > wait) {
45134 clearTimeout(timeout);
45138 result = fn.apply(storedContext, storedArgs);
45140 storedContext = null;
45144 else if (!timeout) {
45145 timeout = window.setTimeout(later, remaining);
45155 // old names for form elements
45156 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45157 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45158 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45159 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45160 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45161 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45162 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45163 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45164 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45165 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45166 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45167 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45168 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45169 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45170 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45171 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45172 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45173 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45174 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45175 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45176 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45177 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45178 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45179 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45180 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45181 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45183 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45184 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45186 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45187 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45189 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45190 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45191 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45192 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator