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
3621 * Bootstrap Menu class - container for MenuItems
3623 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624 * @cfg {bool} hidden if the menu should be hidden when rendered.
3625 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3626 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3628 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3632 * @param {Object} config The config objectQ
3636 Roo.bootstrap.menu.Menu = function(config){
3638 if (config.type == 'treeview') {
3639 // normally menu's are drawn attached to the document to handle layering etc..
3640 // however treeview (used by the docs menu is drawn into the parent element)
3641 this.container_method = 'getChildContainer';
3644 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645 if (this.registerMenu && this.type != 'treeview') {
3646 Roo.bootstrap.menu.Manager.register(this);
3653 * Fires before this menu is displayed (return false to block)
3654 * @param {Roo.menu.Menu} this
3659 * Fires before this menu is hidden (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires after this menu is displayed
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is hidden
3672 * @param {Roo.menu.Menu} this
3677 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * @param {Roo.EventObject} e
3685 * Fires when the mouse is hovering over this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when the mouse exits this menu
3694 * @param {Roo.menu.Menu} this
3695 * @param {Roo.EventObject} e
3696 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3701 * Fires when a menu item contained in this menu is clicked
3702 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703 * @param {Roo.EventObject} e
3707 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3714 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3717 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719 registerMenu : true,
3721 menuItems :false, // stores the menu items..
3731 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733 hideTrigger : false,
3738 getChildContainer : function() {
3742 getAutoCreate : function(){
3744 //if (['right'].indexOf(this.align)!==-1) {
3745 // cfg.cn[1].cls += ' pull-right'
3750 cls : 'dropdown-menu shadow' ,
3751 style : 'z-index:1000'
3755 if (this.type === 'submenu') {
3756 cfg.cls = 'submenu active';
3758 if (this.type === 'treeview') {
3759 cfg.cls = 'treeview-menu';
3764 initEvents : function() {
3766 // Roo.log("ADD event");
3767 // Roo.log(this.triggerEl.dom);
3768 if (this.triggerEl) {
3770 this.triggerEl.on('click', this.onTriggerClick, this);
3772 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774 if (!this.hideTrigger) {
3775 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776 // dropdown toggle on the 'a' in BS4?
3777 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779 this.triggerEl.addClass('dropdown-toggle');
3785 this.el.on('touchstart' , this.onTouch, this);
3787 this.el.on('click' , this.onClick, this);
3789 this.el.on("mouseover", this.onMouseOver, this);
3790 this.el.on("mouseout", this.onMouseOut, this);
3794 findTargetItem : function(e)
3796 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3800 //Roo.log(t); Roo.log(t.id);
3802 //Roo.log(this.menuitems);
3803 return this.menuitems.get(t.id);
3805 //return this.items.get(t.menuItemId);
3811 onTouch : function(e)
3813 Roo.log("menu.onTouch");
3814 //e.stopEvent(); this make the user popdown broken
3818 onClick : function(e)
3820 Roo.log("menu.onClick");
3822 var t = this.findTargetItem(e);
3823 if(!t || t.isContainer){
3828 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3829 if(t == this.activeItem && t.shouldDeactivate(e)){
3830 this.activeItem.deactivate();
3831 delete this.activeItem;
3835 this.setActiveItem(t, true);
3843 Roo.log('pass click event');
3847 this.fireEvent("click", this, t, e);
3851 if(!t.href.length || t.href == '#'){
3852 (function() { _this.hide(); }).defer(100);
3857 onMouseOver : function(e){
3858 var t = this.findTargetItem(e);
3861 // if(t.canActivate && !t.disabled){
3862 // this.setActiveItem(t, true);
3866 this.fireEvent("mouseover", this, e, t);
3868 isVisible : function(){
3869 return !this.hidden;
3871 onMouseOut : function(e){
3872 var t = this.findTargetItem(e);
3875 // if(t == this.activeItem && t.shouldDeactivate(e)){
3876 // this.activeItem.deactivate();
3877 // delete this.activeItem;
3880 this.fireEvent("mouseout", this, e, t);
3885 * Displays this menu relative to another element
3886 * @param {String/HTMLElement/Roo.Element} element The element to align to
3887 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888 * the element (defaults to this.defaultAlign)
3889 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891 show : function(el, pos, parentMenu)
3893 if (false === this.fireEvent("beforeshow", this)) {
3894 Roo.log("show canceled");
3897 this.parentMenu = parentMenu;
3901 this.el.addClass('show'); // show otherwise we do not know how big we are..
3903 var xy = this.el.getAlignToXY(el, pos);
3905 // bl-tl << left align below
3906 // tl-bl << left align
3908 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909 // if it goes to far to the right.. -> align left.
3910 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913 // was left align - go right?
3914 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917 // goes down the bottom
3918 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920 var a = this.align.replace('?', '').split('-');
3921 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3925 this.showAt( xy , parentMenu, false);
3928 * Displays this menu at a specific xy position
3929 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932 showAt : function(xy, parentMenu, /* private: */_e){
3933 this.parentMenu = parentMenu;
3938 this.fireEvent("beforeshow", this);
3939 //xy = this.el.adjustForConstraints(xy);
3943 this.hideMenuItems();
3944 this.hidden = false;
3945 if (this.triggerEl) {
3946 this.triggerEl.addClass('open');
3949 this.el.addClass('show');
3953 // reassign x when hitting right
3955 // reassign y when hitting bottom
3957 // but the list may align on trigger left or trigger top... should it be a properity?
3959 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3964 this.fireEvent("show", this);
3970 this.doFocus.defer(50, this);
3974 doFocus : function(){
3976 this.focusEl.focus();
3981 * Hides this menu and optionally all parent menus
3982 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984 hide : function(deep)
3986 if (false === this.fireEvent("beforehide", this)) {
3987 Roo.log("hide canceled");
3990 this.hideMenuItems();
3991 if(this.el && this.isVisible()){
3993 if(this.activeItem){
3994 this.activeItem.deactivate();
3995 this.activeItem = null;
3997 if (this.triggerEl) {
3998 this.triggerEl.removeClass('open');
4001 this.el.removeClass('show');
4003 this.fireEvent("hide", this);
4005 if(deep === true && this.parentMenu){
4006 this.parentMenu.hide(true);
4010 onTriggerClick : function(e)
4012 Roo.log('trigger click');
4014 var target = e.getTarget();
4016 Roo.log(target.nodeName.toLowerCase());
4018 if(target.nodeName.toLowerCase() === 'i'){
4024 onTriggerPress : function(e)
4026 Roo.log('trigger press');
4027 //Roo.log(e.getTarget());
4028 // Roo.log(this.triggerEl.dom);
4030 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031 var pel = Roo.get(e.getTarget());
4032 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033 Roo.log('is treeview or dropdown?');
4037 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4041 if (this.isVisible()) {
4047 this.show(this.triggerEl, this.align, false);
4050 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4057 hideMenuItems : function()
4059 Roo.log("hide Menu Items");
4064 this.el.select('.open',true).each(function(aa) {
4066 aa.removeClass('open');
4070 addxtypeChild : function (tree, cntr) {
4071 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073 this.menuitems.add(comp);
4085 this.getEl().dom.innerHTML = '';
4086 this.menuitems.clear();
4092 * @class Roo.bootstrap.menu.Item
4093 * @extends Roo.bootstrap.Component
4094 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096 * Bootstrap MenuItem class
4098 * @cfg {String} html the menu label
4099 * @cfg {String} href the link
4100 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4101 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4102 * @cfg {Boolean} active used on sidebars to highlight active itesm
4103 * @cfg {String} fa favicon to show on left of menu item.
4104 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4108 * Create a new MenuItem
4109 * @param {Object} config The config object
4113 Roo.bootstrap.menu.Item = function(config){
4114 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4119 * The raw click event for the entire grid.
4120 * @param {Roo.bootstrap.menu.Item} this
4121 * @param {Roo.EventObject} e
4127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4131 preventDefault: false,
4132 isContainer : false,
4136 getAutoCreate : function(){
4138 if(this.isContainer){
4141 cls: 'dropdown-menu-item '
4151 cls : 'dropdown-item',
4156 if (this.fa !== false) {
4159 cls : 'fa fa-' + this.fa
4168 cls: 'dropdown-menu-item',
4171 if (this.parent().type == 'treeview') {
4172 cfg.cls = 'treeview-menu';
4175 cfg.cls += ' active';
4180 anc.href = this.href || cfg.cn[0].href ;
4181 ctag.html = this.html || cfg.cn[0].html ;
4185 initEvents: function()
4187 if (this.parent().type == 'treeview') {
4188 this.el.select('a').on('click', this.onClick, this);
4192 this.menu.parentType = this.xtype;
4193 this.menu.triggerEl = this.el;
4194 this.menu = this.addxtype(Roo.apply({}, this.menu));
4198 onClick : function(e)
4200 Roo.log('item on click ');
4202 if(this.preventDefault){
4205 //this.parent().hideMenuItems();
4207 this.fireEvent('click', this, e);
4221 * @class Roo.bootstrap.menu.Separator
4222 * @extends Roo.bootstrap.Component
4224 * Bootstrap Separator class
4227 * Create a new Separator
4228 * @param {Object} config The config object
4232 Roo.bootstrap.menu.Separator = function(config){
4233 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4238 getAutoCreate : function(){
4241 cls: 'dropdown-divider divider'
4257 * @class Roo.bootstrap.Modal
4258 * @extends Roo.bootstrap.Component
4261 * @children Roo.bootstrap.Component
4262 * Bootstrap Modal class
4263 * @cfg {String} title Title of dialog
4264 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4266 * @cfg {Boolean} specificTitle default false
4267 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4268 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269 * @cfg {Boolean} animate default true
4270 * @cfg {Boolean} allow_close default true
4271 * @cfg {Boolean} fitwindow default false
4272 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275 * @cfg {String} size (sm|lg|xl) default empty
4276 * @cfg {Number} max_width set the max width of modal
4277 * @cfg {Boolean} editableTitle can the title be edited
4282 * Create a new Modal Dialog
4283 * @param {Object} config The config object
4286 Roo.bootstrap.Modal = function(config){
4287 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4292 * The raw btnclick event for the button
4293 * @param {Roo.EventObject} e
4298 * Fire when dialog resize
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} e
4304 * @event titlechanged
4305 * Fire when the editable title has been changed
4306 * @param {Roo.bootstrap.Modal} this
4307 * @param {Roo.EventObject} value
4309 "titlechanged" : true
4312 this.buttons = this.buttons || [];
4315 this.tmpl = Roo.factory(this.tmpl);
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4322 title : 'test dialog',
4332 specificTitle: false,
4334 buttonPosition: 'right',
4356 editableTitle : false,
4358 onRender : function(ct, position)
4360 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4363 var cfg = Roo.apply({}, this.getAutoCreate());
4366 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368 //if (!cfg.name.length) {
4372 cfg.cls += ' ' + this.cls;
4375 cfg.style = this.style;
4377 this.el = Roo.get(document.body).createChild(cfg, position);
4379 //var type = this.el.dom.type;
4382 if(this.tabIndex !== undefined){
4383 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4386 this.dialogEl = this.el.select('.modal-dialog',true).first();
4387 this.bodyEl = this.el.select('.modal-body',true).first();
4388 this.closeEl = this.el.select('.modal-header .close', true).first();
4389 this.headerEl = this.el.select('.modal-header',true).first();
4390 this.titleEl = this.el.select('.modal-title',true).first();
4391 this.footerEl = this.el.select('.modal-footer',true).first();
4393 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395 //this.el.addClass("x-dlg-modal");
4397 if (this.buttons.length) {
4398 Roo.each(this.buttons, function(bb) {
4399 var b = Roo.apply({}, bb);
4400 b.xns = b.xns || Roo.bootstrap;
4401 b.xtype = b.xtype || 'Button';
4402 if (typeof(b.listeners) == 'undefined') {
4403 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4406 var btn = Roo.factory(b);
4408 btn.render(this.getButtonContainer());
4412 // render the children.
4415 if(typeof(this.items) != 'undefined'){
4416 var items = this.items;
4419 for(var i =0;i < items.length;i++) {
4420 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424 this.items = nitems;
4426 // where are these used - they used to be body/close/footer
4430 //this.el.addClass([this.fieldClass, this.cls]);
4434 getAutoCreate : function()
4436 // we will default to modal-body-overflow - might need to remove or make optional later.
4438 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4439 html : this.html || ''
4444 cls : 'modal-title',
4448 if(this.specificTitle){ // WTF is this?
4453 if (this.allow_close && Roo.bootstrap.version == 3) {
4463 if (this.editableTitle) {
4465 cls: 'form-control roo-editable-title d-none',
4471 if (this.allow_close && Roo.bootstrap.version == 4) {
4481 if(this.size.length){
4482 size = 'modal-' + this.size;
4485 var footer = Roo.bootstrap.version == 3 ?
4487 cls : 'modal-footer',
4491 cls: 'btn-' + this.buttonPosition
4496 { // BS4 uses mr-auto on left buttons....
4497 cls : 'modal-footer'
4508 cls: "modal-dialog " + size,
4511 cls : "modal-content",
4514 cls : 'modal-header',
4529 modal.cls += ' fade';
4535 getChildContainer : function() {
4540 getButtonContainer : function() {
4542 return Roo.bootstrap.version == 4 ?
4543 this.el.select('.modal-footer',true).first()
4544 : this.el.select('.modal-footer div',true).first();
4547 initEvents : function()
4549 if (this.allow_close) {
4550 this.closeEl.on('click', this.hide, this);
4552 Roo.EventManager.onWindowResize(this.resize, this, true);
4553 if (this.editableTitle) {
4554 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4555 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4556 this.headerEditEl.on('keyup', function(e) {
4557 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4558 this.toggleHeaderInput(false)
4561 this.headerEditEl.on('blur', function(e) {
4562 this.toggleHeaderInput(false)
4571 this.maskEl.setSize(
4572 Roo.lib.Dom.getViewWidth(true),
4573 Roo.lib.Dom.getViewHeight(true)
4576 if (this.fitwindow) {
4578 this.dialogEl.setStyle( { 'max-width' : '100%' });
4580 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4581 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4586 if(this.max_width !== 0) {
4588 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4591 this.setSize(w, this.height);
4595 if(this.max_height) {
4596 this.setSize(w,Math.min(
4598 Roo.lib.Dom.getViewportHeight(true) - 60
4604 if(!this.fit_content) {
4605 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609 this.setSize(w, Math.min(
4611 this.headerEl.getHeight() +
4612 this.footerEl.getHeight() +
4613 this.getChildHeight(this.bodyEl.dom.childNodes),
4614 Roo.lib.Dom.getViewportHeight(true) - 60)
4620 setSize : function(w,h)
4631 if (!this.rendered) {
4634 this.toggleHeaderInput(false);
4635 //this.el.setStyle('display', 'block');
4636 this.el.removeClass('hideing');
4637 this.el.dom.style.display='block';
4639 Roo.get(document.body).addClass('modal-open');
4641 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4644 this.el.addClass('show');
4645 this.el.addClass('in');
4648 this.el.addClass('show');
4649 this.el.addClass('in');
4652 // not sure how we can show data in here..
4654 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4657 Roo.get(document.body).addClass("x-body-masked");
4659 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4660 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4661 this.maskEl.dom.style.display = 'block';
4662 this.maskEl.addClass('show');
4667 this.fireEvent('show', this);
4669 // set zindex here - otherwise it appears to be ignored...
4670 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4673 this.items.forEach( function(e) {
4674 e.layout ? e.layout() : false;
4682 if(this.fireEvent("beforehide", this) !== false){
4684 this.maskEl.removeClass('show');
4686 this.maskEl.dom.style.display = '';
4687 Roo.get(document.body).removeClass("x-body-masked");
4688 this.el.removeClass('in');
4689 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691 if(this.animate){ // why
4692 this.el.addClass('hideing');
4693 this.el.removeClass('show');
4695 if (!this.el.hasClass('hideing')) {
4696 return; // it's been shown again...
4699 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4702 this.el.removeClass('hideing');
4706 this.el.removeClass('show');
4707 this.el.dom.style.display='';
4708 Roo.get(document.body).removeClass('modal-open');
4711 this.fireEvent('hide', this);
4714 isVisible : function()
4717 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721 addButton : function(str, cb)
4725 var b = Roo.apply({}, { html : str } );
4726 b.xns = b.xns || Roo.bootstrap;
4727 b.xtype = b.xtype || 'Button';
4728 if (typeof(b.listeners) == 'undefined') {
4729 b.listeners = { click : cb.createDelegate(this) };
4732 var btn = Roo.factory(b);
4734 btn.render(this.getButtonContainer());
4740 setDefaultButton : function(btn)
4742 //this.el.select('.modal-footer').()
4745 resizeTo: function(w,h)
4747 this.dialogEl.setWidth(w);
4749 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4751 this.bodyEl.setHeight(h - diff);
4753 this.fireEvent('resize', this);
4756 setContentSize : function(w, h)
4760 onButtonClick: function(btn,e)
4763 this.fireEvent('btnclick', btn.name, e);
4766 * Set the title of the Dialog
4767 * @param {String} str new Title
4769 setTitle: function(str) {
4770 this.titleEl.dom.innerHTML = str;
4774 * Set the body of the Dialog
4775 * @param {String} str new Title
4777 setBody: function(str) {
4778 this.bodyEl.dom.innerHTML = str;
4781 * Set the body of the Dialog using the template
4782 * @param {Obj} data - apply this data to the template and replace the body contents.
4784 applyBody: function(obj)
4787 Roo.log("Error - using apply Body without a template");
4790 this.tmpl.overwrite(this.bodyEl, obj);
4793 getChildHeight : function(child_nodes)
4797 child_nodes.length == 0
4802 var child_height = 0;
4804 for(var i = 0; i < child_nodes.length; i++) {
4807 * for modal with tabs...
4808 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810 var layout_childs = child_nodes[i].childNodes;
4812 for(var j = 0; j < layout_childs.length; j++) {
4814 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816 var layout_body_childs = layout_childs[j].childNodes;
4818 for(var k = 0; k < layout_body_childs.length; k++) {
4820 if(layout_body_childs[k].classList.contains('navbar')) {
4821 child_height += layout_body_childs[k].offsetHeight;
4825 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4832 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4847 child_height += child_nodes[i].offsetHeight;
4848 // Roo.log(child_nodes[i].offsetHeight);
4851 return child_height;
4853 toggleHeaderInput : function(is_edit)
4855 if (!this.editableTitle) {
4856 return; // not editable.
4858 if (is_edit && this.is_header_editing) {
4859 return; // already editing..
4863 this.headerEditEl.dom.value = this.title;
4864 this.headerEditEl.removeClass('d-none');
4865 this.headerEditEl.dom.focus();
4866 this.titleEl.addClass('d-none');
4868 this.is_header_editing = true;
4871 // flip back to not editing.
4872 this.title = this.headerEditEl.dom.value;
4873 this.headerEditEl.addClass('d-none');
4874 this.titleEl.removeClass('d-none');
4875 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4876 this.is_header_editing = false;
4877 this.fireEvent('titlechanged', this, this.title);
4886 Roo.apply(Roo.bootstrap.Modal, {
4888 * Button config that displays a single OK button
4897 * Button config that displays Yes and No buttons
4913 * Button config that displays OK and Cancel buttons
4928 * Button config that displays Yes, No and Cancel buttons
4953 * messagebox - can be used as a replace
4957 * @class Roo.MessageBox
4958 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4962 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964 // Prompt for user data:
4965 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967 // process text value...
4971 // Show a dialog using config options:
4973 title:'Save Changes?',
4974 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4975 buttons: Roo.Msg.YESNOCANCEL,
4982 Roo.bootstrap.MessageBox = function(){
4983 var dlg, opt, mask, waitTimer;
4984 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4985 var buttons, activeTextEl, bwidth;
4989 var handleButton = function(button){
4991 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995 var handleHide = function(){
4997 dlg.el.removeClass(opt.cls);
5000 // Roo.TaskMgr.stop(waitTimer);
5001 // waitTimer = null;
5006 var updateButtons = function(b){
5009 buttons["ok"].hide();
5010 buttons["cancel"].hide();
5011 buttons["yes"].hide();
5012 buttons["no"].hide();
5013 dlg.footerEl.hide();
5017 dlg.footerEl.show();
5018 for(var k in buttons){
5019 if(typeof buttons[k] != "function"){
5022 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5023 width += buttons[k].el.getWidth()+15;
5033 var handleEsc = function(d, k, e){
5034 if(opt && opt.closable !== false){
5044 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5045 * @return {Roo.BasicDialog} The BasicDialog element
5047 getDialog : function(){
5049 dlg = new Roo.bootstrap.Modal( {
5052 //constraintoviewport:false,
5054 //collapsible : false,
5059 //buttonAlign:"center",
5060 closeClick : function(){
5061 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5064 handleButton("cancel");
5069 dlg.on("hide", handleHide);
5071 //dlg.addKeyListener(27, handleEsc);
5073 this.buttons = buttons;
5074 var bt = this.buttonText;
5075 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5076 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5077 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5078 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080 bodyEl = dlg.bodyEl.createChild({
5082 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5083 '<textarea class="roo-mb-textarea"></textarea>' +
5084 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5086 msgEl = bodyEl.dom.firstChild;
5087 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5088 textboxEl.enableDisplayMode();
5089 textboxEl.addKeyListener([10,13], function(){
5090 if(dlg.isVisible() && opt && opt.buttons){
5093 }else if(opt.buttons.yes){
5094 handleButton("yes");
5098 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5099 textareaEl.enableDisplayMode();
5100 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5101 progressEl.enableDisplayMode();
5103 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5104 var pf = progressEl.dom.firstChild;
5106 pp = Roo.get(pf.firstChild);
5107 pp.setHeight(pf.offsetHeight);
5115 * Updates the message box body text
5116 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5117 * the XHTML-compliant non-breaking space character '&#160;')
5118 * @return {Roo.MessageBox} This message box
5120 updateText : function(text)
5122 if(!dlg.isVisible() && !opt.width){
5123 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5124 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126 msgEl.innerHTML = text || ' ';
5128 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5129 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131 Math.min(opt.width || cw , this.maxWidth),
5132 Math.max(opt.minWidth || this.minWidth, bwidth)
5135 activeTextEl.setWidth(w);
5137 if(dlg.isVisible()){
5138 dlg.fixedcenter = false;
5140 // to big, make it scroll. = But as usual stupid IE does not support
5143 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5144 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5145 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147 bodyEl.dom.style.height = '';
5148 bodyEl.dom.style.overflowY = '';
5151 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153 bodyEl.dom.style.overflowX = '';
5156 dlg.setContentSize(w, bodyEl.getHeight());
5157 if(dlg.isVisible()){
5158 dlg.fixedcenter = true;
5164 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5165 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5166 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5167 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5168 * @return {Roo.MessageBox} This message box
5170 updateProgress : function(value, text){
5172 this.updateText(text);
5175 if (pp) { // weird bug on my firefox - for some reason this is not defined
5176 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5177 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5183 * Returns true if the message box is currently displayed
5184 * @return {Boolean} True if the message box is visible, else false
5186 isVisible : function(){
5187 return dlg && dlg.isVisible();
5191 * Hides the message box if it is displayed
5194 if(this.isVisible()){
5200 * Displays a new message box, or reinitializes an existing message box, based on the config options
5201 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5202 * The following config object properties are supported:
5204 Property Type Description
5205 ---------- --------------- ------------------------------------------------------------------------------------
5206 animEl String/Element An id or Element from which the message box should animate as it opens and
5207 closes (defaults to undefined)
5208 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5209 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5210 closable Boolean False to hide the top-right close button (defaults to true). Note that
5211 progress and wait dialogs will ignore this property and always hide the
5212 close button as they can only be closed programmatically.
5213 cls String A custom CSS class to apply to the message box element
5214 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5215 displayed (defaults to 75)
5216 fn Function A callback function to execute after closing the dialog. The arguments to the
5217 function will be btn (the name of the button that was clicked, if applicable,
5218 e.g. "ok"), and text (the value of the active text field, if applicable).
5219 Progress and wait dialogs will ignore this option since they do not respond to
5220 user actions and can only be closed programmatically, so any required function
5221 should be called by the same code after it closes the dialog.
5222 icon String A CSS class that provides a background image to be used as an icon for
5223 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5224 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5225 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5226 modal Boolean False to allow user interaction with the page while the message box is
5227 displayed (defaults to true)
5228 msg String A string that will replace the existing message box body text (defaults
5229 to the XHTML-compliant non-breaking space character ' ')
5230 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5231 progress Boolean True to display a progress bar (defaults to false)
5232 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5233 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5234 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5235 title String The title text
5236 value String The string value to set into the active textbox element if displayed
5237 wait Boolean True to display a progress bar (defaults to false)
5238 width Number The width of the dialog in pixels
5245 msg: 'Please enter your address:',
5247 buttons: Roo.MessageBox.OKCANCEL,
5250 animEl: 'addAddressBtn'
5253 * @param {Object} config Configuration options
5254 * @return {Roo.MessageBox} This message box
5256 show : function(options)
5259 // this causes nightmares if you show one dialog after another
5260 // especially on callbacks..
5262 if(this.isVisible()){
5265 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5266 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5267 Roo.log("New Dialog Message:" + options.msg )
5268 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5269 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5272 var d = this.getDialog();
5274 d.setTitle(opt.title || " ");
5275 d.closeEl.setDisplayed(opt.closable !== false);
5276 activeTextEl = textboxEl;
5277 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5282 textareaEl.setHeight(typeof opt.multiline == "number" ?
5283 opt.multiline : this.defaultTextHeight);
5284 activeTextEl = textareaEl;
5293 progressEl.setDisplayed(opt.progress === true);
5295 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297 this.updateProgress(0);
5298 activeTextEl.dom.value = opt.value || "";
5300 dlg.setDefaultButton(activeTextEl);
5302 var bs = opt.buttons;
5306 }else if(bs && bs.yes){
5307 db = buttons["yes"];
5309 dlg.setDefaultButton(db);
5311 bwidth = updateButtons(opt.buttons);
5312 this.updateText(opt.msg);
5314 d.el.addClass(opt.cls);
5316 d.proxyDrag = opt.proxyDrag === true;
5317 d.modal = opt.modal !== false;
5318 d.mask = opt.modal !== false ? mask : false;
5320 // force it to the end of the z-index stack so it gets a cursor in FF
5321 document.body.appendChild(dlg.el.dom);
5322 d.animateTarget = null;
5323 d.show(options.animEl);
5329 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5330 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5331 * and closing the message box when the process is complete.
5332 * @param {String} title The title bar text
5333 * @param {String} msg The message box body text
5334 * @return {Roo.MessageBox} This message box
5336 progress : function(title, msg){
5343 minWidth: this.minProgressWidth,
5350 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5351 * If a callback function is passed it will be called after the user clicks the button, and the
5352 * id of the button that was clicked will be passed as the only parameter to the callback
5353 * (could also be the top-right close button).
5354 * @param {String} title The title bar text
5355 * @param {String} msg The message box body text
5356 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5357 * @param {Object} scope (optional) The scope of the callback function
5358 * @return {Roo.MessageBox} This message box
5360 alert : function(title, msg, fn, scope)
5375 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5376 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5377 * You are responsible for closing the message box when the process is complete.
5378 * @param {String} msg The message box body text
5379 * @param {String} title (optional) The title bar text
5380 * @return {Roo.MessageBox} This message box
5382 wait : function(msg, title){
5393 waitTimer = Roo.TaskMgr.start({
5395 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5403 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5404 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5405 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5406 * @param {String} title The title bar text
5407 * @param {String} msg The message box body text
5408 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5409 * @param {Object} scope (optional) The scope of the callback function
5410 * @return {Roo.MessageBox} This message box
5412 confirm : function(title, msg, fn, scope){
5416 buttons: this.YESNO,
5425 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5426 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5427 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5428 * (could also be the top-right close button) and the text that was entered will be passed as the two
5429 * parameters to the callback.
5430 * @param {String} title The title bar text
5431 * @param {String} msg The message box body text
5432 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5433 * @param {Object} scope (optional) The scope of the callback function
5434 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5435 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5436 * @return {Roo.MessageBox} This message box
5438 prompt : function(title, msg, fn, scope, multiline){
5442 buttons: this.OKCANCEL,
5447 multiline: multiline,
5454 * Button config that displays a single OK button
5459 * Button config that displays Yes and No buttons
5462 YESNO : {yes:true, no:true},
5464 * Button config that displays OK and Cancel buttons
5467 OKCANCEL : {ok:true, cancel:true},
5469 * Button config that displays Yes, No and Cancel buttons
5472 YESNOCANCEL : {yes:true, no:true, cancel:true},
5475 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5478 defaultTextHeight : 75,
5480 * The maximum width in pixels of the message box (defaults to 600)
5485 * The minimum width in pixels of the message box (defaults to 100)
5490 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5491 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5494 minProgressWidth : 250,
5496 * An object containing the default button text strings that can be overriden for localized language support.
5497 * Supported properties are: ok, cancel, yes and no.
5498 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5511 * Shorthand for {@link Roo.MessageBox}
5513 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5514 Roo.Msg = Roo.Msg || Roo.MessageBox;
5523 * @class Roo.bootstrap.nav.Bar
5524 * @extends Roo.bootstrap.Component
5526 * Bootstrap Navbar class
5529 * Create a new Navbar
5530 * @param {Object} config The config object
5534 Roo.bootstrap.nav.Bar = function(config){
5535 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5539 * @event beforetoggle
5540 * Fire before toggle the menu
5541 * @param {Roo.EventObject} e
5543 "beforetoggle" : true
5547 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5556 getAutoCreate : function(){
5559 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5563 initEvents :function ()
5565 //Roo.log(this.el.select('.navbar-toggle',true));
5566 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5573 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575 var size = this.el.getSize();
5576 this.maskEl.setSize(size.width, size.height);
5577 this.maskEl.enableDisplayMode("block");
5586 getChildContainer : function()
5588 if (this.el && this.el.select('.collapse').getCount()) {
5589 return this.el.select('.collapse',true).first();
5604 onToggle : function()
5607 if(this.fireEvent('beforetoggle', this) === false){
5610 var ce = this.el.select('.navbar-collapse',true).first();
5612 if (!ce.hasClass('show')) {
5622 * Expand the navbar pulldown
5624 expand : function ()
5627 var ce = this.el.select('.navbar-collapse',true).first();
5628 if (ce.hasClass('collapsing')) {
5631 ce.dom.style.height = '';
5633 ce.addClass('in'); // old...
5634 ce.removeClass('collapse');
5635 ce.addClass('show');
5636 var h = ce.getHeight();
5638 ce.removeClass('show');
5639 // at this point we should be able to see it..
5640 ce.addClass('collapsing');
5642 ce.setHeight(0); // resize it ...
5643 ce.on('transitionend', function() {
5644 //Roo.log('done transition');
5645 ce.removeClass('collapsing');
5646 ce.addClass('show');
5647 ce.removeClass('collapse');
5649 ce.dom.style.height = '';
5650 }, this, { single: true} );
5652 ce.dom.scrollTop = 0;
5655 * Collapse the navbar pulldown
5657 collapse : function()
5659 var ce = this.el.select('.navbar-collapse',true).first();
5661 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5662 // it's collapsed or collapsing..
5665 ce.removeClass('in'); // old...
5666 ce.setHeight(ce.getHeight());
5667 ce.removeClass('show');
5668 ce.addClass('collapsing');
5670 ce.on('transitionend', function() {
5671 ce.dom.style.height = '';
5672 ce.removeClass('collapsing');
5673 ce.addClass('collapse');
5674 }, this, { single: true} );
5694 * @class Roo.bootstrap.nav.Simplebar
5695 * @extends Roo.bootstrap.nav.Bar
5696 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5697 * Bootstrap Sidebar class
5699 * @cfg {Boolean} inverse is inverted color
5701 * @cfg {String} type (nav | pills | tabs)
5702 * @cfg {Boolean} arrangement stacked | justified
5703 * @cfg {String} align (left | right) alignment
5705 * @cfg {Boolean} main (true|false) main nav bar? default false
5706 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708 * @cfg {String} tag (header|footer|nav|div) default is nav
5710 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5714 * Create a new Sidebar
5715 * @param {Object} config The config object
5719 Roo.bootstrap.nav.Simplebar = function(config){
5720 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5723 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5739 getAutoCreate : function(){
5743 tag : this.tag || 'div',
5744 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746 if (['light','white'].indexOf(this.weight) > -1) {
5747 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749 cfg.cls += ' bg-' + this.weight;
5752 cfg.cls += ' navbar-inverse';
5756 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5767 cls: 'nav nav-' + this.xtype,
5773 this.type = this.type || 'nav';
5774 if (['tabs','pills'].indexOf(this.type) != -1) {
5775 cfg.cn[0].cls += ' nav-' + this.type
5779 if (this.type!=='nav') {
5780 Roo.log('nav type must be nav/tabs/pills')
5782 cfg.cn[0].cls += ' navbar-nav'
5788 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5789 cfg.cn[0].cls += ' nav-' + this.arrangement;
5793 if (this.align === 'right') {
5794 cfg.cn[0].cls += ' navbar-right';
5819 * navbar-expand-md fixed-top
5823 * @class Roo.bootstrap.nav.Headerbar
5824 * @extends Roo.bootstrap.nav.Simplebar
5825 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5826 * Bootstrap Sidebar class
5828 * @cfg {String} brand what is brand
5829 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5830 * @cfg {String} brand_href href of the brand
5831 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5832 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5833 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5834 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5837 * Create a new Sidebar
5838 * @param {Object} config The config object
5842 Roo.bootstrap.nav.Headerbar = function(config){
5843 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5847 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5854 desktopCenter : false,
5857 getAutoCreate : function(){
5860 tag: this.nav || 'nav',
5861 cls: 'navbar navbar-expand-md',
5867 if (this.desktopCenter) {
5868 cn.push({cls : 'container', cn : []});
5876 cls: 'navbar-toggle navbar-toggler',
5877 'data-toggle': 'collapse',
5882 html: 'Toggle navigation'
5886 cls: 'icon-bar navbar-toggler-icon'
5899 cn.push( Roo.bootstrap.version == 4 ? btn : {
5901 cls: 'navbar-header',
5910 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5914 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916 if (['light','white'].indexOf(this.weight) > -1) {
5917 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919 cfg.cls += ' bg-' + this.weight;
5922 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5923 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925 // tag can override this..
5927 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5930 if (this.brand !== '') {
5931 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5932 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934 href: this.brand_href ? this.brand_href : '#',
5935 cls: 'navbar-brand',
5943 cfg.cls += ' main-nav';
5951 getHeaderChildContainer : function()
5953 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5954 return this.el.select('.navbar-header',true).first();
5957 return this.getChildContainer();
5960 getChildContainer : function()
5963 return this.el.select('.roo-navbar-collapse',true).first();
5968 initEvents : function()
5970 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972 if (this.autohide) {
5977 Roo.get(document).on('scroll',function(e) {
5978 var ns = Roo.get(document).getScroll().top;
5979 var os = prevScroll;
5983 ft.removeClass('slideDown');
5984 ft.addClass('slideUp');
5987 ft.removeClass('slideUp');
5988 ft.addClass('slideDown');
6009 * @class Roo.bootstrap.nav.Sidebar
6010 * @extends Roo.bootstrap.nav.Bar
6011 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6012 * Bootstrap Sidebar class
6015 * Create a new Sidebar
6016 * @param {Object} config The config object
6020 Roo.bootstrap.nav.Sidebar = function(config){
6021 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6024 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6026 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028 getAutoCreate : function(){
6033 cls: 'sidebar sidebar-nav'
6055 * @class Roo.bootstrap.nav.Group
6056 * @extends Roo.bootstrap.Component
6057 * @children Roo.bootstrap.nav.Item
6058 * Bootstrap NavGroup class
6059 * @cfg {String} align (left|right)
6060 * @cfg {Boolean} inverse
6061 * @cfg {String} type (nav|pills|tab) default nav
6062 * @cfg {String} navId - reference Id for navbar.
6063 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6066 * Create a new nav group
6067 * @param {Object} config The config object
6070 Roo.bootstrap.nav.Group = function(config){
6071 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6074 Roo.bootstrap.nav.Group.register(this);
6078 * Fires when the active item changes
6079 * @param {Roo.bootstrap.nav.Group} this
6080 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6081 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6088 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6100 getAutoCreate : function()
6102 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6108 if (Roo.bootstrap.version == 4) {
6109 if (['tabs','pills'].indexOf(this.type) != -1) {
6110 cfg.cls += ' nav-' + this.type;
6112 // trying to remove so header bar can right align top?
6113 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6114 // do not use on header bar...
6115 cfg.cls += ' navbar-nav';
6120 if (['tabs','pills'].indexOf(this.type) != -1) {
6121 cfg.cls += ' nav-' + this.type
6123 if (this.type !== 'nav') {
6124 Roo.log('nav type must be nav/tabs/pills')
6126 cfg.cls += ' navbar-nav'
6130 if (this.parent() && this.parent().sidebar) {
6133 cls: 'dashboard-menu sidebar-menu'
6139 if (this.form === true) {
6142 cls: 'navbar-form form-inline'
6144 //nav navbar-right ml-md-auto
6145 if (this.align === 'right') {
6146 cfg.cls += ' navbar-right ml-md-auto';
6148 cfg.cls += ' navbar-left';
6152 if (this.align === 'right') {
6153 cfg.cls += ' navbar-right ml-md-auto';
6155 cfg.cls += ' mr-auto';
6159 cfg.cls += ' navbar-inverse';
6167 * sets the active Navigation item
6168 * @param {Roo.bootstrap.nav.Item} the new current navitem
6170 setActiveItem : function(item)
6173 Roo.each(this.navItems, function(v){
6178 v.setActive(false, true);
6185 item.setActive(true, true);
6186 this.fireEvent('changed', this, item, prev);
6191 * gets the active Navigation item
6192 * @return {Roo.bootstrap.nav.Item} the current navitem
6194 getActive : function()
6198 Roo.each(this.navItems, function(v){
6209 indexOfNav : function()
6213 Roo.each(this.navItems, function(v,i){
6224 * adds a Navigation item
6225 * @param {Roo.bootstrap.nav.Item} the navitem to add
6227 addItem : function(cfg)
6229 if (this.form && Roo.bootstrap.version == 4) {
6232 var cn = new Roo.bootstrap.nav.Item(cfg);
6234 cn.parentId = this.id;
6235 cn.onRender(this.el, null);
6239 * register a Navigation item
6240 * @param {Roo.bootstrap.nav.Item} the navitem to add
6242 register : function(item)
6244 this.navItems.push( item);
6245 item.navId = this.navId;
6250 * clear all the Navigation item
6253 clearAll : function()
6256 this.el.dom.innerHTML = '';
6259 getNavItem: function(tabId)
6262 Roo.each(this.navItems, function(e) {
6263 if (e.tabId == tabId) {
6273 setActiveNext : function()
6275 var i = this.indexOfNav(this.getActive());
6276 if (i > this.navItems.length) {
6279 this.setActiveItem(this.navItems[i+1]);
6281 setActivePrev : function()
6283 var i = this.indexOfNav(this.getActive());
6287 this.setActiveItem(this.navItems[i-1]);
6289 clearWasActive : function(except) {
6290 Roo.each(this.navItems, function(e) {
6291 if (e.tabId != except.tabId && e.was_active) {
6292 e.was_active = false;
6299 getWasActive : function ()
6302 Roo.each(this.navItems, function(e) {
6317 Roo.apply(Roo.bootstrap.nav.Group, {
6321 * register a Navigation Group
6322 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324 register : function(navgrp)
6326 this.groups[navgrp.navId] = navgrp;
6330 * fetch a Navigation Group based on the navigation ID
6331 * @param {string} the navgroup to add
6332 * @returns {Roo.bootstrap.nav.Group} the navgroup
6334 get: function(navId) {
6335 if (typeof(this.groups[navId]) == 'undefined') {
6337 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339 return this.groups[navId] ;
6347 * @class Roo.bootstrap.nav.Item
6348 * @extends Roo.bootstrap.Component
6349 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351 * Bootstrap Navbar.NavItem class
6353 * @cfg {String} href link to
6354 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6355 * @cfg {Boolean} button_outline show and outlined button
6356 * @cfg {String} html content of button
6357 * @cfg {String} badge text inside badge
6358 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6359 * @cfg {String} glyphicon DEPRICATED - use fa
6360 * @cfg {String} icon DEPRICATED - use fa
6361 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6362 * @cfg {Boolean} active Is item active
6363 * @cfg {Boolean} disabled Is item disabled
6364 * @cfg {String} linkcls Link Class
6365 * @cfg {Boolean} preventDefault (true | false) default false
6366 * @cfg {String} tabId the tab that this item activates.
6367 * @cfg {String} tagtype (a|span) render as a href or span?
6368 * @cfg {Boolean} animateRef (true|false) link to element default false
6369 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6372 * Create a new Navbar Item
6373 * @param {Object} config The config object
6375 Roo.bootstrap.nav.Item = function(config){
6376 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6381 * The raw click event for the entire grid.
6382 * @param {Roo.EventObject} e
6387 * Fires when the active item active state changes
6388 * @param {Roo.bootstrap.nav.Item} this
6389 * @param {boolean} state the new state
6395 * Fires when scroll to element
6396 * @param {Roo.bootstrap.nav.Item} this
6397 * @param {Object} options
6398 * @param {Roo.EventObject} e
6406 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6415 preventDefault : false,
6423 button_outline : false,
6427 getAutoCreate : function(){
6434 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6437 cfg.cls += ' active' ;
6439 if (this.disabled) {
6440 cfg.cls += ' disabled';
6444 if (this.button_weight.length) {
6445 cfg.tag = this.href ? 'a' : 'button';
6446 cfg.html = this.html || '';
6447 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6449 cfg.href = this.href;
6452 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6454 cfg.cls += " nav-html";
6457 // menu .. should add dropdown-menu class - so no need for carat..
6459 if (this.badge !== '') {
6461 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6466 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6470 href : this.href || "#",
6471 html: this.html || '',
6475 if (this.tagtype == 'a') {
6476 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6480 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6481 } else if (this.fa) {
6482 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483 } else if(this.glyphicon) {
6484 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6486 cfg.cn[0].cls += " nav-html";
6490 cfg.cn[0].html += " <span class='caret'></span>";
6494 if (this.badge !== '') {
6495 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6503 onRender : function(ct, position)
6505 // Roo.log("Call onRender: " + this.xtype);
6506 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6510 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6511 this.navLink = this.el.select('.nav-link',true).first();
6512 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6517 initEvents: function()
6519 if (typeof (this.menu) != 'undefined') {
6520 this.menu.parentType = this.xtype;
6521 this.menu.triggerEl = this.el;
6522 this.menu = this.addxtype(Roo.apply({}, this.menu));
6525 this.el.on('click', this.onClick, this);
6527 //if(this.tagtype == 'span'){
6528 // this.el.select('span',true).on('click', this.onClick, this);
6531 // at this point parent should be available..
6532 this.parent().register(this);
6535 onClick : function(e)
6537 if (e.getTarget('.dropdown-menu-item')) {
6538 // did you click on a menu itemm.... - then don't trigger onclick..
6543 this.preventDefault ||
6546 Roo.log("NavItem - prevent Default?");
6550 if (this.disabled) {
6554 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6555 if (tg && tg.transition) {
6556 Roo.log("waiting for the transitionend");
6562 //Roo.log("fire event clicked");
6563 if(this.fireEvent('click', this, e) === false){
6567 if(this.tagtype == 'span'){
6571 //Roo.log(this.href);
6572 var ael = this.el.select('a',true).first();
6575 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6576 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6577 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6578 return; // ignore... - it's a 'hash' to another page.
6580 Roo.log("NavItem - prevent Default?");
6582 this.scrollToElement(e);
6586 var p = this.parent();
6588 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6589 if (typeof(p.setActiveItem) !== 'undefined') {
6590 p.setActiveItem(this);
6594 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6595 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6596 // remove the collapsed menu expand...
6597 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6601 isActive: function () {
6604 setActive : function(state, fire, is_was_active)
6606 if (this.active && !state && this.navId) {
6607 this.was_active = true;
6608 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6610 nv.clearWasActive(this);
6614 this.active = state;
6617 this.el.removeClass('active');
6618 this.navLink ? this.navLink.removeClass('active') : false;
6619 } else if (!this.el.hasClass('active')) {
6621 this.el.addClass('active');
6622 if (Roo.bootstrap.version == 4 && this.navLink ) {
6623 this.navLink.addClass('active');
6628 this.fireEvent('changed', this, state);
6631 // show a panel if it's registered and related..
6633 if (!this.navId || !this.tabId || !state || is_was_active) {
6637 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6641 var pan = tg.getPanelByName(this.tabId);
6645 // if we can not flip to new panel - go back to old nav highlight..
6646 if (false == tg.showPanel(pan)) {
6647 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6649 var onav = nv.getWasActive();
6651 onav.setActive(true, false, true);
6660 // this should not be here...
6661 setDisabled : function(state)
6663 this.disabled = state;
6665 this.el.removeClass('disabled');
6666 } else if (!this.el.hasClass('disabled')) {
6667 this.el.addClass('disabled');
6673 * Fetch the element to display the tooltip on.
6674 * @return {Roo.Element} defaults to this.el
6676 tooltipEl : function()
6678 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6681 scrollToElement : function(e)
6683 var c = document.body;
6686 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6688 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6689 c = document.documentElement;
6692 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6698 var o = target.calcOffsetsTo(c);
6705 this.fireEvent('scrollto', this, options, e);
6707 Roo.get(c).scrollTo('top', options.value, true);
6712 * Set the HTML (text content) of the item
6713 * @param {string} html content for the nav item
6715 setHtml : function(html)
6718 this.htmlEl.dom.innerHTML = html;
6730 * <span> icon </span>
6731 * <span> text </span>
6732 * <span>badge </span>
6736 * @class Roo.bootstrap.nav.SidebarItem
6737 * @extends Roo.bootstrap.nav.Item
6738 * Bootstrap Navbar.NavSidebarItem class
6740 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6741 * {Boolean} open is the menu open
6742 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6743 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6744 * {String} buttonSize (sm|md|lg)the extra classes for the button
6745 * {Boolean} showArrow show arrow next to the text (default true)
6747 * Create a new Navbar Button
6748 * @param {Object} config The config object
6750 Roo.bootstrap.nav.SidebarItem = function(config){
6751 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6756 * The raw click event for the entire grid.
6757 * @param {Roo.EventObject} e
6762 * Fires when the active item active state changes
6763 * @param {Roo.bootstrap.nav.SidebarItem} this
6764 * @param {boolean} state the new state
6772 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6774 badgeWeight : 'default',
6780 buttonWeight : 'default',
6786 getAutoCreate : function(){
6791 href : this.href || '#',
6797 if(this.buttonView){
6800 href : this.href || '#',
6801 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6814 cfg.cls += ' active';
6817 if (this.disabled) {
6818 cfg.cls += ' disabled';
6821 cfg.cls += ' open x-open';
6824 if (this.glyphicon || this.icon) {
6825 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6826 a.cn.push({ tag : 'i', cls : c }) ;
6829 if(!this.buttonView){
6832 html : this.html || ''
6839 if (this.badge !== '') {
6840 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6846 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849 a.cls += ' dropdown-toggle treeview' ;
6855 initEvents : function()
6857 if (typeof (this.menu) != 'undefined') {
6858 this.menu.parentType = this.xtype;
6859 this.menu.triggerEl = this.el;
6860 this.menu = this.addxtype(Roo.apply({}, this.menu));
6863 this.el.on('click', this.onClick, this);
6865 if(this.badge !== ''){
6866 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6871 onClick : function(e)
6878 if(this.preventDefault){
6882 this.fireEvent('click', this, e);
6885 disable : function()
6887 this.setDisabled(true);
6892 this.setDisabled(false);
6895 setDisabled : function(state)
6897 if(this.disabled == state){
6901 this.disabled = state;
6904 this.el.addClass('disabled');
6908 this.el.removeClass('disabled');
6913 setActive : function(state)
6915 if(this.active == state){
6919 this.active = state;
6922 this.el.addClass('active');
6926 this.el.removeClass('active');
6931 isActive: function ()
6936 setBadge : function(str)
6942 this.badgeEl.dom.innerHTML = str;
6959 * @class Roo.bootstrap.nav.ProgressBar
6960 * @extends Roo.bootstrap.Component
6961 * @children Roo.bootstrap.nav.ProgressBarItem
6962 * Bootstrap NavProgressBar class
6965 * Create a new nav progress bar - a bar indicating step along a process
6966 * @param {Object} config The config object
6969 Roo.bootstrap.nav.ProgressBar = function(config){
6970 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6972 this.bullets = this.bullets || [];
6974 // Roo.bootstrap.nav.ProgressBar.register(this);
6978 * Fires when the active item changes
6979 * @param {Roo.bootstrap.nav.ProgressBar} this
6980 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6981 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6988 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6990 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6991 * Bullets for the Nav Progress bar for the toolbar
6996 getAutoCreate : function()
6998 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7002 cls : 'roo-navigation-bar-group',
7006 cls : 'roo-navigation-top-bar'
7010 cls : 'roo-navigation-bullets-bar',
7014 cls : 'roo-navigation-bar'
7021 cls : 'roo-navigation-bottom-bar'
7031 initEvents: function()
7036 onRender : function(ct, position)
7038 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7040 if(this.bullets.length){
7041 Roo.each(this.bullets, function(b){
7050 addItem : function(cfg)
7052 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7054 item.parentId = this.id;
7055 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058 var top = new Roo.bootstrap.Element({
7060 cls : 'roo-navigation-bar-text'
7063 var bottom = new Roo.bootstrap.Element({
7065 cls : 'roo-navigation-bar-text'
7068 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7069 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7071 var topText = new Roo.bootstrap.Element({
7073 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076 var bottomText = new Roo.bootstrap.Element({
7078 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081 topText.onRender(top.el, null);
7082 bottomText.onRender(bottom.el, null);
7085 item.bottomEl = bottom;
7088 this.barItems.push(item);
7093 getActive : function()
7097 Roo.each(this.barItems, function(v){
7099 if (!v.isActive()) {
7111 setActiveItem : function(item)
7115 Roo.each(this.barItems, function(v){
7116 if (v.rid == item.rid) {
7126 item.setActive(true);
7128 this.fireEvent('changed', this, item, prev);
7131 getBarItem: function(rid)
7135 Roo.each(this.barItems, function(e) {
7147 indexOfItem : function(item)
7151 Roo.each(this.barItems, function(v, i){
7153 if (v.rid != item.rid) {
7164 setActiveNext : function()
7166 var i = this.indexOfItem(this.getActive());
7168 if (i > this.barItems.length) {
7172 this.setActiveItem(this.barItems[i+1]);
7175 setActivePrev : function()
7177 var i = this.indexOfItem(this.getActive());
7183 this.setActiveItem(this.barItems[i-1]);
7188 if(!this.barItems.length){
7192 var width = 100 / this.barItems.length;
7194 Roo.each(this.barItems, function(i){
7195 i.el.setStyle('width', width + '%');
7196 i.topEl.el.setStyle('width', width + '%');
7197 i.bottomEl.el.setStyle('width', width + '%');
7211 * @class Roo.bootstrap.nav.ProgressBarItem
7212 * @extends Roo.bootstrap.Component
7213 * Bootstrap NavProgressBarItem class
7214 * @cfg {String} rid the reference id
7215 * @cfg {Boolean} active (true|false) Is item active default false
7216 * @cfg {Boolean} disabled (true|false) Is item active default false
7217 * @cfg {String} html
7218 * @cfg {String} position (top|bottom) text position default bottom
7219 * @cfg {String} icon show icon instead of number
7222 * Create a new NavProgressBarItem
7223 * @param {Object} config The config object
7225 Roo.bootstrap.nav.ProgressBarItem = function(config){
7226 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7231 * The raw click event for the entire grid.
7232 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7233 * @param {Roo.EventObject} e
7240 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7246 position : 'bottom',
7249 getAutoCreate : function()
7251 var iconCls = 'roo-navigation-bar-item-icon';
7253 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7257 cls: 'roo-navigation-bar-item',
7267 cfg.cls += ' active';
7270 cfg.cls += ' disabled';
7276 disable : function()
7278 this.setDisabled(true);
7283 this.setDisabled(false);
7286 initEvents: function()
7288 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7290 this.iconEl.on('click', this.onClick, this);
7293 onClick : function(e)
7301 if(this.fireEvent('click', this, e) === false){
7305 this.parent().setActiveItem(this);
7308 isActive: function ()
7313 setActive : function(state)
7315 if(this.active == state){
7319 this.active = state;
7322 this.el.addClass('active');
7326 this.el.removeClass('active');
7331 setDisabled : function(state)
7333 if(this.disabled == state){
7337 this.disabled = state;
7340 this.el.addClass('disabled');
7344 this.el.removeClass('disabled');
7347 tooltipEl : function()
7349 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7360 Roo.namespace('Roo.bootstrap.breadcrumb');
7364 * @class Roo.bootstrap.breadcrumb.Nav
7365 * @extends Roo.bootstrap.Component
7366 * Bootstrap Breadcrumb Nav Class
7368 * @children Roo.bootstrap.breadcrumb.Item
7371 * Create a new breadcrumb.Nav
7372 * @param {Object} config The config object
7376 Roo.bootstrap.breadcrumb.Nav = function(config){
7377 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7382 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7384 getAutoCreate : function()
7401 initEvents: function()
7403 this.olEl = this.el.select('ol',true).first();
7405 getChildContainer : function()
7421 * @class Roo.bootstrap.breadcrumb.Nav
7422 * @extends Roo.bootstrap.Component
7423 * @children Roo.bootstrap.Component
7424 * Bootstrap Breadcrumb Nav Class
7427 * @cfg {String} html the content of the link.
7428 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7429 * @cfg {Boolean} active is it active
7433 * Create a new breadcrumb.Nav
7434 * @param {Object} config The config object
7437 Roo.bootstrap.breadcrumb.Item = function(config){
7438 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7443 * The img click event for the img.
7444 * @param {Roo.EventObject} e
7451 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7456 getAutoCreate : function()
7461 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7463 if (this.href !== false) {
7470 cfg.html = this.html;
7476 initEvents: function()
7479 this.el.select('a', true).first().on('click',this.onClick, this)
7483 onClick : function(e)
7486 this.fireEvent('click',this, e);
7499 * @class Roo.bootstrap.Row
7500 * @extends Roo.bootstrap.Component
7501 * @children Roo.bootstrap.Component
7502 * Bootstrap Row class (contains columns...)
7506 * @param {Object} config The config object
7509 Roo.bootstrap.Row = function(config){
7510 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7513 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7515 getAutoCreate : function(){
7534 * @class Roo.bootstrap.Pagination
7535 * @extends Roo.bootstrap.Component
7536 * @children Roo.bootstrap.Pagination
7537 * Bootstrap Pagination class
7539 * @cfg {String} size (xs|sm|md|lg|xl)
7540 * @cfg {Boolean} inverse
7543 * Create a new Pagination
7544 * @param {Object} config The config object
7547 Roo.bootstrap.Pagination = function(config){
7548 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7551 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7557 getAutoCreate : function(){
7563 cfg.cls += ' inverse';
7569 cfg.cls += " " + this.cls;
7587 * @class Roo.bootstrap.PaginationItem
7588 * @extends Roo.bootstrap.Component
7589 * Bootstrap PaginationItem class
7590 * @cfg {String} html text
7591 * @cfg {String} href the link
7592 * @cfg {Boolean} preventDefault (true | false) default true
7593 * @cfg {Boolean} active (true | false) default false
7594 * @cfg {Boolean} disabled default false
7598 * Create a new PaginationItem
7599 * @param {Object} config The config object
7603 Roo.bootstrap.PaginationItem = function(config){
7604 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7609 * The raw click event for the entire grid.
7610 * @param {Roo.EventObject} e
7616 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7620 preventDefault: true,
7625 getAutoCreate : function(){
7631 href : this.href ? this.href : '#',
7632 html : this.html ? this.html : ''
7642 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7652 initEvents: function() {
7654 this.el.on('click', this.onClick, this);
7657 onClick : function(e)
7659 Roo.log('PaginationItem on click ');
7660 if(this.preventDefault){
7668 this.fireEvent('click', this, e);
7684 * @class Roo.bootstrap.Slider
7685 * @extends Roo.bootstrap.Component
7686 * Bootstrap Slider class
7689 * Create a new Slider
7690 * @param {Object} config The config object
7693 Roo.bootstrap.Slider = function(config){
7694 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7697 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7699 getAutoCreate : function(){
7703 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7719 * Ext JS Library 1.1.1
7720 * Copyright(c) 2006-2007, Ext JS, LLC.
7722 * Originally Released Under LGPL - original licence link has changed is not relivant.
7725 * <script type="text/javascript">
7728 * @extends Roo.dd.DDProxy
7729 * @class Roo.grid.SplitDragZone
7730 * Support for Column Header resizing
7732 * @param {Object} config
7735 // This is a support class used internally by the Grid components
7736 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7738 this.view = grid.getView();
7739 this.proxy = this.view.resizeProxy;
7740 Roo.grid.SplitDragZone.superclass.constructor.call(
7743 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7745 dragElId : Roo.id(this.proxy.dom),
7750 this.setHandleElId(Roo.id(hd));
7751 if (hd2 !== false) {
7752 this.setOuterHandleElId(Roo.id(hd2));
7755 this.scroll = false;
7757 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7758 fly: Roo.Element.fly,
7760 b4StartDrag : function(x, y){
7761 this.view.headersDisabled = true;
7762 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7763 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7765 this.proxy.setHeight(h);
7767 // for old system colWidth really stored the actual width?
7768 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7769 // which in reality did not work.. - it worked only for fixed sizes
7770 // for resizable we need to use actual sizes.
7771 var w = this.cm.getColumnWidth(this.cellIndex);
7772 if (!this.view.mainWrap) {
7774 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7779 // this was w-this.grid.minColumnWidth;
7780 // doesnt really make sense? - w = thie curren width or the rendered one?
7781 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7782 this.resetConstraints();
7783 this.setXConstraint(minw, 1000);
7784 this.setYConstraint(0, 0);
7785 this.minX = x - minw;
7786 this.maxX = x + 1000;
7788 if (!this.view.mainWrap) { // this is Bootstrap code..
7789 this.getDragEl().style.display='block';
7792 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796 handleMouseDown : function(e){
7797 ev = Roo.EventObject.setEvent(e);
7798 var t = this.fly(ev.getTarget());
7799 if(t.hasClass("x-grid-split")){
7800 this.cellIndex = this.view.getCellIndex(t.dom);
7802 this.cm = this.grid.colModel;
7803 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7804 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7809 endDrag : function(e){
7810 this.view.headersDisabled = false;
7811 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7812 var diff = endX - this.startPos;
7814 var w = this.cm.getColumnWidth(this.cellIndex);
7815 if (!this.view.mainWrap) {
7818 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7821 autoOffset : function(){
7826 * Ext JS Library 1.1.1
7827 * Copyright(c) 2006-2007, Ext JS, LLC.
7829 * Originally Released Under LGPL - original licence link has changed is not relivant.
7832 * <script type="text/javascript">
7836 * @class Roo.grid.AbstractSelectionModel
7837 * @extends Roo.util.Observable
7839 * Abstract base class for grid SelectionModels. It provides the interface that should be
7840 * implemented by descendant classes. This class should not be directly instantiated.
7843 Roo.grid.AbstractSelectionModel = function(){
7844 this.locked = false;
7845 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7848 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7849 /** @ignore Called by the grid automatically. Do not call directly. */
7850 init : function(grid){
7856 * Locks the selections.
7863 * Unlocks the selections.
7865 unlock : function(){
7866 this.locked = false;
7870 * Returns true if the selections are locked.
7873 isLocked : function(){
7878 * Ext JS Library 1.1.1
7879 * Copyright(c) 2006-2007, Ext JS, LLC.
7881 * Originally Released Under LGPL - original licence link has changed is not relivant.
7884 * <script type="text/javascript">
7887 * @extends Roo.grid.AbstractSelectionModel
7888 * @class Roo.grid.RowSelectionModel
7889 * The default SelectionModel used by {@link Roo.grid.Grid}.
7890 * It supports multiple selections and keyboard selection/navigation.
7892 * @param {Object} config
7894 Roo.grid.RowSelectionModel = function(config){
7895 Roo.apply(this, config);
7896 this.selections = new Roo.util.MixedCollection(false, function(o){
7901 this.lastActive = false;
7905 * @event selectionchange
7906 * Fires when the selection changes
7907 * @param {SelectionModel} this
7909 "selectionchange" : true,
7911 * @event afterselectionchange
7912 * Fires after the selection changes (eg. by key press or clicking)
7913 * @param {SelectionModel} this
7915 "afterselectionchange" : true,
7917 * @event beforerowselect
7918 * Fires when a row is selected being selected, return false to cancel.
7919 * @param {SelectionModel} this
7920 * @param {Number} rowIndex The selected index
7921 * @param {Boolean} keepExisting False if other selections will be cleared
7923 "beforerowselect" : true,
7926 * Fires when a row is selected.
7927 * @param {SelectionModel} this
7928 * @param {Number} rowIndex The selected index
7929 * @param {Roo.data.Record} r The record
7933 * @event rowdeselect
7934 * Fires when a row is deselected.
7935 * @param {SelectionModel} this
7936 * @param {Number} rowIndex The selected index
7938 "rowdeselect" : true
7940 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7941 this.locked = false;
7944 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7946 * @cfg {Boolean} singleSelect
7947 * True to allow selection of only one row at a time (defaults to false)
7949 singleSelect : false,
7952 initEvents : function(){
7954 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7955 this.grid.on("mousedown", this.handleMouseDown, this);
7956 }else{ // allow click to work like normal
7957 this.grid.on("rowclick", this.handleDragableRowClick, this);
7959 // bootstrap does not have a view..
7960 var view = this.grid.view ? this.grid.view : this.grid;
7961 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7964 this.selectPrevious(e.shiftKey);
7965 }else if(this.last !== false && this.lastActive !== false){
7966 var last = this.last;
7967 this.selectRange(this.last, this.lastActive-1);
7968 view.focusRow(this.lastActive);
7973 this.selectFirstRow();
7975 this.fireEvent("afterselectionchange", this);
7977 "down" : function(e){
7979 this.selectNext(e.shiftKey);
7980 }else if(this.last !== false && this.lastActive !== false){
7981 var last = this.last;
7982 this.selectRange(this.last, this.lastActive+1);
7983 view.focusRow(this.lastActive);
7988 this.selectFirstRow();
7990 this.fireEvent("afterselectionchange", this);
7996 view.on("refresh", this.onRefresh, this);
7997 view.on("rowupdated", this.onRowUpdated, this);
7998 view.on("rowremoved", this.onRemove, this);
8002 onRefresh : function(){
8003 var ds = this.grid.ds, i, v = this.grid.view;
8004 var s = this.selections;
8006 if((i = ds.indexOfId(r.id)) != -1){
8008 s.add(ds.getAt(i)); // updating the selection relate data
8016 onRemove : function(v, index, r){
8017 this.selections.remove(r);
8021 onRowUpdated : function(v, index, r){
8022 if(this.isSelected(r)){
8023 v.onRowSelect(index);
8029 * @param {Array} records The records to select
8030 * @param {Boolean} keepExisting (optional) True to keep existing selections
8032 selectRecords : function(records, keepExisting){
8034 this.clearSelections();
8036 var ds = this.grid.ds;
8037 for(var i = 0, len = records.length; i < len; i++){
8038 this.selectRow(ds.indexOf(records[i]), true);
8043 * Gets the number of selected rows.
8046 getCount : function(){
8047 return this.selections.length;
8051 * Selects the first row in the grid.
8053 selectFirstRow : function(){
8058 * Select the last row.
8059 * @param {Boolean} keepExisting (optional) True to keep existing selections
8061 selectLastRow : function(keepExisting){
8062 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066 * Selects the row immediately following the last selected row.
8067 * @param {Boolean} keepExisting (optional) True to keep existing selections
8069 selectNext : function(keepExisting){
8070 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8071 this.selectRow(this.last+1, keepExisting);
8072 var view = this.grid.view ? this.grid.view : this.grid;
8073 view.focusRow(this.last);
8078 * Selects the row that precedes the last selected row.
8079 * @param {Boolean} keepExisting (optional) True to keep existing selections
8081 selectPrevious : function(keepExisting){
8083 this.selectRow(this.last-1, keepExisting);
8084 var view = this.grid.view ? this.grid.view : this.grid;
8085 view.focusRow(this.last);
8090 * Returns the selected records
8091 * @return {Array} Array of selected records
8093 getSelections : function(){
8094 return [].concat(this.selections.items);
8098 * Returns the first selected record.
8101 getSelected : function(){
8102 return this.selections.itemAt(0);
8107 * Clears all selections.
8109 clearSelections : function(fast){
8114 var ds = this.grid.ds;
8115 var s = this.selections;
8117 this.deselectRow(ds.indexOfId(r.id));
8121 this.selections.clear();
8130 selectAll : function(){
8134 this.selections.clear();
8135 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8136 this.selectRow(i, true);
8141 * Returns True if there is a selection.
8144 hasSelection : function(){
8145 return this.selections.length > 0;
8149 * Returns True if the specified row is selected.
8150 * @param {Number/Record} record The record or index of the record to check
8153 isSelected : function(index){
8154 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8155 return (r && this.selections.key(r.id) ? true : false);
8159 * Returns True if the specified record id is selected.
8160 * @param {String} id The id of record to check
8163 isIdSelected : function(id){
8164 return (this.selections.key(id) ? true : false);
8168 handleMouseDown : function(e, t)
8170 var view = this.grid.view ? this.grid.view : this.grid;
8172 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8175 if(e.shiftKey && this.last !== false){
8176 var last = this.last;
8177 this.selectRange(last, rowIndex, e.ctrlKey);
8178 this.last = last; // reset the last
8179 view.focusRow(rowIndex);
8181 var isSelected = this.isSelected(rowIndex);
8182 if(e.button !== 0 && isSelected){
8183 view.focusRow(rowIndex);
8184 }else if(e.ctrlKey && isSelected){
8185 this.deselectRow(rowIndex);
8186 }else if(!isSelected){
8187 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8188 view.focusRow(rowIndex);
8191 this.fireEvent("afterselectionchange", this);
8194 handleDragableRowClick : function(grid, rowIndex, e)
8196 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8197 this.selectRow(rowIndex, false);
8198 var view = this.grid.view ? this.grid.view : this.grid;
8199 view.focusRow(rowIndex);
8200 this.fireEvent("afterselectionchange", this);
8205 * Selects multiple rows.
8206 * @param {Array} rows Array of the indexes of the row to select
8207 * @param {Boolean} keepExisting (optional) True to keep existing selections
8209 selectRows : function(rows, keepExisting){
8211 this.clearSelections();
8213 for(var i = 0, len = rows.length; i < len; i++){
8214 this.selectRow(rows[i], true);
8219 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8220 * @param {Number} startRow The index of the first row in the range
8221 * @param {Number} endRow The index of the last row in the range
8222 * @param {Boolean} keepExisting (optional) True to retain existing selections
8224 selectRange : function(startRow, endRow, keepExisting){
8229 this.clearSelections();
8231 if(startRow <= endRow){
8232 for(var i = startRow; i <= endRow; i++){
8233 this.selectRow(i, true);
8236 for(var i = startRow; i >= endRow; i--){
8237 this.selectRow(i, true);
8243 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8244 * @param {Number} startRow The index of the first row in the range
8245 * @param {Number} endRow The index of the last row in the range
8247 deselectRange : function(startRow, endRow, preventViewNotify){
8251 for(var i = startRow; i <= endRow; i++){
8252 this.deselectRow(i, preventViewNotify);
8258 * @param {Number} row The index of the row to select
8259 * @param {Boolean} keepExisting (optional) True to keep existing selections
8261 selectRow : function(index, keepExisting, preventViewNotify){
8262 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8265 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8266 if(!keepExisting || this.singleSelect){
8267 this.clearSelections();
8269 var r = this.grid.ds.getAt(index);
8270 this.selections.add(r);
8271 this.last = this.lastActive = index;
8272 if(!preventViewNotify){
8273 var view = this.grid.view ? this.grid.view : this.grid;
8274 view.onRowSelect(index);
8276 this.fireEvent("rowselect", this, index, r);
8277 this.fireEvent("selectionchange", this);
8283 * @param {Number} row The index of the row to deselect
8285 deselectRow : function(index, preventViewNotify){
8289 if(this.last == index){
8292 if(this.lastActive == index){
8293 this.lastActive = false;
8295 var r = this.grid.ds.getAt(index);
8296 this.selections.remove(r);
8297 if(!preventViewNotify){
8298 var view = this.grid.view ? this.grid.view : this.grid;
8299 view.onRowDeselect(index);
8301 this.fireEvent("rowdeselect", this, index);
8302 this.fireEvent("selectionchange", this);
8306 restoreLast : function(){
8308 this.last = this._last;
8313 acceptsNav : function(row, col, cm){
8314 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318 onEditorKey : function(field, e){
8319 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8324 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8326 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8328 }else if(k == e.ENTER && !e.ctrlKey){
8332 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8334 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8336 }else if(k == e.ESC){
8340 g.startEditing(newCell[0], newCell[1]);
8345 * Ext JS Library 1.1.1
8346 * Copyright(c) 2006-2007, Ext JS, LLC.
8348 * Originally Released Under LGPL - original licence link has changed is not relivant.
8351 * <script type="text/javascript">
8356 * @class Roo.grid.ColumnModel
8357 * @extends Roo.util.Observable
8358 * This is the default implementation of a ColumnModel used by the Grid. It defines
8359 * the columns in the grid.
8362 var colModel = new Roo.grid.ColumnModel([
8363 {header: "Ticker", width: 60, sortable: true, locked: true},
8364 {header: "Company Name", width: 150, sortable: true},
8365 {header: "Market Cap.", width: 100, sortable: true},
8366 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8367 {header: "Employees", width: 100, sortable: true, resizable: false}
8372 * The config options listed for this class are options which may appear in each
8373 * individual column definition.
8374 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8376 * @param {Object} config An Array of column config objects. See this class's
8377 * config objects for details.
8379 Roo.grid.ColumnModel = function(config){
8381 * The config passed into the constructor
8383 this.config = []; //config;
8386 // if no id, create one
8387 // if the column does not have a dataIndex mapping,
8388 // map it to the order it is in the config
8389 for(var i = 0, len = config.length; i < len; i++){
8390 this.addColumn(config[i]);
8395 * The width of columns which have no width specified (defaults to 100)
8398 this.defaultWidth = 100;
8401 * Default sortable of columns which have no sortable specified (defaults to false)
8404 this.defaultSortable = false;
8408 * @event widthchange
8409 * Fires when the width of a column changes.
8410 * @param {ColumnModel} this
8411 * @param {Number} columnIndex The column index
8412 * @param {Number} newWidth The new width
8414 "widthchange": true,
8416 * @event headerchange
8417 * Fires when the text of a header changes.
8418 * @param {ColumnModel} this
8419 * @param {Number} columnIndex The column index
8420 * @param {Number} newText The new header text
8422 "headerchange": true,
8424 * @event hiddenchange
8425 * Fires when a column is hidden or "unhidden".
8426 * @param {ColumnModel} this
8427 * @param {Number} columnIndex The column index
8428 * @param {Boolean} hidden true if hidden, false otherwise
8430 "hiddenchange": true,
8432 * @event columnmoved
8433 * Fires when a column is moved.
8434 * @param {ColumnModel} this
8435 * @param {Number} oldIndex
8436 * @param {Number} newIndex
8438 "columnmoved" : true,
8440 * @event columlockchange
8441 * Fires when a column's locked state is changed
8442 * @param {ColumnModel} this
8443 * @param {Number} colIndex
8444 * @param {Boolean} locked true if locked
8446 "columnlockchange" : true
8448 Roo.grid.ColumnModel.superclass.constructor.call(this);
8450 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8452 * @cfg {String} header The header text to display in the Grid view.
8455 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8458 * @cfg {String} smHeader Header at Bootsrap Small width
8461 * @cfg {String} mdHeader Header at Bootsrap Medium width
8464 * @cfg {String} lgHeader Header at Bootsrap Large width
8467 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8470 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8471 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8472 * specified, the column's index is used as an index into the Record's data Array.
8475 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8476 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8479 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8480 * Defaults to the value of the {@link #defaultSortable} property.
8481 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8484 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8487 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8490 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8493 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8496 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8497 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8498 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8499 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8502 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8505 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8508 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8511 * @cfg {String} cursor (Optional)
8514 * @cfg {String} tooltip (Optional)
8517 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8520 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8523 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8526 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8529 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8532 * Returns the id of the column at the specified index.
8533 * @param {Number} index The column index
8534 * @return {String} the id
8536 getColumnId : function(index){
8537 return this.config[index].id;
8541 * Returns the column for a specified id.
8542 * @param {String} id The column id
8543 * @return {Object} the column
8545 getColumnById : function(id){
8546 return this.lookup[id];
8551 * Returns the column Object for a specified dataIndex.
8552 * @param {String} dataIndex The column dataIndex
8553 * @return {Object|Boolean} the column or false if not found
8555 getColumnByDataIndex: function(dataIndex){
8556 var index = this.findColumnIndex(dataIndex);
8557 return index > -1 ? this.config[index] : false;
8561 * Returns the index for a specified column id.
8562 * @param {String} id The column id
8563 * @return {Number} the index, or -1 if not found
8565 getIndexById : function(id){
8566 for(var i = 0, len = this.config.length; i < len; i++){
8567 if(this.config[i].id == id){
8575 * Returns the index for a specified column dataIndex.
8576 * @param {String} dataIndex The column dataIndex
8577 * @return {Number} the index, or -1 if not found
8580 findColumnIndex : function(dataIndex){
8581 for(var i = 0, len = this.config.length; i < len; i++){
8582 if(this.config[i].dataIndex == dataIndex){
8590 moveColumn : function(oldIndex, newIndex){
8591 var c = this.config[oldIndex];
8592 this.config.splice(oldIndex, 1);
8593 this.config.splice(newIndex, 0, c);
8594 this.dataMap = null;
8595 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8598 isLocked : function(colIndex){
8599 return this.config[colIndex].locked === true;
8602 setLocked : function(colIndex, value, suppressEvent){
8603 if(this.isLocked(colIndex) == value){
8606 this.config[colIndex].locked = value;
8608 this.fireEvent("columnlockchange", this, colIndex, value);
8612 getTotalLockedWidth : function(){
8614 for(var i = 0; i < this.config.length; i++){
8615 if(this.isLocked(i) && !this.isHidden(i)){
8616 this.totalWidth += this.getColumnWidth(i);
8622 getLockedCount : function(){
8623 for(var i = 0, len = this.config.length; i < len; i++){
8624 if(!this.isLocked(i)){
8629 return this.config.length;
8633 * Returns the number of columns.
8636 getColumnCount : function(visibleOnly){
8637 if(visibleOnly === true){
8639 for(var i = 0, len = this.config.length; i < len; i++){
8640 if(!this.isHidden(i)){
8646 return this.config.length;
8650 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8651 * @param {Function} fn
8652 * @param {Object} scope (optional)
8653 * @return {Array} result
8655 getColumnsBy : function(fn, scope){
8657 for(var i = 0, len = this.config.length; i < len; i++){
8658 var c = this.config[i];
8659 if(fn.call(scope||this, c, i) === true){
8667 * Returns true if the specified column is sortable.
8668 * @param {Number} col The column index
8671 isSortable : function(col){
8672 if(typeof this.config[col].sortable == "undefined"){
8673 return this.defaultSortable;
8675 return this.config[col].sortable;
8679 * Returns the rendering (formatting) function defined for the column.
8680 * @param {Number} col The column index.
8681 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8683 getRenderer : function(col){
8684 if(!this.config[col].renderer){
8685 return Roo.grid.ColumnModel.defaultRenderer;
8687 return this.config[col].renderer;
8691 * Sets the rendering (formatting) function for a column.
8692 * @param {Number} col The column index
8693 * @param {Function} fn The function to use to process the cell's raw data
8694 * to return HTML markup for the grid view. The render function is called with
8695 * the following parameters:<ul>
8696 * <li>Data value.</li>
8697 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8698 * <li>css A CSS style string to apply to the table cell.</li>
8699 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8700 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8701 * <li>Row index</li>
8702 * <li>Column index</li>
8703 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8705 setRenderer : function(col, fn){
8706 this.config[col].renderer = fn;
8710 * Returns the width for the specified column.
8711 * @param {Number} col The column index
8712 * @param (optional) {String} gridSize bootstrap width size.
8715 getColumnWidth : function(col, gridSize)
8717 var cfg = this.config[col];
8719 if (typeof(gridSize) == 'undefined') {
8720 return cfg.width * 1 || this.defaultWidth;
8722 if (gridSize === false) { // if we set it..
8723 return cfg.width || false;
8725 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8727 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8728 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8731 return cfg[ sizes[i] ];
8738 * Sets the width for a column.
8739 * @param {Number} col The column index
8740 * @param {Number} width The new width
8742 setColumnWidth : function(col, width, suppressEvent){
8743 this.config[col].width = width;
8744 this.totalWidth = null;
8746 this.fireEvent("widthchange", this, col, width);
8751 * Returns the total width of all columns.
8752 * @param {Boolean} includeHidden True to include hidden column widths
8755 getTotalWidth : function(includeHidden){
8756 if(!this.totalWidth){
8757 this.totalWidth = 0;
8758 for(var i = 0, len = this.config.length; i < len; i++){
8759 if(includeHidden || !this.isHidden(i)){
8760 this.totalWidth += this.getColumnWidth(i);
8764 return this.totalWidth;
8768 * Returns the header for the specified column.
8769 * @param {Number} col The column index
8772 getColumnHeader : function(col){
8773 return this.config[col].header;
8777 * Sets the header for a column.
8778 * @param {Number} col The column index
8779 * @param {String} header The new header
8781 setColumnHeader : function(col, header){
8782 this.config[col].header = header;
8783 this.fireEvent("headerchange", this, col, header);
8787 * Returns the tooltip for the specified column.
8788 * @param {Number} col The column index
8791 getColumnTooltip : function(col){
8792 return this.config[col].tooltip;
8795 * Sets the tooltip for a column.
8796 * @param {Number} col The column index
8797 * @param {String} tooltip The new tooltip
8799 setColumnTooltip : function(col, tooltip){
8800 this.config[col].tooltip = tooltip;
8804 * Returns the dataIndex for the specified column.
8805 * @param {Number} col The column index
8808 getDataIndex : function(col){
8809 return this.config[col].dataIndex;
8813 * Sets the dataIndex for a column.
8814 * @param {Number} col The column index
8815 * @param {Number} dataIndex The new dataIndex
8817 setDataIndex : function(col, dataIndex){
8818 this.config[col].dataIndex = dataIndex;
8824 * Returns true if the cell is editable.
8825 * @param {Number} colIndex The column index
8826 * @param {Number} rowIndex The row index - this is nto actually used..?
8829 isCellEditable : function(colIndex, rowIndex){
8830 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834 * Returns the editor defined for the cell/column.
8835 * return false or null to disable editing.
8836 * @param {Number} colIndex The column index
8837 * @param {Number} rowIndex The row index
8840 getCellEditor : function(colIndex, rowIndex){
8841 return this.config[colIndex].editor;
8845 * Sets if a column is editable.
8846 * @param {Number} col The column index
8847 * @param {Boolean} editable True if the column is editable
8849 setEditable : function(col, editable){
8850 this.config[col].editable = editable;
8855 * Returns true if the column is hidden.
8856 * @param {Number} colIndex The column index
8859 isHidden : function(colIndex){
8860 return this.config[colIndex].hidden;
8865 * Returns true if the column width cannot be changed
8867 isFixed : function(colIndex){
8868 return this.config[colIndex].fixed;
8872 * Returns true if the column can be resized
8875 isResizable : function(colIndex){
8876 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8879 * Sets if a column is hidden.
8880 * @param {Number} colIndex The column index
8881 * @param {Boolean} hidden True if the column is hidden
8883 setHidden : function(colIndex, hidden){
8884 this.config[colIndex].hidden = hidden;
8885 this.totalWidth = null;
8886 this.fireEvent("hiddenchange", this, colIndex, hidden);
8890 * Sets the editor for a column.
8891 * @param {Number} col The column index
8892 * @param {Object} editor The editor object
8894 setEditor : function(col, editor){
8895 this.config[col].editor = editor;
8898 * Add a column (experimental...) - defaults to adding to the end..
8899 * @param {Object} config
8901 addColumn : function(c)
8904 var i = this.config.length;
8907 if(typeof c.dataIndex == "undefined"){
8910 if(typeof c.renderer == "string"){
8911 c.renderer = Roo.util.Format[c.renderer];
8913 if(typeof c.id == "undefined"){
8916 if(c.editor && c.editor.xtype){
8917 c.editor = Roo.factory(c.editor, Roo.grid);
8919 if(c.editor && c.editor.isFormField){
8920 c.editor = new Roo.grid.GridEditor(c.editor);
8922 this.lookup[c.id] = c;
8927 Roo.grid.ColumnModel.defaultRenderer = function(value)
8929 if(typeof value == "object") {
8932 if(typeof value == "string" && value.length < 1){
8936 return String.format("{0}", value);
8939 // Alias for backwards compatibility
8940 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8943 * Ext JS Library 1.1.1
8944 * Copyright(c) 2006-2007, Ext JS, LLC.
8946 * Originally Released Under LGPL - original licence link has changed is not relivant.
8949 * <script type="text/javascript">
8953 * @class Roo.LoadMask
8954 * A simple utility class for generically masking elements while loading data. If the element being masked has
8955 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8956 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8957 * element's UpdateManager load indicator and will be destroyed after the initial load.
8959 * Create a new LoadMask
8960 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8961 * @param {Object} config The config object
8963 Roo.LoadMask = function(el, config){
8964 this.el = Roo.get(el);
8965 Roo.apply(this, config);
8967 this.store.on('beforeload', this.onBeforeLoad, this);
8968 this.store.on('load', this.onLoad, this);
8969 this.store.on('loadexception', this.onLoadException, this);
8970 this.removeMask = false;
8972 var um = this.el.getUpdateManager();
8973 um.showLoadIndicator = false; // disable the default indicator
8974 um.on('beforeupdate', this.onBeforeLoad, this);
8975 um.on('update', this.onLoad, this);
8976 um.on('failure', this.onLoad, this);
8977 this.removeMask = true;
8981 Roo.LoadMask.prototype = {
8983 * @cfg {Boolean} removeMask
8984 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8985 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8990 * The text to display in a centered loading message box (defaults to 'Loading...')
8994 * @cfg {String} msgCls
8995 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8997 msgCls : 'x-mask-loading',
9000 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9006 * Disables the mask to prevent it from being displayed
9008 disable : function(){
9009 this.disabled = true;
9013 * Enables the mask so that it can be displayed
9015 enable : function(){
9016 this.disabled = false;
9019 onLoadException : function()
9023 if (typeof(arguments[3]) != 'undefined') {
9024 Roo.MessageBox.alert("Error loading",arguments[3]);
9028 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9029 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9036 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9041 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045 onBeforeLoad : function(){
9047 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9052 destroy : function(){
9054 this.store.un('beforeload', this.onBeforeLoad, this);
9055 this.store.un('load', this.onLoad, this);
9056 this.store.un('loadexception', this.onLoadException, this);
9058 var um = this.el.getUpdateManager();
9059 um.un('beforeupdate', this.onBeforeLoad, this);
9060 um.un('update', this.onLoad, this);
9061 um.un('failure', this.onLoad, this);
9065 * @class Roo.bootstrap.Table
9067 * @extends Roo.bootstrap.Component
9068 * @children Roo.bootstrap.TableBody
9069 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9070 * Similar to Roo.grid.Grid
9072 var table = Roo.factory({
9074 xns : Roo.bootstrap,
9075 autoSizeColumns: true,
9082 sortInfo : { direction : 'ASC', field: 'name' },
9084 xtype : 'HttpProxy',
9087 url : 'https://example.com/some.data.url.json'
9090 xtype : 'JsonReader',
9092 fields : [ 'id', 'name', whatever' ],
9099 xtype : 'ColumnModel',
9103 dataIndex : 'is_in_group',
9106 renderer : function(v, x , r) {
9108 return String.format("{0}", v)
9114 xtype : 'RowSelectionModel',
9115 xns : Roo.bootstrap.Table
9116 // you can add listeners to catch selection change here....
9122 grid.render(Roo.get("some-div"));
9125 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9130 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9131 * @cfg {Roo.data.Store} store The data store to use
9132 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9134 * @cfg {String} cls table class
9137 * @cfg {boolean} striped Should the rows be alternative striped
9138 * @cfg {boolean} bordered Add borders to the table
9139 * @cfg {boolean} hover Add hover highlighting
9140 * @cfg {boolean} condensed Format condensed
9141 * @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,
9142 * also adds table-responsive (see bootstrap docs for details)
9143 * @cfg {Boolean} loadMask (true|false) default false
9144 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9145 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9146 * @cfg {Boolean} rowSelection (true|false) default false
9147 * @cfg {Boolean} cellSelection (true|false) default false
9148 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9149 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9150 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9151 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9152 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9153 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9156 * Create a new Table
9157 * @param {Object} config The config object
9160 Roo.bootstrap.Table = function(config)
9162 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9165 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9166 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9167 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9168 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9170 this.view = this; // compat with grid.
9172 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9174 this.sm.grid = this;
9175 this.selModel = Roo.factory(this.sm, Roo.grid);
9176 this.sm = this.selModel;
9177 this.sm.xmodule = this.xmodule || false;
9180 if (this.cm && typeof(this.cm.config) == 'undefined') {
9181 this.colModel = new Roo.grid.ColumnModel(this.cm);
9182 this.cm = this.colModel;
9183 this.cm.xmodule = this.xmodule || false;
9186 this.store= Roo.factory(this.store, Roo.data);
9187 this.ds = this.store;
9188 this.ds.xmodule = this.xmodule || false;
9191 if (this.footer && this.store) {
9192 this.footer.dataSource = this.ds;
9193 this.footer = Roo.factory(this.footer);
9200 * Fires when a cell is clicked
9201 * @param {Roo.bootstrap.Table} this
9202 * @param {Roo.Element} el
9203 * @param {Number} rowIndex
9204 * @param {Number} columnIndex
9205 * @param {Roo.EventObject} e
9209 * @event celldblclick
9210 * Fires when a cell is double clicked
9211 * @param {Roo.bootstrap.Table} this
9212 * @param {Roo.Element} el
9213 * @param {Number} rowIndex
9214 * @param {Number} columnIndex
9215 * @param {Roo.EventObject} e
9217 "celldblclick" : true,
9220 * Fires when a row is clicked
9221 * @param {Roo.bootstrap.Table} this
9222 * @param {Roo.Element} el
9223 * @param {Number} rowIndex
9224 * @param {Roo.EventObject} e
9228 * @event rowdblclick
9229 * Fires when a row is double clicked
9230 * @param {Roo.bootstrap.Table} this
9231 * @param {Roo.Element} el
9232 * @param {Number} rowIndex
9233 * @param {Roo.EventObject} e
9235 "rowdblclick" : true,
9238 * Fires when a mouseover occur
9239 * @param {Roo.bootstrap.Table} this
9240 * @param {Roo.Element} el
9241 * @param {Number} rowIndex
9242 * @param {Number} columnIndex
9243 * @param {Roo.EventObject} e
9248 * Fires when a mouseout occur
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Number} columnIndex
9253 * @param {Roo.EventObject} e
9258 * Fires when a row is rendered, so you can change add a style to it.
9259 * @param {Roo.bootstrap.Table} this
9260 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9264 * @event rowsrendered
9265 * Fires when all the rows have been rendered
9266 * @param {Roo.bootstrap.Table} this
9268 'rowsrendered' : true,
9270 * @event contextmenu
9271 * The raw contextmenu event for the entire grid.
9272 * @param {Roo.EventObject} e
9274 "contextmenu" : true,
9276 * @event rowcontextmenu
9277 * Fires when a row is right clicked
9278 * @param {Roo.bootstrap.Table} this
9279 * @param {Number} rowIndex
9280 * @param {Roo.EventObject} e
9282 "rowcontextmenu" : true,
9284 * @event cellcontextmenu
9285 * Fires when a cell is right clicked
9286 * @param {Roo.bootstrap.Table} this
9287 * @param {Number} rowIndex
9288 * @param {Number} cellIndex
9289 * @param {Roo.EventObject} e
9291 "cellcontextmenu" : true,
9293 * @event headercontextmenu
9294 * Fires when a header is right clicked
9295 * @param {Roo.bootstrap.Table} this
9296 * @param {Number} columnIndex
9297 * @param {Roo.EventObject} e
9299 "headercontextmenu" : true,
9302 * The raw mousedown event for the entire grid.
9303 * @param {Roo.EventObject} e
9310 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9326 enableColumnResize: true,
9328 rowSelection : false,
9329 cellSelection : false,
9332 minColumnWidth : 50,
9334 // Roo.Element - the tbody
9335 bodyEl: false, // <tbody> Roo.Element - thead element
9336 headEl: false, // <thead> Roo.Element - thead element
9337 resizeProxy : false, // proxy element for dragging?
9341 container: false, // used by gridpanel...
9347 auto_hide_footer : false,
9349 view: false, // actually points to this..
9351 getAutoCreate : function()
9353 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9360 // this get's auto added by panel.Grid
9361 if (this.scrollBody) {
9362 cfg.cls += ' table-body-fixed';
9365 cfg.cls += ' table-striped';
9369 cfg.cls += ' table-hover';
9371 if (this.bordered) {
9372 cfg.cls += ' table-bordered';
9374 if (this.condensed) {
9375 cfg.cls += ' table-condensed';
9378 if (this.responsive) {
9379 cfg.cls += ' table-responsive';
9383 cfg.cls+= ' ' +this.cls;
9389 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9392 if(this.store || this.cm){
9393 if(this.headerShow){
9394 cfg.cn.push(this.renderHeader());
9397 cfg.cn.push(this.renderBody());
9399 if(this.footerShow){
9400 cfg.cn.push(this.renderFooter());
9402 // where does this come from?
9403 //cfg.cls+= ' TableGrid';
9406 return { cn : [ cfg ] };
9409 initEvents : function()
9411 if(!this.store || !this.cm){
9414 if (this.selModel) {
9415 this.selModel.initEvents();
9419 //Roo.log('initEvents with ds!!!!');
9421 this.bodyEl = this.el.select('tbody', true).first();
9422 this.headEl = this.el.select('thead', true).first();
9423 this.mainFoot = this.el.select('tfoot', true).first();
9428 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9429 e.on('click', this.sort, this);
9433 // why is this done????? = it breaks dialogs??
9434 //this.parent().el.setStyle('position', 'relative');
9438 this.footer.parentId = this.id;
9439 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9442 this.el.select('tfoot tr td').first().addClass('hide');
9447 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9450 this.store.on('load', this.onLoad, this);
9451 this.store.on('beforeload', this.onBeforeLoad, this);
9452 this.store.on('update', this.onUpdate, this);
9453 this.store.on('add', this.onAdd, this);
9454 this.store.on("clear", this.clear, this);
9456 this.el.on("contextmenu", this.onContextMenu, this);
9459 this.cm.on("headerchange", this.onHeaderChange, this);
9460 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9462 //?? does bodyEl get replaced on render?
9463 this.bodyEl.on("click", this.onClick, this);
9464 this.bodyEl.on("dblclick", this.onDblClick, this);
9465 this.bodyEl.on('scroll', this.onBodyScroll, this);
9467 // guessing mainbody will work - this relays usually caught by selmodel at present.
9468 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9471 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9474 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9475 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9480 // Compatibility with grid - we implement all the view features at present.
9481 getView : function()
9486 initCSS : function()
9490 var cm = this.cm, styles = [];
9491 this.CSS.removeStyleSheet(this.id + '-cssrules');
9492 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9493 // we can honour xs/sm/md/xl as widths...
9494 // we first have to decide what widht we are currently at...
9495 var sz = Roo.getGridSize();
9499 var cols = []; // visable cols.
9501 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9502 var w = cm.getColumnWidth(i, false);
9504 cols.push( { rel : false, abs : 0 });
9508 cols.push( { rel : false, abs : w });
9510 last = i; // not really..
9513 var w = cm.getColumnWidth(i, sz);
9518 cols.push( { rel : w, abs : false });
9521 var avail = this.bodyEl.dom.clientWidth - total_abs;
9523 var unitWidth = Math.floor(avail / total);
9524 var rem = avail - (unitWidth * total);
9526 var hidden, width, pos = 0 , splithide , left;
9527 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9529 hidden = 'display:none;';
9531 width = 'width:0px;';
9533 if(!cm.isHidden(i)){
9537 // we can honour xs/sm/md/xl ?
9538 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9540 hidden = 'display:none;';
9542 // width should return a small number...
9544 w+=rem; // add the remaining with..
9547 left = "left:" + (pos -4) + "px;";
9548 width = "width:" + w+ "px;";
9551 if (this.responsive) {
9554 hidden = cm.isHidden(i) ? 'display:none;' : '';
9555 splithide = 'display: none;';
9558 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9561 splithide = 'display:none;';
9564 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9565 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9570 //Roo.log(styles.join(''));
9571 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9577 onContextMenu : function(e, t)
9579 this.processEvent("contextmenu", e);
9582 processEvent : function(name, e)
9584 if (name != 'touchstart' ) {
9585 this.fireEvent(name, e);
9588 var t = e.getTarget();
9590 var cell = Roo.get(t);
9596 if(cell.findParent('tfoot', false, true)){
9600 if(cell.findParent('thead', false, true)){
9602 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9603 cell = Roo.get(t).findParent('th', false, true);
9605 Roo.log("failed to find th in thead?");
9606 Roo.log(e.getTarget());
9611 var cellIndex = cell.dom.cellIndex;
9613 var ename = name == 'touchstart' ? 'click' : name;
9614 this.fireEvent("header" + ename, this, cellIndex, e);
9619 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9620 cell = Roo.get(t).findParent('td', false, true);
9622 Roo.log("failed to find th in tbody?");
9623 Roo.log(e.getTarget());
9628 var row = cell.findParent('tr', false, true);
9629 var cellIndex = cell.dom.cellIndex;
9630 var rowIndex = row.dom.rowIndex - 1;
9634 this.fireEvent("row" + name, this, rowIndex, e);
9638 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9644 onMouseover : function(e, el)
9646 var cell = Roo.get(el);
9652 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653 cell = cell.findParent('td', false, true);
9656 var row = cell.findParent('tr', false, true);
9657 var cellIndex = cell.dom.cellIndex;
9658 var rowIndex = row.dom.rowIndex - 1; // start from 0
9660 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9664 onMouseout : function(e, el)
9666 var cell = Roo.get(el);
9672 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9673 cell = cell.findParent('td', false, true);
9676 var row = cell.findParent('tr', false, true);
9677 var cellIndex = cell.dom.cellIndex;
9678 var rowIndex = row.dom.rowIndex - 1; // start from 0
9680 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9684 onClick : function(e, el)
9686 var cell = Roo.get(el);
9688 if(!cell || (!this.cellSelection && !this.rowSelection)){
9692 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9693 cell = cell.findParent('td', false, true);
9696 if(!cell || typeof(cell) == 'undefined'){
9700 var row = cell.findParent('tr', false, true);
9702 if(!row || typeof(row) == 'undefined'){
9706 var cellIndex = cell.dom.cellIndex;
9707 var rowIndex = this.getRowIndex(row);
9709 // why??? - should these not be based on SelectionModel?
9710 //if(this.cellSelection){
9711 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9714 //if(this.rowSelection){
9715 this.fireEvent('rowclick', this, row, rowIndex, e);
9720 onDblClick : function(e,el)
9722 var cell = Roo.get(el);
9724 if(!cell || (!this.cellSelection && !this.rowSelection)){
9728 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9729 cell = cell.findParent('td', false, true);
9732 if(!cell || typeof(cell) == 'undefined'){
9736 var row = cell.findParent('tr', false, true);
9738 if(!row || typeof(row) == 'undefined'){
9742 var cellIndex = cell.dom.cellIndex;
9743 var rowIndex = this.getRowIndex(row);
9745 if(this.cellSelection){
9746 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9749 if(this.rowSelection){
9750 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9753 findRowIndex : function(el)
9755 var cell = Roo.get(el);
9759 var row = cell.findParent('tr', false, true);
9761 if(!row || typeof(row) == 'undefined'){
9764 return this.getRowIndex(row);
9766 sort : function(e,el)
9768 var col = Roo.get(el);
9770 if(!col.hasClass('sortable')){
9774 var sort = col.attr('sort');
9777 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9781 this.store.sortInfo = {field : sort, direction : dir};
9784 Roo.log("calling footer first");
9785 this.footer.onClick('first');
9788 this.store.load({ params : { start : 0 } });
9792 renderHeader : function()
9800 this.totalWidth = 0;
9802 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9804 var config = cm.config[i];
9808 cls : 'x-hcol-' + i,
9811 html: cm.getColumnHeader(i)
9814 var tooltip = cm.getColumnTooltip(i);
9816 c.tooltip = tooltip;
9822 if(typeof(config.sortable) != 'undefined' && config.sortable){
9823 c.cls += ' sortable';
9824 c.html = '<i class="fa"></i>' + c.html;
9827 // could use BS4 hidden-..-down
9829 if(typeof(config.lgHeader) != 'undefined'){
9830 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9833 if(typeof(config.mdHeader) != 'undefined'){
9834 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9837 if(typeof(config.smHeader) != 'undefined'){
9838 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9841 if(typeof(config.xsHeader) != 'undefined'){
9842 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9849 if(typeof(config.tooltip) != 'undefined'){
9850 c.tooltip = config.tooltip;
9853 if(typeof(config.colspan) != 'undefined'){
9854 c.colspan = config.colspan;
9857 // hidden is handled by CSS now
9859 if(typeof(config.dataIndex) != 'undefined'){
9860 c.sort = config.dataIndex;
9865 if(typeof(config.align) != 'undefined' && config.align.length){
9866 c.style += ' text-align:' + config.align + ';';
9869 /* width is done in CSS
9870 *if(typeof(config.width) != 'undefined'){
9871 c.style += ' width:' + config.width + 'px;';
9872 this.totalWidth += config.width;
9874 this.totalWidth += 100; // assume minimum of 100 per column?
9878 if(typeof(config.cls) != 'undefined'){
9879 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9881 // this is the bit that doesnt reall work at all...
9883 if (this.responsive) {
9886 ['xs','sm','md','lg'].map(function(size){
9888 if(typeof(config[size]) == 'undefined'){
9892 if (!config[size]) { // 0 = hidden
9893 // BS 4 '0' is treated as hide that column and below.
9894 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9898 c.cls += ' col-' + size + '-' + config[size] + (
9899 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9907 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9918 renderBody : function()
9928 colspan : this.cm.getColumnCount()
9938 renderFooter : function()
9948 colspan : this.cm.getColumnCount()
9962 // Roo.log('ds onload');
9967 var ds = this.store;
9969 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9970 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9971 if (_this.store.sortInfo) {
9973 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9974 e.select('i', true).addClass(['fa-arrow-up']);
9977 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9978 e.select('i', true).addClass(['fa-arrow-down']);
9983 var tbody = this.bodyEl;
9985 if(ds.getCount() > 0){
9986 ds.data.each(function(d,rowIndex){
9987 var row = this.renderRow(cm, ds, rowIndex);
9989 tbody.createChild(row);
9993 if(row.cellObjects.length){
9994 Roo.each(row.cellObjects, function(r){
9995 _this.renderCellObject(r);
10002 var tfoot = this.el.select('tfoot', true).first();
10004 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10006 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10008 var total = this.ds.getTotalCount();
10010 if(this.footer.pageSize < total){
10011 this.mainFoot.show();
10015 Roo.each(this.el.select('tbody td', true).elements, function(e){
10016 e.on('mouseover', _this.onMouseover, _this);
10019 Roo.each(this.el.select('tbody td', true).elements, function(e){
10020 e.on('mouseout', _this.onMouseout, _this);
10022 this.fireEvent('rowsrendered', this);
10026 this.initCSS(); /// resize cols
10032 onUpdate : function(ds,record)
10034 this.refreshRow(record);
10038 onRemove : function(ds, record, index, isUpdate){
10039 if(isUpdate !== true){
10040 this.fireEvent("beforerowremoved", this, index, record);
10042 var bt = this.bodyEl.dom;
10044 var rows = this.el.select('tbody > tr', true).elements;
10046 if(typeof(rows[index]) != 'undefined'){
10047 bt.removeChild(rows[index].dom);
10050 // if(bt.rows[index]){
10051 // bt.removeChild(bt.rows[index]);
10054 if(isUpdate !== true){
10055 //this.stripeRows(index);
10056 //this.syncRowHeights(index, index);
10058 this.fireEvent("rowremoved", this, index, record);
10062 onAdd : function(ds, records, rowIndex)
10064 //Roo.log('on Add called');
10065 // - note this does not handle multiple adding very well..
10066 var bt = this.bodyEl.dom;
10067 for (var i =0 ; i < records.length;i++) {
10068 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10069 //Roo.log(records[i]);
10070 //Roo.log(this.store.getAt(rowIndex+i));
10071 this.insertRow(this.store, rowIndex + i, false);
10078 refreshRow : function(record){
10079 var ds = this.store, index;
10080 if(typeof record == 'number'){
10082 record = ds.getAt(index);
10084 index = ds.indexOf(record);
10086 return; // should not happen - but seems to
10089 this.insertRow(ds, index, true);
10091 this.onRemove(ds, record, index+1, true);
10093 //this.syncRowHeights(index, index);
10095 this.fireEvent("rowupdated", this, index, record);
10097 // private - called by RowSelection
10098 onRowSelect : function(rowIndex){
10099 var row = this.getRowDom(rowIndex);
10100 row.addClass(['bg-info','info']);
10102 // private - called by RowSelection
10103 onRowDeselect : function(rowIndex)
10105 if (rowIndex < 0) {
10108 var row = this.getRowDom(rowIndex);
10109 row.removeClass(['bg-info','info']);
10112 * Focuses the specified row.
10113 * @param {Number} row The row index
10115 focusRow : function(row)
10117 //Roo.log('GridView.focusRow');
10118 var x = this.bodyEl.dom.scrollLeft;
10119 this.focusCell(row, 0, false);
10120 this.bodyEl.dom.scrollLeft = x;
10124 * Focuses the specified cell.
10125 * @param {Number} row The row index
10126 * @param {Number} col The column index
10127 * @param {Boolean} hscroll false to disable horizontal scrolling
10129 focusCell : function(row, col, hscroll)
10131 //Roo.log('GridView.focusCell');
10132 var el = this.ensureVisible(row, col, hscroll);
10133 // not sure what focusEL achives = it's a <a> pos relative
10134 //this.focusEl.alignTo(el, "tl-tl");
10136 // this.focusEl.focus();
10138 // this.focusEl.focus.defer(1, this.focusEl);
10143 * Scrolls the specified cell into view
10144 * @param {Number} row The row index
10145 * @param {Number} col The column index
10146 * @param {Boolean} hscroll false to disable horizontal scrolling
10148 ensureVisible : function(row, col, hscroll)
10150 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10151 //return null; //disable for testing.
10152 if(typeof row != "number"){
10153 row = row.rowIndex;
10155 if(row < 0 && row >= this.ds.getCount()){
10158 col = (col !== undefined ? col : 0);
10160 while(cm.isHidden(col)){
10164 var el = this.getCellDom(row, col);
10168 var c = this.bodyEl.dom;
10170 var ctop = parseInt(el.offsetTop, 10);
10171 var cleft = parseInt(el.offsetLeft, 10);
10172 var cbot = ctop + el.offsetHeight;
10173 var cright = cleft + el.offsetWidth;
10175 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10176 var ch = 0; //?? header is not withing the area?
10177 var stop = parseInt(c.scrollTop, 10);
10178 var sleft = parseInt(c.scrollLeft, 10);
10179 var sbot = stop + ch;
10180 var sright = sleft + c.clientWidth;
10182 Roo.log('GridView.ensureVisible:' +
10184 ' c.clientHeight:' + c.clientHeight +
10185 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10193 c.scrollTop = ctop;
10194 //Roo.log("set scrolltop to ctop DISABLE?");
10195 }else if(cbot > sbot){
10196 //Roo.log("set scrolltop to cbot-ch");
10197 c.scrollTop = cbot-ch;
10200 if(hscroll !== false){
10202 c.scrollLeft = cleft;
10203 }else if(cright > sright){
10204 c.scrollLeft = cright-c.clientWidth;
10212 insertRow : function(dm, rowIndex, isUpdate){
10215 this.fireEvent("beforerowsinserted", this, rowIndex);
10217 //var s = this.getScrollState();
10218 var row = this.renderRow(this.cm, this.store, rowIndex);
10219 // insert before rowIndex..
10220 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10224 if(row.cellObjects.length){
10225 Roo.each(row.cellObjects, function(r){
10226 _this.renderCellObject(r);
10231 this.fireEvent("rowsinserted", this, rowIndex);
10232 //this.syncRowHeights(firstRow, lastRow);
10233 //this.stripeRows(firstRow);
10240 getRowDom : function(rowIndex)
10242 var rows = this.el.select('tbody > tr', true).elements;
10244 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10247 getCellDom : function(rowIndex, colIndex)
10249 var row = this.getRowDom(rowIndex);
10250 if (row === false) {
10253 var cols = row.select('td', true).elements;
10254 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10258 // returns the object tree for a tr..
10261 renderRow : function(cm, ds, rowIndex)
10263 var d = ds.getAt(rowIndex);
10267 cls : 'x-row-' + rowIndex,
10271 var cellObjects = [];
10273 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10274 var config = cm.config[i];
10276 var renderer = cm.getRenderer(i);
10280 if(typeof(renderer) !== 'undefined'){
10281 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10283 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10284 // and are rendered into the cells after the row is rendered - using the id for the element.
10286 if(typeof(value) === 'object'){
10296 rowIndex : rowIndex,
10301 this.fireEvent('rowclass', this, rowcfg);
10305 // this might end up displaying HTML?
10306 // this is too messy... - better to only do it on columsn you know are going to be too long
10307 //tooltip : (typeof(value) === 'object') ? '' : value,
10308 cls : rowcfg.rowClass + ' x-col-' + i,
10310 html: (typeof(value) === 'object') ? '' : value
10317 if(typeof(config.colspan) != 'undefined'){
10318 td.colspan = config.colspan;
10323 if(typeof(config.align) != 'undefined' && config.align.length){
10324 td.style += ' text-align:' + config.align + ';';
10326 if(typeof(config.valign) != 'undefined' && config.valign.length){
10327 td.style += ' vertical-align:' + config.valign + ';';
10330 if(typeof(config.width) != 'undefined'){
10331 td.style += ' width:' + config.width + 'px;';
10335 if(typeof(config.cursor) != 'undefined'){
10336 td.style += ' cursor:' + config.cursor + ';';
10339 if(typeof(config.cls) != 'undefined'){
10340 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10342 if (this.responsive) {
10343 ['xs','sm','md','lg'].map(function(size){
10345 if(typeof(config[size]) == 'undefined'){
10351 if (!config[size]) { // 0 = hidden
10352 // BS 4 '0' is treated as hide that column and below.
10353 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10357 td.cls += ' col-' + size + '-' + config[size] + (
10358 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10368 row.cellObjects = cellObjects;
10376 onBeforeLoad : function()
10385 this.el.select('tbody', true).first().dom.innerHTML = '';
10388 * Show or hide a row.
10389 * @param {Number} rowIndex to show or hide
10390 * @param {Boolean} state hide
10392 setRowVisibility : function(rowIndex, state)
10394 var bt = this.bodyEl.dom;
10396 var rows = this.el.select('tbody > tr', true).elements;
10398 if(typeof(rows[rowIndex]) == 'undefined'){
10401 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10406 getSelectionModel : function(){
10407 if(!this.selModel){
10408 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10410 return this.selModel;
10413 * Render the Roo.bootstrap object from renderder
10415 renderCellObject : function(r)
10419 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10421 var t = r.cfg.render(r.container);
10424 Roo.each(r.cfg.cn, function(c){
10426 container: t.getChildContainer(),
10429 _this.renderCellObject(child);
10434 * get the Row Index from a dom element.
10435 * @param {Roo.Element} row The row to look for
10436 * @returns {Number} the row
10438 getRowIndex : function(row)
10442 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10453 * get the header TH element for columnIndex
10454 * @param {Number} columnIndex
10455 * @returns {Roo.Element}
10457 getHeaderIndex: function(colIndex)
10459 var cols = this.headEl.select('th', true).elements;
10460 return cols[colIndex];
10463 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10464 * @param {domElement} cell to look for
10465 * @returns {Number} the column
10467 getCellIndex : function(cell)
10469 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10471 return parseInt(id[1], 10);
10476 * Returns the grid's underlying element = used by panel.Grid
10477 * @return {Element} The element
10479 getGridEl : function(){
10483 * Forces a resize - used by panel.Grid
10484 * @return {Element} The element
10486 autoSize : function()
10488 //var ctr = Roo.get(this.container.dom.parentElement);
10489 var ctr = Roo.get(this.el.dom);
10491 var thd = this.getGridEl().select('thead',true).first();
10492 var tbd = this.getGridEl().select('tbody', true).first();
10493 var tfd = this.getGridEl().select('tfoot', true).first();
10495 var cw = ctr.getWidth();
10496 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10500 tbd.setWidth(ctr.getWidth());
10501 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10502 // this needs fixing for various usage - currently only hydra job advers I think..
10504 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10506 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10509 cw = Math.max(cw, this.totalWidth);
10510 this.getGridEl().select('tbody tr',true).setWidth(cw);
10513 // resize 'expandable coloumn?
10515 return; // we doe not have a view in this design..
10518 onBodyScroll: function()
10520 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10522 this.headEl.setStyle({
10523 'position' : 'relative',
10524 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10530 var scrollHeight = this.bodyEl.dom.scrollHeight;
10532 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10534 var height = this.bodyEl.getHeight();
10536 if(scrollHeight - height == scrollTop) {
10538 var total = this.ds.getTotalCount();
10540 if(this.footer.cursor + this.footer.pageSize < total){
10542 this.footer.ds.load({
10544 start : this.footer.cursor + this.footer.pageSize,
10545 limit : this.footer.pageSize
10554 onColumnSplitterMoved : function(i, diff)
10556 this.userResized = true;
10558 var cm = this.colModel;
10560 var w = this.getHeaderIndex(i).getWidth() + diff;
10563 cm.setColumnWidth(i, w, true);
10565 //var cid = cm.getColumnId(i); << not used in this version?
10566 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10568 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10569 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10570 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10572 //this.updateSplitters();
10573 //this.layout(); << ??
10574 this.fireEvent("columnresize", i, w);
10576 onHeaderChange : function()
10578 var header = this.renderHeader();
10579 var table = this.el.select('table', true).first();
10581 this.headEl.remove();
10582 this.headEl = table.createChild(header, this.bodyEl, false);
10584 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10585 e.on('click', this.sort, this);
10588 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10589 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10594 onHiddenChange : function(colModel, colIndex, hidden)
10597 this.cm.setHidden()
10598 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10599 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10601 this.CSS.updateRule(thSelector, "display", "");
10602 this.CSS.updateRule(tdSelector, "display", "");
10605 this.CSS.updateRule(thSelector, "display", "none");
10606 this.CSS.updateRule(tdSelector, "display", "none");
10609 // onload calls initCSS()
10610 this.onHeaderChange();
10614 setColumnWidth: function(col_index, width)
10616 // width = "md-2 xs-2..."
10617 if(!this.colModel.config[col_index]) {
10621 var w = width.split(" ");
10623 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10625 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10628 for(var j = 0; j < w.length; j++) {
10634 var size_cls = w[j].split("-");
10636 if(!Number.isInteger(size_cls[1] * 1)) {
10640 if(!this.colModel.config[col_index][size_cls[0]]) {
10644 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10648 h_row[0].classList.replace(
10649 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10650 "col-"+size_cls[0]+"-"+size_cls[1]
10653 for(var i = 0; i < rows.length; i++) {
10655 var size_cls = w[j].split("-");
10657 if(!Number.isInteger(size_cls[1] * 1)) {
10661 if(!this.colModel.config[col_index][size_cls[0]]) {
10665 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10669 rows[i].classList.replace(
10670 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10671 "col-"+size_cls[0]+"-"+size_cls[1]
10675 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10680 // currently only used to find the split on drag..
10681 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10686 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10687 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10696 * @class Roo.bootstrap.TableCell
10697 * @extends Roo.bootstrap.Component
10698 * @children Roo.bootstrap.Component
10699 * @parent Roo.bootstrap.TableRow
10700 * Bootstrap TableCell class
10702 * @cfg {String} html cell contain text
10703 * @cfg {String} cls cell class
10704 * @cfg {String} tag cell tag (td|th) default td
10705 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10706 * @cfg {String} align Aligns the content in a cell
10707 * @cfg {String} axis Categorizes cells
10708 * @cfg {String} bgcolor Specifies the background color of a cell
10709 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10710 * @cfg {Number} colspan Specifies the number of columns a cell should span
10711 * @cfg {String} headers Specifies one or more header cells a cell is related to
10712 * @cfg {Number} height Sets the height of a cell
10713 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10714 * @cfg {Number} rowspan Sets the number of rows a cell should span
10715 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10716 * @cfg {String} valign Vertical aligns the content in a cell
10717 * @cfg {Number} width Specifies the width of a cell
10720 * Create a new TableCell
10721 * @param {Object} config The config object
10724 Roo.bootstrap.TableCell = function(config){
10725 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10728 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10748 getAutoCreate : function(){
10749 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10756 cfg.tag = this.tag;
10769 cfg.align=this.align
10774 if (this.bgcolor) {
10775 cfg.bgcolor=this.bgcolor
10777 if (this.charoff) {
10778 cfg.charoff=this.charoff
10780 if (this.colspan) {
10781 cfg.colspan=this.colspan
10783 if (this.headers) {
10784 cfg.headers=this.headers
10787 cfg.height=this.height
10790 cfg.nowrap=this.nowrap
10792 if (this.rowspan) {
10793 cfg.rowspan=this.rowspan
10796 cfg.scope=this.scope
10799 cfg.valign=this.valign
10802 cfg.width=this.width
10821 * @class Roo.bootstrap.TableRow
10822 * @extends Roo.bootstrap.Component
10823 * @children Roo.bootstrap.TableCell
10824 * @parent Roo.bootstrap.TableBody
10825 * Bootstrap TableRow class
10826 * @cfg {String} cls row class
10827 * @cfg {String} align Aligns the content in a table row
10828 * @cfg {String} bgcolor Specifies a background color for a table row
10829 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10830 * @cfg {String} valign Vertical aligns the content in a table row
10833 * Create a new TableRow
10834 * @param {Object} config The config object
10837 Roo.bootstrap.TableRow = function(config){
10838 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10841 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10849 getAutoCreate : function(){
10850 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10857 cfg.cls = this.cls;
10860 cfg.align = this.align;
10863 cfg.bgcolor = this.bgcolor;
10866 cfg.charoff = this.charoff;
10869 cfg.valign = this.valign;
10887 * @class Roo.bootstrap.TableBody
10888 * @extends Roo.bootstrap.Component
10889 * @children Roo.bootstrap.TableRow
10890 * @parent Roo.bootstrap.Table
10891 * Bootstrap TableBody class
10892 * @cfg {String} cls element class
10893 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10894 * @cfg {String} align Aligns the content inside the element
10895 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10896 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10899 * Create a new TableBody
10900 * @param {Object} config The config object
10903 Roo.bootstrap.TableBody = function(config){
10904 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10907 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10915 getAutoCreate : function(){
10916 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10926 cfg.tag = this.tag;
10930 cfg.align = this.align;
10933 cfg.charoff = this.charoff;
10936 cfg.valign = this.valign;
10943 // initEvents : function()
10946 // if(!this.store){
10950 // this.store = Roo.factory(this.store, Roo.data);
10951 // this.store.on('load', this.onLoad, this);
10953 // this.store.load();
10957 // onLoad: function ()
10959 // this.fireEvent('load', this);
10969 * Ext JS Library 1.1.1
10970 * Copyright(c) 2006-2007, Ext JS, LLC.
10972 * Originally Released Under LGPL - original licence link has changed is not relivant.
10975 * <script type="text/javascript">
10978 // as we use this in bootstrap.
10979 Roo.namespace('Roo.form');
10981 * @class Roo.form.Action
10982 * Internal Class used to handle form actions
10984 * @param {Roo.form.BasicForm} el The form element or its id
10985 * @param {Object} config Configuration options
10990 // define the action interface
10991 Roo.form.Action = function(form, options){
10993 this.options = options || {};
10996 * Client Validation Failed
10999 Roo.form.Action.CLIENT_INVALID = 'client';
11001 * Server Validation Failed
11004 Roo.form.Action.SERVER_INVALID = 'server';
11006 * Connect to Server Failed
11009 Roo.form.Action.CONNECT_FAILURE = 'connect';
11011 * Reading Data from Server Failed
11014 Roo.form.Action.LOAD_FAILURE = 'load';
11016 Roo.form.Action.prototype = {
11018 failureType : undefined,
11019 response : undefined,
11020 result : undefined,
11022 // interface method
11023 run : function(options){
11027 // interface method
11028 success : function(response){
11032 // interface method
11033 handleResponse : function(response){
11037 // default connection failure
11038 failure : function(response){
11040 this.response = response;
11041 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11042 this.form.afterAction(this, false);
11045 processResponse : function(response){
11046 this.response = response;
11047 if(!response.responseText){
11050 this.result = this.handleResponse(response);
11051 return this.result;
11054 // utility functions used internally
11055 getUrl : function(appendParams){
11056 var url = this.options.url || this.form.url || this.form.el.dom.action;
11058 var p = this.getParams();
11060 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11066 getMethod : function(){
11067 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11070 getParams : function(){
11071 var bp = this.form.baseParams;
11072 var p = this.options.params;
11074 if(typeof p == "object"){
11075 p = Roo.urlEncode(Roo.applyIf(p, bp));
11076 }else if(typeof p == 'string' && bp){
11077 p += '&' + Roo.urlEncode(bp);
11080 p = Roo.urlEncode(bp);
11085 createCallback : function(){
11087 success: this.success,
11088 failure: this.failure,
11090 timeout: (this.form.timeout*1000),
11091 upload: this.form.fileUpload ? this.success : undefined
11096 Roo.form.Action.Submit = function(form, options){
11097 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11100 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11103 haveProgress : false,
11104 uploadComplete : false,
11106 // uploadProgress indicator.
11107 uploadProgress : function()
11109 if (!this.form.progressUrl) {
11113 if (!this.haveProgress) {
11114 Roo.MessageBox.progress("Uploading", "Uploading");
11116 if (this.uploadComplete) {
11117 Roo.MessageBox.hide();
11121 this.haveProgress = true;
11123 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11125 var c = new Roo.data.Connection();
11127 url : this.form.progressUrl,
11132 success : function(req){
11133 //console.log(data);
11137 rdata = Roo.decode(req.responseText)
11139 Roo.log("Invalid data from server..");
11143 if (!rdata || !rdata.success) {
11145 Roo.MessageBox.alert(Roo.encode(rdata));
11148 var data = rdata.data;
11150 if (this.uploadComplete) {
11151 Roo.MessageBox.hide();
11156 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11157 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11160 this.uploadProgress.defer(2000,this);
11163 failure: function(data) {
11164 Roo.log('progress url failed ');
11175 // run get Values on the form, so it syncs any secondary forms.
11176 this.form.getValues();
11178 var o = this.options;
11179 var method = this.getMethod();
11180 var isPost = method == 'POST';
11181 if(o.clientValidation === false || this.form.isValid()){
11183 if (this.form.progressUrl) {
11184 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11185 (new Date() * 1) + '' + Math.random());
11190 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11191 form:this.form.el.dom,
11192 url:this.getUrl(!isPost),
11194 params:isPost ? this.getParams() : null,
11195 isUpload: this.form.fileUpload,
11196 formData : this.form.formData
11199 this.uploadProgress();
11201 }else if (o.clientValidation !== false){ // client validation failed
11202 this.failureType = Roo.form.Action.CLIENT_INVALID;
11203 this.form.afterAction(this, false);
11207 success : function(response)
11209 this.uploadComplete= true;
11210 if (this.haveProgress) {
11211 Roo.MessageBox.hide();
11215 var result = this.processResponse(response);
11216 if(result === true || result.success){
11217 this.form.afterAction(this, true);
11221 this.form.markInvalid(result.errors);
11222 this.failureType = Roo.form.Action.SERVER_INVALID;
11224 this.form.afterAction(this, false);
11226 failure : function(response)
11228 this.uploadComplete= true;
11229 if (this.haveProgress) {
11230 Roo.MessageBox.hide();
11233 this.response = response;
11234 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11235 this.form.afterAction(this, false);
11238 handleResponse : function(response){
11239 if(this.form.errorReader){
11240 var rs = this.form.errorReader.read(response);
11243 for(var i = 0, len = rs.records.length; i < len; i++) {
11244 var r = rs.records[i];
11245 errors[i] = r.data;
11248 if(errors.length < 1){
11252 success : rs.success,
11258 ret = Roo.decode(response.responseText);
11262 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11272 Roo.form.Action.Load = function(form, options){
11273 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11274 this.reader = this.form.reader;
11277 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11282 Roo.Ajax.request(Roo.apply(
11283 this.createCallback(), {
11284 method:this.getMethod(),
11285 url:this.getUrl(false),
11286 params:this.getParams()
11290 success : function(response){
11292 var result = this.processResponse(response);
11293 if(result === true || !result.success || !result.data){
11294 this.failureType = Roo.form.Action.LOAD_FAILURE;
11295 this.form.afterAction(this, false);
11298 this.form.clearInvalid();
11299 this.form.setValues(result.data);
11300 this.form.afterAction(this, true);
11303 handleResponse : function(response){
11304 if(this.form.reader){
11305 var rs = this.form.reader.read(response);
11306 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11308 success : rs.success,
11312 return Roo.decode(response.responseText);
11316 Roo.form.Action.ACTION_TYPES = {
11317 'load' : Roo.form.Action.Load,
11318 'submit' : Roo.form.Action.Submit
11327 * @class Roo.bootstrap.form.Form
11328 * @extends Roo.bootstrap.Component
11329 * @children Roo.bootstrap.Component
11330 * Bootstrap Form class
11331 * @cfg {String} method GET | POST (default POST)
11332 * @cfg {String} labelAlign top | left (default top)
11333 * @cfg {String} align left | right - for navbars
11334 * @cfg {Boolean} loadMask load mask when submit (default true)
11338 * Create a new Form
11339 * @param {Object} config The config object
11343 Roo.bootstrap.form.Form = function(config){
11345 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11347 Roo.bootstrap.form.Form.popover.apply();
11351 * @event clientvalidation
11352 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11353 * @param {Form} this
11354 * @param {Boolean} valid true if the form has passed client-side validation
11356 clientvalidation: true,
11358 * @event beforeaction
11359 * Fires before any action is performed. Return false to cancel the action.
11360 * @param {Form} this
11361 * @param {Action} action The action to be performed
11363 beforeaction: true,
11365 * @event actionfailed
11366 * Fires when an action fails.
11367 * @param {Form} this
11368 * @param {Action} action The action that failed
11370 actionfailed : true,
11372 * @event actioncomplete
11373 * Fires when an action is completed.
11374 * @param {Form} this
11375 * @param {Action} action The action that completed
11377 actioncomplete : true
11381 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11384 * @cfg {String} method
11385 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11389 * @cfg {String} url
11390 * The URL to use for form actions if one isn't supplied in the action options.
11393 * @cfg {Boolean} fileUpload
11394 * Set to true if this form is a file upload.
11398 * @cfg {Object} baseParams
11399 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11403 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11407 * @cfg {Sting} align (left|right) for navbar forms
11412 activeAction : null,
11415 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11416 * element by passing it or its id or mask the form itself by passing in true.
11419 waitMsgTarget : false,
11424 * @cfg {Boolean} errorMask (true|false) default false
11429 * @cfg {Number} maskOffset Default 100
11434 * @cfg {Boolean} maskBody
11438 getAutoCreate : function(){
11442 method : this.method || 'POST',
11443 id : this.id || Roo.id(),
11446 if (this.parent().xtype.match(/^Nav/)) {
11447 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11451 if (this.labelAlign == 'left' ) {
11452 cfg.cls += ' form-horizontal';
11458 initEvents : function()
11460 this.el.on('submit', this.onSubmit, this);
11461 // this was added as random key presses on the form where triggering form submit.
11462 this.el.on('keypress', function(e) {
11463 if (e.getCharCode() != 13) {
11466 // we might need to allow it for textareas.. and some other items.
11467 // check e.getTarget().
11469 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11473 Roo.log("keypress blocked");
11475 e.preventDefault();
11481 onSubmit : function(e){
11486 * Returns true if client-side validation on the form is successful.
11489 isValid : function(){
11490 var items = this.getItems();
11492 var target = false;
11494 items.each(function(f){
11500 Roo.log('invalid field: ' + f.name);
11504 if(!target && f.el.isVisible(true)){
11510 if(this.errorMask && !valid){
11511 Roo.bootstrap.form.Form.popover.mask(this, target);
11518 * Returns true if any fields in this form have changed since their original load.
11521 isDirty : function(){
11523 var items = this.getItems();
11524 items.each(function(f){
11534 * Performs a predefined action (submit or load) or custom actions you define on this form.
11535 * @param {String} actionName The name of the action type
11536 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11537 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11538 * accept other config options):
11540 Property Type Description
11541 ---------------- --------------- ----------------------------------------------------------------------------------
11542 url String The url for the action (defaults to the form's url)
11543 method String The form method to use (defaults to the form's method, or POST if not defined)
11544 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11545 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11546 validate the form on the client (defaults to false)
11548 * @return {BasicForm} this
11550 doAction : function(action, options){
11551 if(typeof action == 'string'){
11552 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11554 if(this.fireEvent('beforeaction', this, action) !== false){
11555 this.beforeAction(action);
11556 action.run.defer(100, action);
11562 beforeAction : function(action){
11563 var o = action.options;
11568 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11570 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11573 // not really supported yet.. ??
11575 //if(this.waitMsgTarget === true){
11576 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11577 //}else if(this.waitMsgTarget){
11578 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11579 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11581 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11587 afterAction : function(action, success){
11588 this.activeAction = null;
11589 var o = action.options;
11594 Roo.get(document.body).unmask();
11600 //if(this.waitMsgTarget === true){
11601 // this.el.unmask();
11602 //}else if(this.waitMsgTarget){
11603 // this.waitMsgTarget.unmask();
11605 // Roo.MessageBox.updateProgress(1);
11606 // Roo.MessageBox.hide();
11613 Roo.callback(o.success, o.scope, [this, action]);
11614 this.fireEvent('actioncomplete', this, action);
11618 // failure condition..
11619 // we have a scenario where updates need confirming.
11620 // eg. if a locking scenario exists..
11621 // we look for { errors : { needs_confirm : true }} in the response.
11623 (typeof(action.result) != 'undefined') &&
11624 (typeof(action.result.errors) != 'undefined') &&
11625 (typeof(action.result.errors.needs_confirm) != 'undefined')
11628 Roo.log("not supported yet");
11631 Roo.MessageBox.confirm(
11632 "Change requires confirmation",
11633 action.result.errorMsg,
11638 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11648 Roo.callback(o.failure, o.scope, [this, action]);
11649 // show an error message if no failed handler is set..
11650 if (!this.hasListener('actionfailed')) {
11651 Roo.log("need to add dialog support");
11653 Roo.MessageBox.alert("Error",
11654 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11655 action.result.errorMsg :
11656 "Saving Failed, please check your entries or try again"
11661 this.fireEvent('actionfailed', this, action);
11666 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11667 * @param {String} id The value to search for
11670 findField : function(id){
11671 var items = this.getItems();
11672 var field = items.get(id);
11674 items.each(function(f){
11675 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11682 return field || null;
11685 * Mark fields in this form invalid in bulk.
11686 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11687 * @return {BasicForm} this
11689 markInvalid : function(errors){
11690 if(errors instanceof Array){
11691 for(var i = 0, len = errors.length; i < len; i++){
11692 var fieldError = errors[i];
11693 var f = this.findField(fieldError.id);
11695 f.markInvalid(fieldError.msg);
11701 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11702 field.markInvalid(errors[id]);
11706 //Roo.each(this.childForms || [], function (f) {
11707 // f.markInvalid(errors);
11714 * Set values for fields in this form in bulk.
11715 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11716 * @return {BasicForm} this
11718 setValues : function(values){
11719 if(values instanceof Array){ // array of objects
11720 for(var i = 0, len = values.length; i < len; i++){
11722 var f = this.findField(v.id);
11724 f.setValue(v.value);
11725 if(this.trackResetOnLoad){
11726 f.originalValue = f.getValue();
11730 }else{ // object hash
11733 if(typeof values[id] != 'function' && (field = this.findField(id))){
11735 if (field.setFromData &&
11736 field.valueField &&
11737 field.displayField &&
11738 // combos' with local stores can
11739 // be queried via setValue()
11740 // to set their value..
11741 (field.store && !field.store.isLocal)
11745 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11746 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11747 field.setFromData(sd);
11749 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11751 field.setFromData(values);
11754 field.setValue(values[id]);
11758 if(this.trackResetOnLoad){
11759 field.originalValue = field.getValue();
11765 //Roo.each(this.childForms || [], function (f) {
11766 // f.setValues(values);
11773 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11774 * they are returned as an array.
11775 * @param {Boolean} asString
11778 getValues : function(asString){
11779 //if (this.childForms) {
11780 // copy values from the child forms
11781 // Roo.each(this.childForms, function (f) {
11782 // this.setValues(f.getValues());
11788 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11789 if(asString === true){
11792 return Roo.urlDecode(fs);
11796 * Returns the fields in this form as an object with key/value pairs.
11797 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11800 getFieldValues : function(with_hidden)
11802 var items = this.getItems();
11804 items.each(function(f){
11806 if (!f.getName()) {
11810 var v = f.getValue();
11812 if (f.inputType =='radio') {
11813 if (typeof(ret[f.getName()]) == 'undefined') {
11814 ret[f.getName()] = ''; // empty..
11817 if (!f.el.dom.checked) {
11821 v = f.el.dom.value;
11825 if(f.xtype == 'MoneyField'){
11826 ret[f.currencyName] = f.getCurrency();
11829 // not sure if this supported any more..
11830 if ((typeof(v) == 'object') && f.getRawValue) {
11831 v = f.getRawValue() ; // dates..
11833 // combo boxes where name != hiddenName...
11834 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11835 ret[f.name] = f.getRawValue();
11837 ret[f.getName()] = v;
11844 * Clears all invalid messages in this form.
11845 * @return {BasicForm} this
11847 clearInvalid : function(){
11848 var items = this.getItems();
11850 items.each(function(f){
11858 * Resets this form.
11859 * @return {BasicForm} this
11861 reset : function(){
11862 var items = this.getItems();
11863 items.each(function(f){
11867 Roo.each(this.childForms || [], function (f) {
11875 getItems : function()
11877 var r=new Roo.util.MixedCollection(false, function(o){
11878 return o.id || (o.id = Roo.id());
11880 var iter = function(el) {
11887 Roo.each(el.items,function(e) {
11896 hideFields : function(items)
11898 Roo.each(items, function(i){
11900 var f = this.findField(i);
11911 showFields : function(items)
11913 Roo.each(items, function(i){
11915 var f = this.findField(i);
11928 Roo.apply(Roo.bootstrap.form.Form, {
11944 intervalID : false,
11950 if(this.isApplied){
11955 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11956 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11957 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11958 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11961 this.maskEl.top.enableDisplayMode("block");
11962 this.maskEl.left.enableDisplayMode("block");
11963 this.maskEl.bottom.enableDisplayMode("block");
11964 this.maskEl.right.enableDisplayMode("block");
11966 this.toolTip = new Roo.bootstrap.Tooltip({
11967 cls : 'roo-form-error-popover',
11969 'left' : ['r-l', [-2,0], 'right'],
11970 'right' : ['l-r', [2,0], 'left'],
11971 'bottom' : ['tl-bl', [0,2], 'top'],
11972 'top' : [ 'bl-tl', [0,-2], 'bottom']
11976 this.toolTip.render(Roo.get(document.body));
11978 this.toolTip.el.enableDisplayMode("block");
11980 Roo.get(document.body).on('click', function(){
11984 Roo.get(document.body).on('touchstart', function(){
11988 this.isApplied = true
11991 mask : function(form, target)
11995 this.target = target;
11997 if(!this.form.errorMask || !target.el){
12001 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12003 Roo.log(scrollable);
12005 var ot = this.target.el.calcOffsetsTo(scrollable);
12007 var scrollTo = ot[1] - this.form.maskOffset;
12009 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12011 scrollable.scrollTo('top', scrollTo);
12013 var box = this.target.el.getBox();
12015 var zIndex = Roo.bootstrap.Modal.zIndex++;
12018 this.maskEl.top.setStyle('position', 'absolute');
12019 this.maskEl.top.setStyle('z-index', zIndex);
12020 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12021 this.maskEl.top.setLeft(0);
12022 this.maskEl.top.setTop(0);
12023 this.maskEl.top.show();
12025 this.maskEl.left.setStyle('position', 'absolute');
12026 this.maskEl.left.setStyle('z-index', zIndex);
12027 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12028 this.maskEl.left.setLeft(0);
12029 this.maskEl.left.setTop(box.y - this.padding);
12030 this.maskEl.left.show();
12032 this.maskEl.bottom.setStyle('position', 'absolute');
12033 this.maskEl.bottom.setStyle('z-index', zIndex);
12034 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12035 this.maskEl.bottom.setLeft(0);
12036 this.maskEl.bottom.setTop(box.bottom + this.padding);
12037 this.maskEl.bottom.show();
12039 this.maskEl.right.setStyle('position', 'absolute');
12040 this.maskEl.right.setStyle('z-index', zIndex);
12041 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12042 this.maskEl.right.setLeft(box.right + this.padding);
12043 this.maskEl.right.setTop(box.y - this.padding);
12044 this.maskEl.right.show();
12046 this.toolTip.bindEl = this.target.el;
12048 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12050 var tip = this.target.blankText;
12052 if(this.target.getValue() !== '' ) {
12054 if (this.target.invalidText.length) {
12055 tip = this.target.invalidText;
12056 } else if (this.target.regexText.length){
12057 tip = this.target.regexText;
12061 this.toolTip.show(tip);
12063 this.intervalID = window.setInterval(function() {
12064 Roo.bootstrap.form.Form.popover.unmask();
12067 window.onwheel = function(){ return false;};
12069 (function(){ this.isMasked = true; }).defer(500, this);
12073 unmask : function()
12075 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12079 this.maskEl.top.setStyle('position', 'absolute');
12080 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12081 this.maskEl.top.hide();
12083 this.maskEl.left.setStyle('position', 'absolute');
12084 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12085 this.maskEl.left.hide();
12087 this.maskEl.bottom.setStyle('position', 'absolute');
12088 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12089 this.maskEl.bottom.hide();
12091 this.maskEl.right.setStyle('position', 'absolute');
12092 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12093 this.maskEl.right.hide();
12095 this.toolTip.hide();
12097 this.toolTip.el.hide();
12099 window.onwheel = function(){ return true;};
12101 if(this.intervalID){
12102 window.clearInterval(this.intervalID);
12103 this.intervalID = false;
12106 this.isMasked = false;
12116 * Ext JS Library 1.1.1
12117 * Copyright(c) 2006-2007, Ext JS, LLC.
12119 * Originally Released Under LGPL - original licence link has changed is not relivant.
12122 * <script type="text/javascript">
12125 * @class Roo.form.VTypes
12126 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12129 Roo.form.VTypes = function(){
12130 // closure these in so they are only created once.
12131 var alpha = /^[a-zA-Z_]+$/;
12132 var alphanum = /^[a-zA-Z0-9_]+$/;
12133 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12134 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12136 // All these messages and functions are configurable
12139 * The function used to validate email addresses
12140 * @param {String} value The email address
12142 'email' : function(v){
12143 return email.test(v);
12146 * The error text to display when the email validation function returns false
12149 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12151 * The keystroke filter mask to be applied on email input
12154 'emailMask' : /[a-z0-9_\.\-@]/i,
12157 * The function used to validate URLs
12158 * @param {String} value The URL
12160 'url' : function(v){
12161 return url.test(v);
12164 * The error text to display when the url validation function returns false
12167 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12170 * The function used to validate alpha values
12171 * @param {String} value The value
12173 'alpha' : function(v){
12174 return alpha.test(v);
12177 * The error text to display when the alpha validation function returns false
12180 'alphaText' : 'This field should only contain letters and _',
12182 * The keystroke filter mask to be applied on alpha input
12185 'alphaMask' : /[a-z_]/i,
12188 * The function used to validate alphanumeric values
12189 * @param {String} value The value
12191 'alphanum' : function(v){
12192 return alphanum.test(v);
12195 * The error text to display when the alphanumeric validation function returns false
12198 'alphanumText' : 'This field should only contain letters, numbers and _',
12200 * The keystroke filter mask to be applied on alphanumeric input
12203 'alphanumMask' : /[a-z0-9_]/i
12213 * @class Roo.bootstrap.form.Input
12214 * @extends Roo.bootstrap.Component
12215 * Bootstrap Input class
12216 * @cfg {Boolean} disabled is it disabled
12217 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12218 * @cfg {String} name name of the input
12219 * @cfg {string} fieldLabel - the label associated
12220 * @cfg {string} placeholder - placeholder to put in text.
12221 * @cfg {string} before - input group add on before
12222 * @cfg {string} after - input group add on after
12223 * @cfg {string} size - (lg|sm) or leave empty..
12224 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12225 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12226 * @cfg {Number} md colspan out of 12 for computer-sized screens
12227 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12228 * @cfg {string} value default value of the input
12229 * @cfg {Number} labelWidth set the width of label
12230 * @cfg {Number} labellg set the width of label (1-12)
12231 * @cfg {Number} labelmd set the width of label (1-12)
12232 * @cfg {Number} labelsm set the width of label (1-12)
12233 * @cfg {Number} labelxs set the width of label (1-12)
12234 * @cfg {String} labelAlign (top|left)
12235 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12236 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12237 * @cfg {String} indicatorpos (left|right) default left
12238 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12239 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12240 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12241 * @cfg {Roo.bootstrap.Button} before Button to show before
12242 * @cfg {Roo.bootstrap.Button} afterButton to show before
12243 * @cfg {String} align (left|center|right) Default left
12244 * @cfg {Boolean} forceFeedback (true|false) Default false
12247 * Create a new Input
12248 * @param {Object} config The config object
12251 Roo.bootstrap.form.Input = function(config){
12253 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12258 * Fires when this field receives input focus.
12259 * @param {Roo.form.Field} this
12264 * Fires when this field loses input focus.
12265 * @param {Roo.form.Field} this
12269 * @event specialkey
12270 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12271 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12272 * @param {Roo.form.Field} this
12273 * @param {Roo.EventObject} e The event object
12278 * Fires just before the field blurs if the field value has changed.
12279 * @param {Roo.form.Field} this
12280 * @param {Mixed} newValue The new value
12281 * @param {Mixed} oldValue The original value
12286 * Fires after the field has been marked as invalid.
12287 * @param {Roo.form.Field} this
12288 * @param {String} msg The validation message
12293 * Fires after the field has been validated with no errors.
12294 * @param {Roo.form.Field} this
12299 * Fires after the key up
12300 * @param {Roo.form.Field} this
12301 * @param {Roo.EventObject} e The event Object
12306 * Fires after the user pastes into input
12307 * @param {Roo.form.Field} this
12308 * @param {Roo.EventObject} e The event Object
12314 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12316 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12317 automatic validation (defaults to "keyup").
12319 validationEvent : "keyup",
12321 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12323 validateOnBlur : true,
12325 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12327 validationDelay : 250,
12329 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12331 focusClass : "x-form-focus", // not needed???
12335 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12337 invalidClass : "has-warning",
12340 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12342 validClass : "has-success",
12345 * @cfg {Boolean} hasFeedback (true|false) default true
12347 hasFeedback : true,
12350 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12352 invalidFeedbackClass : "glyphicon-warning-sign",
12355 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12357 validFeedbackClass : "glyphicon-ok",
12360 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12362 selectOnFocus : false,
12365 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12369 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12374 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12376 disableKeyFilter : false,
12379 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12383 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12387 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12389 blankText : "Please complete this mandatory field",
12392 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12396 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12398 maxLength : Number.MAX_VALUE,
12400 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12402 minLengthText : "The minimum length for this field is {0}",
12404 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12406 maxLengthText : "The maximum length for this field is {0}",
12410 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12411 * If available, this function will be called only after the basic validators all return true, and will be passed the
12412 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12416 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12417 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12418 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12422 * @cfg {String} regexText -- Depricated - use Invalid Text
12427 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12433 autocomplete: false,
12437 inputType : 'text',
12440 placeholder: false,
12445 preventMark: false,
12446 isFormField : true,
12449 labelAlign : false,
12452 formatedValue : false,
12453 forceFeedback : false,
12455 indicatorpos : 'left',
12465 parentLabelAlign : function()
12468 while (parent.parent()) {
12469 parent = parent.parent();
12470 if (typeof(parent.labelAlign) !='undefined') {
12471 return parent.labelAlign;
12478 getAutoCreate : function()
12480 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12486 if(this.inputType != 'hidden'){
12487 cfg.cls = 'form-group' //input-group
12493 type : this.inputType,
12494 value : this.value,
12495 cls : 'form-control',
12496 placeholder : this.placeholder || '',
12497 autocomplete : this.autocomplete || 'new-password'
12499 if (this.inputType == 'file') {
12500 input.style = 'overflow:hidden'; // why not in CSS?
12503 if(this.capture.length){
12504 input.capture = this.capture;
12507 if(this.accept.length){
12508 input.accept = this.accept + "/*";
12512 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12515 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12516 input.maxLength = this.maxLength;
12519 if (this.disabled) {
12520 input.disabled=true;
12523 if (this.readOnly) {
12524 input.readonly=true;
12528 input.name = this.name;
12532 input.cls += ' input-' + this.size;
12536 ['xs','sm','md','lg'].map(function(size){
12537 if (settings[size]) {
12538 cfg.cls += ' col-' + size + '-' + settings[size];
12542 var inputblock = input;
12546 cls: 'glyphicon form-control-feedback'
12549 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12552 cls : 'has-feedback',
12560 if (this.before || this.after) {
12563 cls : 'input-group',
12567 if (this.before && typeof(this.before) == 'string') {
12569 inputblock.cn.push({
12571 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12575 if (this.before && typeof(this.before) == 'object') {
12576 this.before = Roo.factory(this.before);
12578 inputblock.cn.push({
12580 cls : 'roo-input-before input-group-prepend input-group-' +
12581 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12585 inputblock.cn.push(input);
12587 if (this.after && typeof(this.after) == 'string') {
12588 inputblock.cn.push({
12590 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12594 if (this.after && typeof(this.after) == 'object') {
12595 this.after = Roo.factory(this.after);
12597 inputblock.cn.push({
12599 cls : 'roo-input-after input-group-append input-group-' +
12600 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12604 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12605 inputblock.cls += ' has-feedback';
12606 inputblock.cn.push(feedback);
12611 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12612 tooltip : 'This field is required'
12614 if (this.allowBlank ) {
12615 indicator.style = this.allowBlank ? ' display:none' : '';
12617 if (align ==='left' && this.fieldLabel.length) {
12619 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12626 cls : 'control-label col-form-label',
12627 html : this.fieldLabel
12638 var labelCfg = cfg.cn[1];
12639 var contentCfg = cfg.cn[2];
12641 if(this.indicatorpos == 'right'){
12646 cls : 'control-label col-form-label',
12650 html : this.fieldLabel
12664 labelCfg = cfg.cn[0];
12665 contentCfg = cfg.cn[1];
12669 if(this.labelWidth > 12){
12670 labelCfg.style = "width: " + this.labelWidth + 'px';
12673 if(this.labelWidth < 13 && this.labelmd == 0){
12674 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12677 if(this.labellg > 0){
12678 labelCfg.cls += ' col-lg-' + this.labellg;
12679 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12682 if(this.labelmd > 0){
12683 labelCfg.cls += ' col-md-' + this.labelmd;
12684 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12687 if(this.labelsm > 0){
12688 labelCfg.cls += ' col-sm-' + this.labelsm;
12689 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12692 if(this.labelxs > 0){
12693 labelCfg.cls += ' col-xs-' + this.labelxs;
12694 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12698 } else if ( this.fieldLabel.length) {
12705 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12706 tooltip : 'This field is required',
12707 style : this.allowBlank ? ' display:none' : ''
12711 //cls : 'input-group-addon',
12712 html : this.fieldLabel
12720 if(this.indicatorpos == 'right'){
12725 //cls : 'input-group-addon',
12726 html : this.fieldLabel
12731 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12732 tooltip : 'This field is required',
12733 style : this.allowBlank ? ' display:none' : ''
12753 if (this.parentType === 'Navbar' && this.parent().bar) {
12754 cfg.cls += ' navbar-form';
12757 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12758 // on BS4 we do this only if not form
12759 cfg.cls += ' navbar-form';
12767 * return the real input element.
12769 inputEl: function ()
12771 return this.el.select('input.form-control',true).first();
12774 tooltipEl : function()
12776 return this.inputEl();
12779 indicatorEl : function()
12781 if (Roo.bootstrap.version == 4) {
12782 return false; // not enabled in v4 yet.
12785 var indicator = this.el.select('i.roo-required-indicator',true).first();
12795 setDisabled : function(v)
12797 var i = this.inputEl().dom;
12799 i.removeAttribute('disabled');
12803 i.setAttribute('disabled','true');
12805 initEvents : function()
12808 this.inputEl().on("keydown" , this.fireKey, this);
12809 this.inputEl().on("focus", this.onFocus, this);
12810 this.inputEl().on("blur", this.onBlur, this);
12812 this.inputEl().relayEvent('keyup', this);
12813 this.inputEl().relayEvent('paste', this);
12815 this.indicator = this.indicatorEl();
12817 if(this.indicator){
12818 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12821 // reference to original value for reset
12822 this.originalValue = this.getValue();
12823 //Roo.form.TextField.superclass.initEvents.call(this);
12824 if(this.validationEvent == 'keyup'){
12825 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12826 this.inputEl().on('keyup', this.filterValidation, this);
12828 else if(this.validationEvent !== false){
12829 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12832 if(this.selectOnFocus){
12833 this.on("focus", this.preFocus, this);
12836 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12837 this.inputEl().on("keypress", this.filterKeys, this);
12839 this.inputEl().relayEvent('keypress', this);
12842 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12843 this.el.on("click", this.autoSize, this);
12846 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12847 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12850 if (typeof(this.before) == 'object') {
12851 this.before.render(this.el.select('.roo-input-before',true).first());
12853 if (typeof(this.after) == 'object') {
12854 this.after.render(this.el.select('.roo-input-after',true).first());
12857 this.inputEl().on('change', this.onChange, this);
12860 filterValidation : function(e){
12861 if(!e.isNavKeyPress()){
12862 this.validationTask.delay(this.validationDelay);
12866 * Validates the field value
12867 * @return {Boolean} True if the value is valid, else false
12869 validate : function(){
12870 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12871 if(this.disabled || this.validateValue(this.getRawValue())){
12876 this.markInvalid();
12882 * Validates a value according to the field's validation rules and marks the field as invalid
12883 * if the validation fails
12884 * @param {Mixed} value The value to validate
12885 * @return {Boolean} True if the value is valid, else false
12887 validateValue : function(value)
12889 if(this.getVisibilityEl().hasClass('hidden')){
12893 if(value.length < 1) { // if it's blank
12894 if(this.allowBlank){
12900 if(value.length < this.minLength){
12903 if(value.length > this.maxLength){
12907 var vt = Roo.form.VTypes;
12908 if(!vt[this.vtype](value, this)){
12912 if(typeof this.validator == "function"){
12913 var msg = this.validator(value);
12917 if (typeof(msg) == 'string') {
12918 this.invalidText = msg;
12922 if(this.regex && !this.regex.test(value)){
12930 fireKey : function(e){
12931 //Roo.log('field ' + e.getKey());
12932 if(e.isNavKeyPress()){
12933 this.fireEvent("specialkey", this, e);
12936 focus : function (selectText){
12938 this.inputEl().focus();
12939 if(selectText === true){
12940 this.inputEl().dom.select();
12946 onFocus : function(){
12947 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12948 // this.el.addClass(this.focusClass);
12950 if(!this.hasFocus){
12951 this.hasFocus = true;
12952 this.startValue = this.getValue();
12953 this.fireEvent("focus", this);
12957 beforeBlur : Roo.emptyFn,
12961 onBlur : function(){
12963 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12964 //this.el.removeClass(this.focusClass);
12966 this.hasFocus = false;
12967 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12970 var v = this.getValue();
12971 if(String(v) !== String(this.startValue)){
12972 this.fireEvent('change', this, v, this.startValue);
12974 this.fireEvent("blur", this);
12977 onChange : function(e)
12979 var v = this.getValue();
12980 if(String(v) !== String(this.startValue)){
12981 this.fireEvent('change', this, v, this.startValue);
12987 * Resets the current field value to the originally loaded value and clears any validation messages
12989 reset : function(){
12990 this.setValue(this.originalValue);
12994 * Returns the name of the field
12995 * @return {Mixed} name The name field
12997 getName: function(){
13001 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13002 * @return {Mixed} value The field value
13004 getValue : function(){
13006 var v = this.inputEl().getValue();
13011 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13012 * @return {Mixed} value The field value
13014 getRawValue : function(){
13015 var v = this.inputEl().getValue();
13021 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13022 * @param {Mixed} value The value to set
13024 setRawValue : function(v){
13025 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13028 selectText : function(start, end){
13029 var v = this.getRawValue();
13031 start = start === undefined ? 0 : start;
13032 end = end === undefined ? v.length : end;
13033 var d = this.inputEl().dom;
13034 if(d.setSelectionRange){
13035 d.setSelectionRange(start, end);
13036 }else if(d.createTextRange){
13037 var range = d.createTextRange();
13038 range.moveStart("character", start);
13039 range.moveEnd("character", v.length-end);
13046 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13047 * @param {Mixed} value The value to set
13049 setValue : function(v){
13052 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13058 processValue : function(value){
13059 if(this.stripCharsRe){
13060 var newValue = value.replace(this.stripCharsRe, '');
13061 if(newValue !== value){
13062 this.setRawValue(newValue);
13069 preFocus : function(){
13071 if(this.selectOnFocus){
13072 this.inputEl().dom.select();
13075 filterKeys : function(e){
13076 var k = e.getKey();
13077 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13080 var c = e.getCharCode(), cc = String.fromCharCode(c);
13081 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13084 if(!this.maskRe.test(cc)){
13089 * Clear any invalid styles/messages for this field
13091 clearInvalid : function(){
13093 if(!this.el || this.preventMark){ // not rendered
13098 this.el.removeClass([this.invalidClass, 'is-invalid']);
13100 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13102 var feedback = this.el.select('.form-control-feedback', true).first();
13105 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13110 if(this.indicator){
13111 this.indicator.removeClass('visible');
13112 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13115 this.fireEvent('valid', this);
13119 * Mark this field as valid
13121 markValid : function()
13123 if(!this.el || this.preventMark){ // not rendered...
13127 this.el.removeClass([this.invalidClass, this.validClass]);
13128 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13130 var feedback = this.el.select('.form-control-feedback', true).first();
13133 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13136 if(this.indicator){
13137 this.indicator.removeClass('visible');
13138 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13146 if(this.allowBlank && !this.getRawValue().length){
13149 if (Roo.bootstrap.version == 3) {
13150 this.el.addClass(this.validClass);
13152 this.inputEl().addClass('is-valid');
13155 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13157 var feedback = this.el.select('.form-control-feedback', true).first();
13160 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13161 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13166 this.fireEvent('valid', this);
13170 * Mark this field as invalid
13171 * @param {String} msg The validation message
13173 markInvalid : function(msg)
13175 if(!this.el || this.preventMark){ // not rendered
13179 this.el.removeClass([this.invalidClass, this.validClass]);
13180 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13182 var feedback = this.el.select('.form-control-feedback', true).first();
13185 this.el.select('.form-control-feedback', true).first().removeClass(
13186 [this.invalidFeedbackClass, this.validFeedbackClass]);
13193 if(this.allowBlank && !this.getRawValue().length){
13197 if(this.indicator){
13198 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13199 this.indicator.addClass('visible');
13201 if (Roo.bootstrap.version == 3) {
13202 this.el.addClass(this.invalidClass);
13204 this.inputEl().addClass('is-invalid');
13209 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13211 var feedback = this.el.select('.form-control-feedback', true).first();
13214 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13216 if(this.getValue().length || this.forceFeedback){
13217 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13224 this.fireEvent('invalid', this, msg);
13227 SafariOnKeyDown : function(event)
13229 // this is a workaround for a password hang bug on chrome/ webkit.
13230 if (this.inputEl().dom.type != 'password') {
13234 var isSelectAll = false;
13236 if(this.inputEl().dom.selectionEnd > 0){
13237 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13239 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13240 event.preventDefault();
13245 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13247 event.preventDefault();
13248 // this is very hacky as keydown always get's upper case.
13250 var cc = String.fromCharCode(event.getCharCode());
13251 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13255 adjustWidth : function(tag, w){
13256 tag = tag.toLowerCase();
13257 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13258 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13259 if(tag == 'input'){
13262 if(tag == 'textarea'){
13265 }else if(Roo.isOpera){
13266 if(tag == 'input'){
13269 if(tag == 'textarea'){
13277 setFieldLabel : function(v)
13279 if(!this.rendered){
13283 if(this.indicatorEl()){
13284 var ar = this.el.select('label > span',true);
13286 if (ar.elements.length) {
13287 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13288 this.fieldLabel = v;
13292 var br = this.el.select('label',true);
13294 if(br.elements.length) {
13295 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13296 this.fieldLabel = v;
13300 Roo.log('Cannot Found any of label > span || label in input');
13304 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13305 this.fieldLabel = v;
13320 * @class Roo.bootstrap.form.TextArea
13321 * @extends Roo.bootstrap.form.Input
13322 * Bootstrap TextArea class
13323 * @cfg {Number} cols Specifies the visible width of a text area
13324 * @cfg {Number} rows Specifies the visible number of lines in a text area
13325 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13326 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13327 * @cfg {string} html text
13330 * Create a new TextArea
13331 * @param {Object} config The config object
13334 Roo.bootstrap.form.TextArea = function(config){
13335 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13339 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13349 getAutoCreate : function(){
13351 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13357 if(this.inputType != 'hidden'){
13358 cfg.cls = 'form-group' //input-group
13366 value : this.value || '',
13367 html: this.html || '',
13368 cls : 'form-control',
13369 placeholder : this.placeholder || ''
13373 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13374 input.maxLength = this.maxLength;
13378 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13382 input.cols = this.cols;
13385 if (this.readOnly) {
13386 input.readonly = true;
13390 input.name = this.name;
13394 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13398 ['xs','sm','md','lg'].map(function(size){
13399 if (settings[size]) {
13400 cfg.cls += ' col-' + size + '-' + settings[size];
13404 var inputblock = input;
13406 if(this.hasFeedback && !this.allowBlank){
13410 cls: 'glyphicon form-control-feedback'
13414 cls : 'has-feedback',
13423 if (this.before || this.after) {
13426 cls : 'input-group',
13430 inputblock.cn.push({
13432 cls : 'input-group-addon',
13437 inputblock.cn.push(input);
13439 if(this.hasFeedback && !this.allowBlank){
13440 inputblock.cls += ' has-feedback';
13441 inputblock.cn.push(feedback);
13445 inputblock.cn.push({
13447 cls : 'input-group-addon',
13454 if (align ==='left' && this.fieldLabel.length) {
13459 cls : 'control-label',
13460 html : this.fieldLabel
13471 if(this.labelWidth > 12){
13472 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13475 if(this.labelWidth < 13 && this.labelmd == 0){
13476 this.labelmd = this.labelWidth;
13479 if(this.labellg > 0){
13480 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13481 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13484 if(this.labelmd > 0){
13485 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13486 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13489 if(this.labelsm > 0){
13490 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13491 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13494 if(this.labelxs > 0){
13495 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13496 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13499 } else if ( this.fieldLabel.length) {
13504 //cls : 'input-group-addon',
13505 html : this.fieldLabel
13523 if (this.disabled) {
13524 input.disabled=true;
13531 * return the real textarea element.
13533 inputEl: function ()
13535 return this.el.select('textarea.form-control',true).first();
13539 * Clear any invalid styles/messages for this field
13541 clearInvalid : function()
13544 if(!this.el || this.preventMark){ // not rendered
13548 var label = this.el.select('label', true).first();
13549 var icon = this.el.select('i.fa-star', true).first();
13554 this.el.removeClass( this.validClass);
13555 this.inputEl().removeClass('is-invalid');
13557 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13559 var feedback = this.el.select('.form-control-feedback', true).first();
13562 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13567 this.fireEvent('valid', this);
13571 * Mark this field as valid
13573 markValid : function()
13575 if(!this.el || this.preventMark){ // not rendered
13579 this.el.removeClass([this.invalidClass, this.validClass]);
13580 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13582 var feedback = this.el.select('.form-control-feedback', true).first();
13585 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13588 if(this.disabled || this.allowBlank){
13592 var label = this.el.select('label', true).first();
13593 var icon = this.el.select('i.fa-star', true).first();
13598 if (Roo.bootstrap.version == 3) {
13599 this.el.addClass(this.validClass);
13601 this.inputEl().addClass('is-valid');
13605 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13607 var feedback = this.el.select('.form-control-feedback', true).first();
13610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13611 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13616 this.fireEvent('valid', this);
13620 * Mark this field as invalid
13621 * @param {String} msg The validation message
13623 markInvalid : function(msg)
13625 if(!this.el || this.preventMark){ // not rendered
13629 this.el.removeClass([this.invalidClass, this.validClass]);
13630 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13632 var feedback = this.el.select('.form-control-feedback', true).first();
13635 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13638 if(this.disabled || this.allowBlank){
13642 var label = this.el.select('label', true).first();
13643 var icon = this.el.select('i.fa-star', true).first();
13645 if(!this.getValue().length && label && !icon){
13646 this.el.createChild({
13648 cls : 'text-danger fa fa-lg fa-star',
13649 tooltip : 'This field is required',
13650 style : 'margin-right:5px;'
13654 if (Roo.bootstrap.version == 3) {
13655 this.el.addClass(this.invalidClass);
13657 this.inputEl().addClass('is-invalid');
13660 // fixme ... this may be depricated need to test..
13661 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13663 var feedback = this.el.select('.form-control-feedback', true).first();
13666 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13668 if(this.getValue().length || this.forceFeedback){
13669 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13676 this.fireEvent('invalid', this, msg);
13684 * trigger field - base class for combo..
13689 * @class Roo.bootstrap.form.TriggerField
13690 * @extends Roo.bootstrap.form.Input
13691 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13692 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13693 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13694 * for which you can provide a custom implementation. For example:
13696 var trigger = new Roo.bootstrap.form.TriggerField();
13697 trigger.onTriggerClick = myTriggerFn;
13698 trigger.applyTo('my-field');
13701 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13702 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13703 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13704 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13705 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13708 * Create a new TriggerField.
13709 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13710 * to the base TextField)
13712 Roo.bootstrap.form.TriggerField = function(config){
13713 this.mimicing = false;
13714 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13717 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13719 * @cfg {String} triggerClass A CSS class to apply to the trigger
13722 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13727 * @cfg {Boolean} removable (true|false) special filter default false
13731 /** @cfg {Boolean} grow @hide */
13732 /** @cfg {Number} growMin @hide */
13733 /** @cfg {Number} growMax @hide */
13739 autoSize: Roo.emptyFn,
13743 deferHeight : true,
13746 actionMode : 'wrap',
13751 getAutoCreate : function(){
13753 var align = this.labelAlign || this.parentLabelAlign();
13758 cls: 'form-group' //input-group
13765 type : this.inputType,
13766 cls : 'form-control',
13767 autocomplete: 'new-password',
13768 placeholder : this.placeholder || ''
13772 input.name = this.name;
13775 input.cls += ' input-' + this.size;
13778 if (this.disabled) {
13779 input.disabled=true;
13782 var inputblock = input;
13784 if(this.hasFeedback && !this.allowBlank){
13788 cls: 'glyphicon form-control-feedback'
13791 if(this.removable && !this.editable ){
13793 cls : 'has-feedback',
13799 cls : 'roo-combo-removable-btn close'
13806 cls : 'has-feedback',
13815 if(this.removable && !this.editable ){
13817 cls : 'roo-removable',
13823 cls : 'roo-combo-removable-btn close'
13830 if (this.before || this.after) {
13833 cls : 'input-group',
13837 inputblock.cn.push({
13839 cls : 'input-group-addon input-group-prepend input-group-text',
13844 inputblock.cn.push(input);
13846 if(this.hasFeedback && !this.allowBlank){
13847 inputblock.cls += ' has-feedback';
13848 inputblock.cn.push(feedback);
13852 inputblock.cn.push({
13854 cls : 'input-group-addon input-group-append input-group-text',
13863 var ibwrap = inputblock;
13868 cls: 'roo-select2-choices',
13872 cls: 'roo-select2-search-field',
13884 cls: 'roo-select2-container input-group',
13889 cls: 'form-hidden-field'
13895 if(!this.multiple && this.showToggleBtn){
13901 if (this.caret != false) {
13904 cls: 'fa fa-' + this.caret
13911 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13913 Roo.bootstrap.version == 3 ? caret : '',
13916 cls: 'combobox-clear',
13930 combobox.cls += ' roo-select2-container-multi';
13934 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13935 tooltip : 'This field is required'
13937 if (Roo.bootstrap.version == 4) {
13940 style : 'display:none'
13945 if (align ==='left' && this.fieldLabel.length) {
13947 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13954 cls : 'control-label',
13955 html : this.fieldLabel
13967 var labelCfg = cfg.cn[1];
13968 var contentCfg = cfg.cn[2];
13970 if(this.indicatorpos == 'right'){
13975 cls : 'control-label',
13979 html : this.fieldLabel
13993 labelCfg = cfg.cn[0];
13994 contentCfg = cfg.cn[1];
13997 if(this.labelWidth > 12){
13998 labelCfg.style = "width: " + this.labelWidth + 'px';
14001 if(this.labelWidth < 13 && this.labelmd == 0){
14002 this.labelmd = this.labelWidth;
14005 if(this.labellg > 0){
14006 labelCfg.cls += ' col-lg-' + this.labellg;
14007 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14010 if(this.labelmd > 0){
14011 labelCfg.cls += ' col-md-' + this.labelmd;
14012 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14015 if(this.labelsm > 0){
14016 labelCfg.cls += ' col-sm-' + this.labelsm;
14017 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14020 if(this.labelxs > 0){
14021 labelCfg.cls += ' col-xs-' + this.labelxs;
14022 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14025 } else if ( this.fieldLabel.length) {
14026 // Roo.log(" label");
14031 //cls : 'input-group-addon',
14032 html : this.fieldLabel
14040 if(this.indicatorpos == 'right'){
14048 html : this.fieldLabel
14062 // Roo.log(" no label && no align");
14069 ['xs','sm','md','lg'].map(function(size){
14070 if (settings[size]) {
14071 cfg.cls += ' col-' + size + '-' + settings[size];
14082 onResize : function(w, h){
14083 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14084 // if(typeof w == 'number'){
14085 // var x = w - this.trigger.getWidth();
14086 // this.inputEl().setWidth(this.adjustWidth('input', x));
14087 // this.trigger.setStyle('left', x+'px');
14092 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14095 getResizeEl : function(){
14096 return this.inputEl();
14100 getPositionEl : function(){
14101 return this.inputEl();
14105 alignErrorIcon : function(){
14106 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14110 initEvents : function(){
14114 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14115 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14116 if(!this.multiple && this.showToggleBtn){
14117 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14118 if(this.hideTrigger){
14119 this.trigger.setDisplayed(false);
14121 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14125 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14128 if(this.removable && !this.editable && !this.tickable){
14129 var close = this.closeTriggerEl();
14132 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14133 close.on('click', this.removeBtnClick, this, close);
14137 //this.trigger.addClassOnOver('x-form-trigger-over');
14138 //this.trigger.addClassOnClick('x-form-trigger-click');
14141 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14145 closeTriggerEl : function()
14147 var close = this.el.select('.roo-combo-removable-btn', true).first();
14148 return close ? close : false;
14151 removeBtnClick : function(e, h, el)
14153 e.preventDefault();
14155 if(this.fireEvent("remove", this) !== false){
14157 this.fireEvent("afterremove", this)
14161 createList : function()
14163 this.list = Roo.get(document.body).createChild({
14164 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14165 cls: 'typeahead typeahead-long dropdown-menu shadow',
14166 style: 'display:none'
14169 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14174 initTrigger : function(){
14179 onDestroy : function(){
14181 this.trigger.removeAllListeners();
14182 // this.trigger.remove();
14185 // this.wrap.remove();
14187 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14191 onFocus : function(){
14192 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14194 if(!this.mimicing){
14195 this.wrap.addClass('x-trigger-wrap-focus');
14196 this.mimicing = true;
14197 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14198 if(this.monitorTab){
14199 this.el.on("keydown", this.checkTab, this);
14206 checkTab : function(e){
14207 if(e.getKey() == e.TAB){
14208 this.triggerBlur();
14213 onBlur : function(){
14218 mimicBlur : function(e, t){
14220 if(!this.wrap.contains(t) && this.validateBlur()){
14221 this.triggerBlur();
14227 triggerBlur : function(){
14228 this.mimicing = false;
14229 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14230 if(this.monitorTab){
14231 this.el.un("keydown", this.checkTab, this);
14233 //this.wrap.removeClass('x-trigger-wrap-focus');
14234 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14238 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14239 validateBlur : function(e, t){
14244 onDisable : function(){
14245 this.inputEl().dom.disabled = true;
14246 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14248 // this.wrap.addClass('x-item-disabled');
14253 onEnable : function(){
14254 this.inputEl().dom.disabled = false;
14255 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14257 // this.el.removeClass('x-item-disabled');
14262 onShow : function(){
14263 var ae = this.getActionEl();
14266 ae.dom.style.display = '';
14267 ae.dom.style.visibility = 'visible';
14273 onHide : function(){
14274 var ae = this.getActionEl();
14275 ae.dom.style.display = 'none';
14279 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14280 * by an implementing function.
14282 * @param {EventObject} e
14284 onTriggerClick : Roo.emptyFn
14292 * @class Roo.bootstrap.form.CardUploader
14293 * @extends Roo.bootstrap.Button
14294 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14295 * @cfg {Number} errorTimeout default 3000
14296 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14297 * @cfg {Array} html The button text.
14301 * Create a new CardUploader
14302 * @param {Object} config The config object
14305 Roo.bootstrap.form.CardUploader = function(config){
14309 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14312 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14320 * When a image is clicked on - and needs to display a slideshow or similar..
14321 * @param {Roo.bootstrap.Card} this
14322 * @param {Object} The image information data
14328 * When a the download link is clicked
14329 * @param {Roo.bootstrap.Card} this
14330 * @param {Object} The image information data contains
14337 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14340 errorTimeout : 3000,
14344 fileCollection : false,
14347 getAutoCreate : function()
14351 cls :'form-group' ,
14356 //cls : 'input-group-addon',
14357 html : this.fieldLabel
14365 value : this.value,
14366 cls : 'd-none form-control'
14371 multiple : 'multiple',
14373 cls : 'd-none roo-card-upload-selector'
14377 cls : 'roo-card-uploader-button-container w-100 mb-2'
14380 cls : 'card-columns roo-card-uploader-container'
14390 getChildContainer : function() /// what children are added to.
14392 return this.containerEl;
14395 getButtonContainer : function() /// what children are added to.
14397 return this.el.select(".roo-card-uploader-button-container").first();
14400 initEvents : function()
14403 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14407 xns: Roo.bootstrap,
14410 container_method : 'getButtonContainer' ,
14411 html : this.html, // fix changable?
14414 'click' : function(btn, e) {
14423 this.urlAPI = (window.createObjectURL && window) ||
14424 (window.URL && URL.revokeObjectURL && URL) ||
14425 (window.webkitURL && webkitURL);
14430 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14432 this.selectorEl.on('change', this.onFileSelected, this);
14435 this.images.forEach(function(img) {
14438 this.images = false;
14440 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14446 onClick : function(e)
14448 e.preventDefault();
14450 this.selectorEl.dom.click();
14454 onFileSelected : function(e)
14456 e.preventDefault();
14458 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14462 Roo.each(this.selectorEl.dom.files, function(file){
14463 this.addFile(file);
14472 addFile : function(file)
14475 if(typeof(file) === 'string'){
14476 throw "Add file by name?"; // should not happen
14480 if(!file || !this.urlAPI){
14490 var url = _this.urlAPI.createObjectURL( file);
14493 id : Roo.bootstrap.form.CardUploader.ID--,
14494 is_uploaded : false,
14498 mimetype : file.type,
14506 * addCard - add an Attachment to the uploader
14507 * @param data - the data about the image to upload
14511 title : "Title of file",
14512 is_uploaded : false,
14513 src : "http://.....",
14514 srcfile : { the File upload object },
14515 mimetype : file.type,
14518 .. any other data...
14524 addCard : function (data)
14526 // hidden input element?
14527 // if the file is not an image...
14528 //then we need to use something other that and header_image
14533 xns : Roo.bootstrap,
14534 xtype : 'CardFooter',
14537 xns : Roo.bootstrap,
14543 xns : Roo.bootstrap,
14545 html : String.format("<small>{0}</small>", data.title),
14546 cls : 'col-10 text-left',
14551 click : function() {
14553 t.fireEvent( "download", t, data );
14559 xns : Roo.bootstrap,
14561 style: 'max-height: 28px; ',
14567 click : function() {
14568 t.removeCard(data.id)
14580 var cn = this.addxtype(
14583 xns : Roo.bootstrap,
14586 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14587 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14588 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14593 initEvents : function() {
14594 Roo.bootstrap.Card.prototype.initEvents.call(this);
14596 this.imgEl = this.el.select('.card-img-top').first();
14598 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14599 this.imgEl.set({ 'pointer' : 'cursor' });
14602 this.getCardFooter().addClass('p-1');
14609 // dont' really need ot update items.
14610 // this.items.push(cn);
14611 this.fileCollection.add(cn);
14613 if (!data.srcfile) {
14614 this.updateInput();
14619 var reader = new FileReader();
14620 reader.addEventListener("load", function() {
14621 data.srcdata = reader.result;
14624 reader.readAsDataURL(data.srcfile);
14629 removeCard : function(id)
14632 var card = this.fileCollection.get(id);
14633 card.data.is_deleted = 1;
14634 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14635 //this.fileCollection.remove(card);
14636 //this.items = this.items.filter(function(e) { return e != card });
14637 // dont' really need ot update items.
14638 card.el.dom.parentNode.removeChild(card.el.dom);
14639 this.updateInput();
14645 this.fileCollection.each(function(card) {
14646 if (card.el.dom && card.el.dom.parentNode) {
14647 card.el.dom.parentNode.removeChild(card.el.dom);
14650 this.fileCollection.clear();
14651 this.updateInput();
14654 updateInput : function()
14657 this.fileCollection.each(function(e) {
14661 this.inputEl().dom.value = JSON.stringify(data);
14671 Roo.bootstrap.form.CardUploader.ID = -1;/*
14673 * Ext JS Library 1.1.1
14674 * Copyright(c) 2006-2007, Ext JS, LLC.
14676 * Originally Released Under LGPL - original licence link has changed is not relivant.
14679 * <script type="text/javascript">
14684 * @class Roo.data.SortTypes
14686 * Defines the default sorting (casting?) comparison functions used when sorting data.
14688 Roo.data.SortTypes = {
14690 * Default sort that does nothing
14691 * @param {Mixed} s The value being converted
14692 * @return {Mixed} The comparison value
14694 none : function(s){
14699 * The regular expression used to strip tags
14703 stripTagsRE : /<\/?[^>]+>/gi,
14706 * Strips all HTML tags to sort on text only
14707 * @param {Mixed} s The value being converted
14708 * @return {String} The comparison value
14710 asText : function(s){
14711 return String(s).replace(this.stripTagsRE, "");
14715 * Strips all HTML tags to sort on text only - Case insensitive
14716 * @param {Mixed} s The value being converted
14717 * @return {String} The comparison value
14719 asUCText : function(s){
14720 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14724 * Case insensitive string
14725 * @param {Mixed} s The value being converted
14726 * @return {String} The comparison value
14728 asUCString : function(s) {
14729 return String(s).toUpperCase();
14734 * @param {Mixed} s The value being converted
14735 * @return {Number} The comparison value
14737 asDate : function(s) {
14741 if(s instanceof Date){
14742 return s.getTime();
14744 return Date.parse(String(s));
14749 * @param {Mixed} s The value being converted
14750 * @return {Float} The comparison value
14752 asFloat : function(s) {
14753 var val = parseFloat(String(s).replace(/,/g, ""));
14762 * @param {Mixed} s The value being converted
14763 * @return {Number} The comparison value
14765 asInt : function(s) {
14766 var val = parseInt(String(s).replace(/,/g, ""));
14774 * Ext JS Library 1.1.1
14775 * Copyright(c) 2006-2007, Ext JS, LLC.
14777 * Originally Released Under LGPL - original licence link has changed is not relivant.
14780 * <script type="text/javascript">
14784 * @class Roo.data.Record
14785 * Instances of this class encapsulate both record <em>definition</em> information, and record
14786 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14787 * to access Records cached in an {@link Roo.data.Store} object.<br>
14789 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14790 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14793 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14795 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14796 * {@link #create}. The parameters are the same.
14797 * @param {Array} data An associative Array of data values keyed by the field name.
14798 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14799 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14800 * not specified an integer id is generated.
14802 Roo.data.Record = function(data, id){
14803 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14808 * Generate a constructor for a specific record layout.
14809 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14810 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14811 * Each field definition object may contain the following properties: <ul>
14812 * <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,
14813 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14814 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14815 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14816 * is being used, then this is a string containing the javascript expression to reference the data relative to
14817 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14818 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14819 * this may be omitted.</p></li>
14820 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14821 * <ul><li>auto (Default, implies no conversion)</li>
14826 * <li>date</li></ul></p></li>
14827 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14828 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14829 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14830 * by the Reader into an object that will be stored in the Record. It is passed the
14831 * following parameters:<ul>
14832 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14834 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14836 * <br>usage:<br><pre><code>
14837 var TopicRecord = Roo.data.Record.create(
14838 {name: 'title', mapping: 'topic_title'},
14839 {name: 'author', mapping: 'username'},
14840 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14841 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14842 {name: 'lastPoster', mapping: 'user2'},
14843 {name: 'excerpt', mapping: 'post_text'}
14846 var myNewRecord = new TopicRecord({
14847 title: 'Do my job please',
14850 lastPost: new Date(),
14851 lastPoster: 'Animal',
14852 excerpt: 'No way dude!'
14854 myStore.add(myNewRecord);
14859 Roo.data.Record.create = function(o){
14860 var f = function(){
14861 f.superclass.constructor.apply(this, arguments);
14863 Roo.extend(f, Roo.data.Record);
14864 var p = f.prototype;
14865 p.fields = new Roo.util.MixedCollection(false, function(field){
14868 for(var i = 0, len = o.length; i < len; i++){
14869 p.fields.add(new Roo.data.Field(o[i]));
14871 f.getField = function(name){
14872 return p.fields.get(name);
14877 Roo.data.Record.AUTO_ID = 1000;
14878 Roo.data.Record.EDIT = 'edit';
14879 Roo.data.Record.REJECT = 'reject';
14880 Roo.data.Record.COMMIT = 'commit';
14882 Roo.data.Record.prototype = {
14884 * Readonly flag - true if this record has been modified.
14893 join : function(store){
14894 this.store = store;
14898 * Set the named field to the specified value.
14899 * @param {String} name The name of the field to set.
14900 * @param {Object} value The value to set the field to.
14902 set : function(name, value){
14903 if(this.data[name] == value){
14907 if(!this.modified){
14908 this.modified = {};
14910 if(typeof this.modified[name] == 'undefined'){
14911 this.modified[name] = this.data[name];
14913 this.data[name] = value;
14914 if(!this.editing && this.store){
14915 this.store.afterEdit(this);
14920 * Get the value of the named field.
14921 * @param {String} name The name of the field to get the value of.
14922 * @return {Object} The value of the field.
14924 get : function(name){
14925 return this.data[name];
14929 beginEdit : function(){
14930 this.editing = true;
14931 this.modified = {};
14935 cancelEdit : function(){
14936 this.editing = false;
14937 delete this.modified;
14941 endEdit : function(){
14942 this.editing = false;
14943 if(this.dirty && this.store){
14944 this.store.afterEdit(this);
14949 * Usually called by the {@link Roo.data.Store} which owns the Record.
14950 * Rejects all changes made to the Record since either creation, or the last commit operation.
14951 * Modified fields are reverted to their original values.
14953 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14954 * of reject operations.
14956 reject : function(){
14957 var m = this.modified;
14959 if(typeof m[n] != "function"){
14960 this.data[n] = m[n];
14963 this.dirty = false;
14964 delete this.modified;
14965 this.editing = false;
14967 this.store.afterReject(this);
14972 * Usually called by the {@link Roo.data.Store} which owns the Record.
14973 * Commits all changes made to the Record since either creation, or the last commit operation.
14975 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14976 * of commit operations.
14978 commit : function(){
14979 this.dirty = false;
14980 delete this.modified;
14981 this.editing = false;
14983 this.store.afterCommit(this);
14988 hasError : function(){
14989 return this.error != null;
14993 clearError : function(){
14998 * Creates a copy of this record.
14999 * @param {String} id (optional) A new record id if you don't want to use this record's id
15002 copy : function(newId) {
15003 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15007 * Ext JS Library 1.1.1
15008 * Copyright(c) 2006-2007, Ext JS, LLC.
15010 * Originally Released Under LGPL - original licence link has changed is not relivant.
15013 * <script type="text/javascript">
15019 * @class Roo.data.Store
15020 * @extends Roo.util.Observable
15021 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15022 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15024 * 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
15025 * has no knowledge of the format of the data returned by the Proxy.<br>
15027 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15028 * instances from the data object. These records are cached and made available through accessor functions.
15030 * Creates a new Store.
15031 * @param {Object} config A config object containing the objects needed for the Store to access data,
15032 * and read the data into Records.
15034 Roo.data.Store = function(config){
15035 this.data = new Roo.util.MixedCollection(false);
15036 this.data.getKey = function(o){
15039 this.baseParams = {};
15041 this.paramNames = {
15046 "multisort" : "_multisort"
15049 if(config && config.data){
15050 this.inlineData = config.data;
15051 delete config.data;
15054 Roo.apply(this, config);
15056 if(this.reader){ // reader passed
15057 this.reader = Roo.factory(this.reader, Roo.data);
15058 this.reader.xmodule = this.xmodule || false;
15059 if(!this.recordType){
15060 this.recordType = this.reader.recordType;
15062 if(this.reader.onMetaChange){
15063 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15067 if(this.recordType){
15068 this.fields = this.recordType.prototype.fields;
15070 this.modified = [];
15074 * @event datachanged
15075 * Fires when the data cache has changed, and a widget which is using this Store
15076 * as a Record cache should refresh its view.
15077 * @param {Store} this
15079 datachanged : true,
15081 * @event metachange
15082 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15083 * @param {Store} this
15084 * @param {Object} meta The JSON metadata
15089 * Fires when Records have been added to the Store
15090 * @param {Store} this
15091 * @param {Roo.data.Record[]} records The array of Records added
15092 * @param {Number} index The index at which the record(s) were added
15097 * Fires when a Record has been removed from the Store
15098 * @param {Store} this
15099 * @param {Roo.data.Record} record The Record that was removed
15100 * @param {Number} index The index at which the record was removed
15105 * Fires when a Record has been updated
15106 * @param {Store} this
15107 * @param {Roo.data.Record} record The Record that was updated
15108 * @param {String} operation The update operation being performed. Value may be one of:
15110 Roo.data.Record.EDIT
15111 Roo.data.Record.REJECT
15112 Roo.data.Record.COMMIT
15118 * Fires when the data cache has been cleared.
15119 * @param {Store} this
15123 * @event beforeload
15124 * Fires before a request is made for a new data object. If the beforeload handler returns false
15125 * the load action will be canceled.
15126 * @param {Store} this
15127 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15131 * @event beforeloadadd
15132 * Fires after a new set of Records has been loaded.
15133 * @param {Store} this
15134 * @param {Roo.data.Record[]} records The Records that were loaded
15135 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15137 beforeloadadd : true,
15140 * Fires after a new set of Records has been loaded, before they are added to the store.
15141 * @param {Store} this
15142 * @param {Roo.data.Record[]} records The Records that were loaded
15143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15144 * @params {Object} return from reader
15148 * @event loadexception
15149 * Fires if an exception occurs in the Proxy during loading.
15150 * Called with the signature of the Proxy's "loadexception" event.
15151 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15154 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15155 * @param {Object} load options
15156 * @param {Object} jsonData from your request (normally this contains the Exception)
15158 loadexception : true
15162 this.proxy = Roo.factory(this.proxy, Roo.data);
15163 this.proxy.xmodule = this.xmodule || false;
15164 this.relayEvents(this.proxy, ["loadexception"]);
15166 this.sortToggle = {};
15167 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15169 Roo.data.Store.superclass.constructor.call(this);
15171 if(this.inlineData){
15172 this.loadData(this.inlineData);
15173 delete this.inlineData;
15177 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15179 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15180 * without a remote query - used by combo/forms at present.
15184 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15187 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15190 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15191 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15194 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15195 * on any HTTP request
15198 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15201 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15205 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15206 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15208 remoteSort : false,
15211 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15212 * loaded or when a record is removed. (defaults to false).
15214 pruneModifiedRecords : false,
15217 lastOptions : null,
15220 * Add Records to the Store and fires the add event.
15221 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15223 add : function(records){
15224 records = [].concat(records);
15225 for(var i = 0, len = records.length; i < len; i++){
15226 records[i].join(this);
15228 var index = this.data.length;
15229 this.data.addAll(records);
15230 this.fireEvent("add", this, records, index);
15234 * Remove a Record from the Store and fires the remove event.
15235 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15237 remove : function(record){
15238 var index = this.data.indexOf(record);
15239 this.data.removeAt(index);
15241 if(this.pruneModifiedRecords){
15242 this.modified.remove(record);
15244 this.fireEvent("remove", this, record, index);
15248 * Remove all Records from the Store and fires the clear event.
15250 removeAll : function(){
15252 if(this.pruneModifiedRecords){
15253 this.modified = [];
15255 this.fireEvent("clear", this);
15259 * Inserts Records to the Store at the given index and fires the add event.
15260 * @param {Number} index The start index at which to insert the passed Records.
15261 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15263 insert : function(index, records){
15264 records = [].concat(records);
15265 for(var i = 0, len = records.length; i < len; i++){
15266 this.data.insert(index, records[i]);
15267 records[i].join(this);
15269 this.fireEvent("add", this, records, index);
15273 * Get the index within the cache of the passed Record.
15274 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15275 * @return {Number} The index of the passed Record. Returns -1 if not found.
15277 indexOf : function(record){
15278 return this.data.indexOf(record);
15282 * Get the index within the cache of the Record with the passed id.
15283 * @param {String} id The id of the Record to find.
15284 * @return {Number} The index of the Record. Returns -1 if not found.
15286 indexOfId : function(id){
15287 return this.data.indexOfKey(id);
15291 * Get the Record with the specified id.
15292 * @param {String} id The id of the Record to find.
15293 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15295 getById : function(id){
15296 return this.data.key(id);
15300 * Get the Record at the specified index.
15301 * @param {Number} index The index of the Record to find.
15302 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15304 getAt : function(index){
15305 return this.data.itemAt(index);
15309 * Returns a range of Records between specified indices.
15310 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15311 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15312 * @return {Roo.data.Record[]} An array of Records
15314 getRange : function(start, end){
15315 return this.data.getRange(start, end);
15319 storeOptions : function(o){
15320 o = Roo.apply({}, o);
15323 this.lastOptions = o;
15327 * Loads the Record cache from the configured Proxy using the configured Reader.
15329 * If using remote paging, then the first load call must specify the <em>start</em>
15330 * and <em>limit</em> properties in the options.params property to establish the initial
15331 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15333 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15334 * and this call will return before the new data has been loaded. Perform any post-processing
15335 * in a callback function, or in a "load" event handler.</strong>
15337 * @param {Object} options An object containing properties which control loading options:<ul>
15338 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15339 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15340 * passed the following arguments:<ul>
15341 * <li>r : Roo.data.Record[]</li>
15342 * <li>options: Options object from the load call</li>
15343 * <li>success: Boolean success indicator</li></ul></li>
15344 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15345 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15348 load : function(options){
15349 options = options || {};
15350 if(this.fireEvent("beforeload", this, options) !== false){
15351 this.storeOptions(options);
15352 var p = Roo.apply(options.params || {}, this.baseParams);
15353 // if meta was not loaded from remote source.. try requesting it.
15354 if (!this.reader.metaFromRemote) {
15355 p._requestMeta = 1;
15357 if(this.sortInfo && this.remoteSort){
15358 var pn = this.paramNames;
15359 p[pn["sort"]] = this.sortInfo.field;
15360 p[pn["dir"]] = this.sortInfo.direction;
15362 if (this.multiSort) {
15363 var pn = this.paramNames;
15364 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15367 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15372 * Reloads the Record cache from the configured Proxy using the configured Reader and
15373 * the options from the last load operation performed.
15374 * @param {Object} options (optional) An object containing properties which may override the options
15375 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15376 * the most recently used options are reused).
15378 reload : function(options){
15379 this.load(Roo.applyIf(options||{}, this.lastOptions));
15383 // Called as a callback by the Reader during a load operation.
15384 loadRecords : function(o, options, success){
15385 if(!o || success === false){
15386 if(success !== false){
15387 this.fireEvent("load", this, [], options, o);
15389 if(options.callback){
15390 options.callback.call(options.scope || this, [], options, false);
15394 // if data returned failure - throw an exception.
15395 if (o.success === false) {
15396 // show a message if no listener is registered.
15397 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15398 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15400 // loadmask wil be hooked into this..
15401 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15404 var r = o.records, t = o.totalRecords || r.length;
15406 this.fireEvent("beforeloadadd", this, r, options, o);
15408 if(!options || options.add !== true){
15409 if(this.pruneModifiedRecords){
15410 this.modified = [];
15412 for(var i = 0, len = r.length; i < len; i++){
15416 this.data = this.snapshot;
15417 delete this.snapshot;
15420 this.data.addAll(r);
15421 this.totalLength = t;
15423 this.fireEvent("datachanged", this);
15425 this.totalLength = Math.max(t, this.data.length+r.length);
15429 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15431 var e = new Roo.data.Record({});
15433 e.set(this.parent.displayField, this.parent.emptyTitle);
15434 e.set(this.parent.valueField, '');
15439 this.fireEvent("load", this, r, options, o);
15440 if(options.callback){
15441 options.callback.call(options.scope || this, r, options, true);
15447 * Loads data from a passed data block. A Reader which understands the format of the data
15448 * must have been configured in the constructor.
15449 * @param {Object} data The data block from which to read the Records. The format of the data expected
15450 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15451 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15453 loadData : function(o, append){
15454 var r = this.reader.readRecords(o);
15455 this.loadRecords(r, {add: append}, true);
15459 * using 'cn' the nested child reader read the child array into it's child stores.
15460 * @param {Object} rec The record with a 'children array
15462 loadDataFromChildren : function(rec)
15464 this.loadData(this.reader.toLoadData(rec));
15469 * Gets the number of cached records.
15471 * <em>If using paging, this may not be the total size of the dataset. If the data object
15472 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15473 * the data set size</em>
15475 getCount : function(){
15476 return this.data.length || 0;
15480 * Gets the total number of records in the dataset as returned by the server.
15482 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15483 * the dataset size</em>
15485 getTotalCount : function(){
15486 return this.totalLength || 0;
15490 * Returns the sort state of the Store as an object with two properties:
15492 field {String} The name of the field by which the Records are sorted
15493 direction {String} The sort order, "ASC" or "DESC"
15496 getSortState : function(){
15497 return this.sortInfo;
15501 applySort : function(){
15502 if(this.sortInfo && !this.remoteSort){
15503 var s = this.sortInfo, f = s.field;
15504 var st = this.fields.get(f).sortType;
15505 var fn = function(r1, r2){
15506 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15507 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15509 this.data.sort(s.direction, fn);
15510 if(this.snapshot && this.snapshot != this.data){
15511 this.snapshot.sort(s.direction, fn);
15517 * Sets the default sort column and order to be used by the next load operation.
15518 * @param {String} fieldName The name of the field to sort by.
15519 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15521 setDefaultSort : function(field, dir){
15522 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15526 * Sort the Records.
15527 * If remote sorting is used, the sort is performed on the server, and the cache is
15528 * reloaded. If local sorting is used, the cache is sorted internally.
15529 * @param {String} fieldName The name of the field to sort by.
15530 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15532 sort : function(fieldName, dir){
15533 var f = this.fields.get(fieldName);
15535 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15537 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15538 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15543 this.sortToggle[f.name] = dir;
15544 this.sortInfo = {field: f.name, direction: dir};
15545 if(!this.remoteSort){
15547 this.fireEvent("datachanged", this);
15549 this.load(this.lastOptions);
15554 * Calls the specified function for each of the Records in the cache.
15555 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15556 * Returning <em>false</em> aborts and exits the iteration.
15557 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15559 each : function(fn, scope){
15560 this.data.each(fn, scope);
15564 * Gets all records modified since the last commit. Modified records are persisted across load operations
15565 * (e.g., during paging).
15566 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15568 getModifiedRecords : function(){
15569 return this.modified;
15573 createFilterFn : function(property, value, anyMatch){
15574 if(!value.exec){ // not a regex
15575 value = String(value);
15576 if(value.length == 0){
15579 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15581 return function(r){
15582 return value.test(r.data[property]);
15587 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15588 * @param {String} property A field on your records
15589 * @param {Number} start The record index to start at (defaults to 0)
15590 * @param {Number} end The last record index to include (defaults to length - 1)
15591 * @return {Number} The sum
15593 sum : function(property, start, end){
15594 var rs = this.data.items, v = 0;
15595 start = start || 0;
15596 end = (end || end === 0) ? end : rs.length-1;
15598 for(var i = start; i <= end; i++){
15599 v += (rs[i].data[property] || 0);
15605 * Filter the records by a specified property.
15606 * @param {String} field A field on your records
15607 * @param {String/RegExp} value Either a string that the field
15608 * should start with or a RegExp to test against the field
15609 * @param {Boolean} anyMatch True to match any part not just the beginning
15611 filter : function(property, value, anyMatch){
15612 var fn = this.createFilterFn(property, value, anyMatch);
15613 return fn ? this.filterBy(fn) : this.clearFilter();
15617 * Filter by a function. The specified function will be called with each
15618 * record in this data source. If the function returns true the record is included,
15619 * otherwise it is filtered.
15620 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15621 * @param {Object} scope (optional) The scope of the function (defaults to this)
15623 filterBy : function(fn, scope){
15624 this.snapshot = this.snapshot || this.data;
15625 this.data = this.queryBy(fn, scope||this);
15626 this.fireEvent("datachanged", this);
15630 * Query the records by a specified property.
15631 * @param {String} field A field on your records
15632 * @param {String/RegExp} value Either a string that the field
15633 * should start with or a RegExp to test against the field
15634 * @param {Boolean} anyMatch True to match any part not just the beginning
15635 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15637 query : function(property, value, anyMatch){
15638 var fn = this.createFilterFn(property, value, anyMatch);
15639 return fn ? this.queryBy(fn) : this.data.clone();
15643 * Query by a function. The specified function will be called with each
15644 * record in this data source. If the function returns true the record is included
15646 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15647 * @param {Object} scope (optional) The scope of the function (defaults to this)
15648 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15650 queryBy : function(fn, scope){
15651 var data = this.snapshot || this.data;
15652 return data.filterBy(fn, scope||this);
15656 * Collects unique values for a particular dataIndex from this store.
15657 * @param {String} dataIndex The property to collect
15658 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15659 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15660 * @return {Array} An array of the unique values
15662 collect : function(dataIndex, allowNull, bypassFilter){
15663 var d = (bypassFilter === true && this.snapshot) ?
15664 this.snapshot.items : this.data.items;
15665 var v, sv, r = [], l = {};
15666 for(var i = 0, len = d.length; i < len; i++){
15667 v = d[i].data[dataIndex];
15669 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15678 * Revert to a view of the Record cache with no filtering applied.
15679 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15681 clearFilter : function(suppressEvent){
15682 if(this.snapshot && this.snapshot != this.data){
15683 this.data = this.snapshot;
15684 delete this.snapshot;
15685 if(suppressEvent !== true){
15686 this.fireEvent("datachanged", this);
15692 afterEdit : function(record){
15693 if(this.modified.indexOf(record) == -1){
15694 this.modified.push(record);
15696 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15700 afterReject : function(record){
15701 this.modified.remove(record);
15702 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15706 afterCommit : function(record){
15707 this.modified.remove(record);
15708 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15712 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15713 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15715 commitChanges : function(){
15716 var m = this.modified.slice(0);
15717 this.modified = [];
15718 for(var i = 0, len = m.length; i < len; i++){
15724 * Cancel outstanding changes on all changed records.
15726 rejectChanges : function(){
15727 var m = this.modified.slice(0);
15728 this.modified = [];
15729 for(var i = 0, len = m.length; i < len; i++){
15734 onMetaChange : function(meta, rtype, o){
15735 this.recordType = rtype;
15736 this.fields = rtype.prototype.fields;
15737 delete this.snapshot;
15738 this.sortInfo = meta.sortInfo || this.sortInfo;
15739 this.modified = [];
15740 this.fireEvent('metachange', this, this.reader.meta);
15743 moveIndex : function(data, type)
15745 var index = this.indexOf(data);
15747 var newIndex = index + type;
15751 this.insert(newIndex, data);
15756 * Ext JS Library 1.1.1
15757 * Copyright(c) 2006-2007, Ext JS, LLC.
15759 * Originally Released Under LGPL - original licence link has changed is not relivant.
15762 * <script type="text/javascript">
15766 * @class Roo.data.SimpleStore
15767 * @extends Roo.data.Store
15768 * Small helper class to make creating Stores from Array data easier.
15769 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15770 * @cfg {Array} fields An array of field definition objects, or field name strings.
15771 * @cfg {Object} an existing reader (eg. copied from another store)
15772 * @cfg {Array} data The multi-dimensional array of data
15773 * @cfg {Roo.data.DataProxy} proxy [not-required]
15774 * @cfg {Roo.data.Reader} reader [not-required]
15776 * @param {Object} config
15778 Roo.data.SimpleStore = function(config)
15780 Roo.data.SimpleStore.superclass.constructor.call(this, {
15782 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15785 Roo.data.Record.create(config.fields)
15787 proxy : new Roo.data.MemoryProxy(config.data)
15791 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15793 * Ext JS Library 1.1.1
15794 * Copyright(c) 2006-2007, Ext JS, LLC.
15796 * Originally Released Under LGPL - original licence link has changed is not relivant.
15799 * <script type="text/javascript">
15804 * @extends Roo.data.Store
15805 * @class Roo.data.JsonStore
15806 * Small helper class to make creating Stores for JSON data easier. <br/>
15808 var store = new Roo.data.JsonStore({
15809 url: 'get-images.php',
15811 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15814 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15815 * JsonReader and HttpProxy (unless inline data is provided).</b>
15816 * @cfg {Array} fields An array of field definition objects, or field name strings.
15818 * @param {Object} config
15820 Roo.data.JsonStore = function(c){
15821 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15822 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15823 reader: new Roo.data.JsonReader(c, c.fields)
15826 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15828 * Ext JS Library 1.1.1
15829 * Copyright(c) 2006-2007, Ext JS, LLC.
15831 * Originally Released Under LGPL - original licence link has changed is not relivant.
15834 * <script type="text/javascript">
15838 Roo.data.Field = function(config){
15839 if(typeof config == "string"){
15840 config = {name: config};
15842 Roo.apply(this, config);
15845 this.type = "auto";
15848 var st = Roo.data.SortTypes;
15849 // named sortTypes are supported, here we look them up
15850 if(typeof this.sortType == "string"){
15851 this.sortType = st[this.sortType];
15854 // set default sortType for strings and dates
15855 if(!this.sortType){
15858 this.sortType = st.asUCString;
15861 this.sortType = st.asDate;
15864 this.sortType = st.none;
15869 var stripRe = /[\$,%]/g;
15871 // prebuilt conversion function for this field, instead of
15872 // switching every time we're reading a value
15874 var cv, dateFormat = this.dateFormat;
15879 cv = function(v){ return v; };
15882 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15886 return v !== undefined && v !== null && v !== '' ?
15887 parseInt(String(v).replace(stripRe, ""), 10) : '';
15892 return v !== undefined && v !== null && v !== '' ?
15893 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15898 cv = function(v){ return v === true || v === "true" || v == 1; };
15905 if(v instanceof Date){
15909 if(dateFormat == "timestamp"){
15910 return new Date(v*1000);
15912 return Date.parseDate(v, dateFormat);
15914 var parsed = Date.parse(v);
15915 return parsed ? new Date(parsed) : null;
15924 Roo.data.Field.prototype = {
15932 * Ext JS Library 1.1.1
15933 * Copyright(c) 2006-2007, Ext JS, LLC.
15935 * Originally Released Under LGPL - original licence link has changed is not relivant.
15938 * <script type="text/javascript">
15941 // Base class for reading structured data from a data source. This class is intended to be
15942 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15945 * @class Roo.data.DataReader
15947 * Base class for reading structured data from a data source. This class is intended to be
15948 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15951 Roo.data.DataReader = function(meta, recordType){
15955 this.recordType = recordType instanceof Array ?
15956 Roo.data.Record.create(recordType) : recordType;
15959 Roo.data.DataReader.prototype = {
15962 readerType : 'Data',
15964 * Create an empty record
15965 * @param {Object} data (optional) - overlay some values
15966 * @return {Roo.data.Record} record created.
15968 newRow : function(d) {
15970 this.recordType.prototype.fields.each(function(c) {
15972 case 'int' : da[c.name] = 0; break;
15973 case 'date' : da[c.name] = new Date(); break;
15974 case 'float' : da[c.name] = 0.0; break;
15975 case 'boolean' : da[c.name] = false; break;
15976 default : da[c.name] = ""; break;
15980 return new this.recordType(Roo.apply(da, d));
15986 * Ext JS Library 1.1.1
15987 * Copyright(c) 2006-2007, Ext JS, LLC.
15989 * Originally Released Under LGPL - original licence link has changed is not relivant.
15992 * <script type="text/javascript">
15996 * @class Roo.data.DataProxy
15997 * @extends Roo.util.Observable
15999 * This class is an abstract base class for implementations which provide retrieval of
16000 * unformatted data objects.<br>
16002 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16003 * (of the appropriate type which knows how to parse the data object) to provide a block of
16004 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16006 * Custom implementations must implement the load method as described in
16007 * {@link Roo.data.HttpProxy#load}.
16009 Roo.data.DataProxy = function(){
16012 * @event beforeload
16013 * Fires before a network request is made to retrieve a data object.
16014 * @param {Object} This DataProxy object.
16015 * @param {Object} params The params parameter to the load function.
16020 * Fires before the load method's callback is called.
16021 * @param {Object} This DataProxy object.
16022 * @param {Object} o The data object.
16023 * @param {Object} arg The callback argument object passed to the load function.
16027 * @event loadexception
16028 * Fires if an Exception occurs during data retrieval.
16029 * @param {Object} This DataProxy object.
16030 * @param {Object} o The data object.
16031 * @param {Object} arg The callback argument object passed to the load function.
16032 * @param {Object} e The Exception.
16034 loadexception : true
16036 Roo.data.DataProxy.superclass.constructor.call(this);
16039 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16042 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16046 * Ext JS Library 1.1.1
16047 * Copyright(c) 2006-2007, Ext JS, LLC.
16049 * Originally Released Under LGPL - original licence link has changed is not relivant.
16052 * <script type="text/javascript">
16055 * @class Roo.data.MemoryProxy
16056 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16057 * to the Reader when its load method is called.
16059 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16061 Roo.data.MemoryProxy = function(data){
16065 Roo.data.MemoryProxy.superclass.constructor.call(this);
16069 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16072 * Load data from the requested source (in this case an in-memory
16073 * data object passed to the constructor), read the data object into
16074 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16075 * process that block using the passed callback.
16076 * @param {Object} params This parameter is not used by the MemoryProxy class.
16077 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16078 * object into a block of Roo.data.Records.
16079 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16080 * The function must be passed <ul>
16081 * <li>The Record block object</li>
16082 * <li>The "arg" argument from the load function</li>
16083 * <li>A boolean success indicator</li>
16085 * @param {Object} scope The scope in which to call the callback
16086 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16088 load : function(params, reader, callback, scope, arg){
16089 params = params || {};
16092 result = reader.readRecords(params.data ? params.data :this.data);
16094 this.fireEvent("loadexception", this, arg, null, e);
16095 callback.call(scope, null, arg, false);
16098 callback.call(scope, result, arg, true);
16102 update : function(params, records){
16107 * Ext JS Library 1.1.1
16108 * Copyright(c) 2006-2007, Ext JS, LLC.
16110 * Originally Released Under LGPL - original licence link has changed is not relivant.
16113 * <script type="text/javascript">
16116 * @class Roo.data.HttpProxy
16117 * @extends Roo.data.DataProxy
16118 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16119 * configured to reference a certain URL.<br><br>
16121 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16122 * from which the running page was served.<br><br>
16124 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16126 * Be aware that to enable the browser to parse an XML document, the server must set
16127 * the Content-Type header in the HTTP response to "text/xml".
16129 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16130 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16131 * will be used to make the request.
16133 Roo.data.HttpProxy = function(conn){
16134 Roo.data.HttpProxy.superclass.constructor.call(this);
16135 // is conn a conn config or a real conn?
16137 this.useAjax = !conn || !conn.events;
16141 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16142 // thse are take from connection...
16145 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16148 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16149 * extra parameters to each request made by this object. (defaults to undefined)
16152 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16153 * to each request made by this object. (defaults to undefined)
16156 * @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)
16159 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16162 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16168 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16172 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16173 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16174 * a finer-grained basis than the DataProxy events.
16176 getConnection : function(){
16177 return this.useAjax ? Roo.Ajax : this.conn;
16181 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16182 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16183 * process that block using the passed callback.
16184 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16185 * for the request to the remote server.
16186 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16187 * object into a block of Roo.data.Records.
16188 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16189 * The function must be passed <ul>
16190 * <li>The Record block object</li>
16191 * <li>The "arg" argument from the load function</li>
16192 * <li>A boolean success indicator</li>
16194 * @param {Object} scope The scope in which to call the callback
16195 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16197 load : function(params, reader, callback, scope, arg){
16198 if(this.fireEvent("beforeload", this, params) !== false){
16200 params : params || {},
16202 callback : callback,
16207 callback : this.loadResponse,
16211 Roo.applyIf(o, this.conn);
16212 if(this.activeRequest){
16213 Roo.Ajax.abort(this.activeRequest);
16215 this.activeRequest = Roo.Ajax.request(o);
16217 this.conn.request(o);
16220 callback.call(scope||this, null, arg, false);
16225 loadResponse : function(o, success, response){
16226 delete this.activeRequest;
16228 this.fireEvent("loadexception", this, o, response);
16229 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16234 result = o.reader.read(response);
16236 this.fireEvent("loadexception", this, o, response, e);
16237 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16241 this.fireEvent("load", this, o, o.request.arg);
16242 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16246 update : function(dataSet){
16251 updateResponse : function(dataSet){
16256 * Ext JS Library 1.1.1
16257 * Copyright(c) 2006-2007, Ext JS, LLC.
16259 * Originally Released Under LGPL - original licence link has changed is not relivant.
16262 * <script type="text/javascript">
16266 * @class Roo.data.ScriptTagProxy
16267 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16268 * other than the originating domain of the running page.<br><br>
16270 * <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
16271 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16273 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16274 * source code that is used as the source inside a <script> tag.<br><br>
16276 * In order for the browser to process the returned data, the server must wrap the data object
16277 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16278 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16279 * depending on whether the callback name was passed:
16282 boolean scriptTag = false;
16283 String cb = request.getParameter("callback");
16286 response.setContentType("text/javascript");
16288 response.setContentType("application/x-json");
16290 Writer out = response.getWriter();
16292 out.write(cb + "(");
16294 out.print(dataBlock.toJsonString());
16301 * @param {Object} config A configuration object.
16303 Roo.data.ScriptTagProxy = function(config){
16304 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16305 Roo.apply(this, config);
16306 this.head = document.getElementsByTagName("head")[0];
16309 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16311 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16313 * @cfg {String} url The URL from which to request the data object.
16316 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16320 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16321 * the server the name of the callback function set up by the load call to process the returned data object.
16322 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16323 * javascript output which calls this named function passing the data object as its only parameter.
16325 callbackParam : "callback",
16327 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16328 * name to the request.
16333 * Load data from the configured URL, read the data object into
16334 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16335 * process that block using the passed callback.
16336 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16337 * for the request to the remote server.
16338 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16339 * object into a block of Roo.data.Records.
16340 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16341 * The function must be passed <ul>
16342 * <li>The Record block object</li>
16343 * <li>The "arg" argument from the load function</li>
16344 * <li>A boolean success indicator</li>
16346 * @param {Object} scope The scope in which to call the callback
16347 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16349 load : function(params, reader, callback, scope, arg){
16350 if(this.fireEvent("beforeload", this, params) !== false){
16352 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16354 var url = this.url;
16355 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16357 url += "&_dc=" + (new Date().getTime());
16359 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16362 cb : "stcCallback"+transId,
16363 scriptId : "stcScript"+transId,
16367 callback : callback,
16373 window[trans.cb] = function(o){
16374 conn.handleResponse(o, trans);
16377 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16379 if(this.autoAbort !== false){
16383 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16385 var script = document.createElement("script");
16386 script.setAttribute("src", url);
16387 script.setAttribute("type", "text/javascript");
16388 script.setAttribute("id", trans.scriptId);
16389 this.head.appendChild(script);
16391 this.trans = trans;
16393 callback.call(scope||this, null, arg, false);
16398 isLoading : function(){
16399 return this.trans ? true : false;
16403 * Abort the current server request.
16405 abort : function(){
16406 if(this.isLoading()){
16407 this.destroyTrans(this.trans);
16412 destroyTrans : function(trans, isLoaded){
16413 this.head.removeChild(document.getElementById(trans.scriptId));
16414 clearTimeout(trans.timeoutId);
16416 window[trans.cb] = undefined;
16418 delete window[trans.cb];
16421 // if hasn't been loaded, wait for load to remove it to prevent script error
16422 window[trans.cb] = function(){
16423 window[trans.cb] = undefined;
16425 delete window[trans.cb];
16432 handleResponse : function(o, trans){
16433 this.trans = false;
16434 this.destroyTrans(trans, true);
16437 result = trans.reader.readRecords(o);
16439 this.fireEvent("loadexception", this, o, trans.arg, e);
16440 trans.callback.call(trans.scope||window, null, trans.arg, false);
16443 this.fireEvent("load", this, o, trans.arg);
16444 trans.callback.call(trans.scope||window, result, trans.arg, true);
16448 handleFailure : function(trans){
16449 this.trans = false;
16450 this.destroyTrans(trans, false);
16451 this.fireEvent("loadexception", this, null, trans.arg);
16452 trans.callback.call(trans.scope||window, null, trans.arg, false);
16456 * Ext JS Library 1.1.1
16457 * Copyright(c) 2006-2007, Ext JS, LLC.
16459 * Originally Released Under LGPL - original licence link has changed is not relivant.
16462 * <script type="text/javascript">
16466 * @class Roo.data.JsonReader
16467 * @extends Roo.data.DataReader
16468 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16469 * based on mappings in a provided Roo.data.Record constructor.
16471 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16472 * in the reply previously.
16477 var RecordDef = Roo.data.Record.create([
16478 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16479 {name: 'occupation'} // This field will use "occupation" as the mapping.
16481 var myReader = new Roo.data.JsonReader({
16482 totalProperty: "results", // The property which contains the total dataset size (optional)
16483 root: "rows", // The property which contains an Array of row objects
16484 id: "id" // The property within each row object that provides an ID for the record (optional)
16488 * This would consume a JSON file like this:
16490 { 'results': 2, 'rows': [
16491 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16492 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16495 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16496 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16497 * paged from the remote server.
16498 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16499 * @cfg {String} root name of the property which contains the Array of row objects.
16500 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16501 * @cfg {Array} fields Array of field definition objects
16503 * Create a new JsonReader
16504 * @param {Object} meta Metadata configuration options
16505 * @param {Object} recordType Either an Array of field definition objects,
16506 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16508 Roo.data.JsonReader = function(meta, recordType){
16511 // set some defaults:
16512 Roo.applyIf(meta, {
16513 totalProperty: 'total',
16514 successProperty : 'success',
16519 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16521 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16523 readerType : 'Json',
16526 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16527 * Used by Store query builder to append _requestMeta to params.
16530 metaFromRemote : false,
16532 * This method is only used by a DataProxy which has retrieved data from a remote server.
16533 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16534 * @return {Object} data A data block which is used by an Roo.data.Store object as
16535 * a cache of Roo.data.Records.
16537 read : function(response){
16538 var json = response.responseText;
16540 var o = /* eval:var:o */ eval("("+json+")");
16542 throw {message: "JsonReader.read: Json object not found"};
16548 this.metaFromRemote = true;
16549 this.meta = o.metaData;
16550 this.recordType = Roo.data.Record.create(o.metaData.fields);
16551 this.onMetaChange(this.meta, this.recordType, o);
16553 return this.readRecords(o);
16556 // private function a store will implement
16557 onMetaChange : function(meta, recordType, o){
16564 simpleAccess: function(obj, subsc) {
16571 getJsonAccessor: function(){
16573 return function(expr) {
16575 return(re.test(expr))
16576 ? new Function("obj", "return obj." + expr)
16581 return Roo.emptyFn;
16586 * Create a data block containing Roo.data.Records from an XML document.
16587 * @param {Object} o An object which contains an Array of row objects in the property specified
16588 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16589 * which contains the total size of the dataset.
16590 * @return {Object} data A data block which is used by an Roo.data.Store object as
16591 * a cache of Roo.data.Records.
16593 readRecords : function(o){
16595 * After any data loads, the raw JSON data is available for further custom processing.
16599 var s = this.meta, Record = this.recordType,
16600 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16602 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16604 if(s.totalProperty) {
16605 this.getTotal = this.getJsonAccessor(s.totalProperty);
16607 if(s.successProperty) {
16608 this.getSuccess = this.getJsonAccessor(s.successProperty);
16610 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16612 var g = this.getJsonAccessor(s.id);
16613 this.getId = function(rec) {
16615 return (r === undefined || r === "") ? null : r;
16618 this.getId = function(){return null;};
16621 for(var jj = 0; jj < fl; jj++){
16623 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16624 this.ef[jj] = this.getJsonAccessor(map);
16628 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16629 if(s.totalProperty){
16630 var vt = parseInt(this.getTotal(o), 10);
16635 if(s.successProperty){
16636 var vs = this.getSuccess(o);
16637 if(vs === false || vs === 'false'){
16642 for(var i = 0; i < c; i++){
16645 var id = this.getId(n);
16646 for(var j = 0; j < fl; j++){
16648 var v = this.ef[j](n);
16650 Roo.log('missing convert for ' + f.name);
16654 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16656 var record = new Record(values, id);
16658 records[i] = record;
16664 totalRecords : totalRecords
16667 // used when loading children.. @see loadDataFromChildren
16668 toLoadData: function(rec)
16670 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16671 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16672 return { data : data, total : data.length };
16677 * Ext JS Library 1.1.1
16678 * Copyright(c) 2006-2007, Ext JS, LLC.
16680 * Originally Released Under LGPL - original licence link has changed is not relivant.
16683 * <script type="text/javascript">
16687 * @class Roo.data.ArrayReader
16688 * @extends Roo.data.DataReader
16689 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16690 * Each element of that Array represents a row of data fields. The
16691 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16692 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16696 var RecordDef = Roo.data.Record.create([
16697 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16698 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16700 var myReader = new Roo.data.ArrayReader({
16701 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16705 * This would consume an Array like this:
16707 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16711 * Create a new JsonReader
16712 * @param {Object} meta Metadata configuration options.
16713 * @param {Object|Array} recordType Either an Array of field definition objects
16715 * @cfg {Array} fields Array of field definition objects
16716 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16717 * as specified to {@link Roo.data.Record#create},
16718 * or an {@link Roo.data.Record} object
16721 * created using {@link Roo.data.Record#create}.
16723 Roo.data.ArrayReader = function(meta, recordType)
16725 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16728 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16731 * Create a data block containing Roo.data.Records from an XML document.
16732 * @param {Object} o An Array of row objects which represents the dataset.
16733 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16734 * a cache of Roo.data.Records.
16736 readRecords : function(o)
16738 var sid = this.meta ? this.meta.id : null;
16739 var recordType = this.recordType, fields = recordType.prototype.fields;
16742 for(var i = 0; i < root.length; i++){
16745 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16746 for(var j = 0, jlen = fields.length; j < jlen; j++){
16747 var f = fields.items[j];
16748 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16749 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16751 values[f.name] = v;
16753 var record = new recordType(values, id);
16755 records[records.length] = record;
16759 totalRecords : records.length
16762 // used when loading children.. @see loadDataFromChildren
16763 toLoadData: function(rec)
16765 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16766 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16777 * @class Roo.bootstrap.form.ComboBox
16778 * @extends Roo.bootstrap.form.TriggerField
16779 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16780 * @cfg {Boolean} append (true|false) default false
16781 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16782 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16783 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16784 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16785 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16786 * @cfg {Boolean} animate default true
16787 * @cfg {Boolean} emptyResultText only for touch device
16788 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16789 * @cfg {String} emptyTitle default ''
16790 * @cfg {Number} width fixed with? experimental
16792 * Create a new ComboBox.
16793 * @param {Object} config Configuration options
16795 Roo.bootstrap.form.ComboBox = function(config){
16796 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16800 * Fires when the dropdown list is expanded
16801 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16806 * Fires when the dropdown list is collapsed
16807 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16811 * @event beforeselect
16812 * Fires before a list item is selected. Return false to cancel the selection.
16813 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16814 * @param {Roo.data.Record} record The data record returned from the underlying store
16815 * @param {Number} index The index of the selected item in the dropdown list
16817 'beforeselect' : true,
16820 * Fires when a list item is selected
16821 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16822 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16823 * @param {Number} index The index of the selected item in the dropdown list
16827 * @event beforequery
16828 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16829 * The event object passed has these properties:
16830 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16831 * @param {String} query The query
16832 * @param {Boolean} forceAll true to force "all" query
16833 * @param {Boolean} cancel true to cancel the query
16834 * @param {Object} e The query event object
16836 'beforequery': true,
16839 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16840 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16845 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16846 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16847 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16852 * Fires when the remove value from the combobox array
16853 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857 * @event afterremove
16858 * Fires when the remove value from the combobox array
16859 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861 'afterremove' : true,
16863 * @event specialfilter
16864 * Fires when specialfilter
16865 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867 'specialfilter' : true,
16870 * Fires when tick the element
16871 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875 * @event touchviewdisplay
16876 * Fires when touch view require special display (default is using displayField)
16877 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878 * @param {Object} cfg set html .
16880 'touchviewdisplay' : true
16885 this.tickItems = [];
16887 this.selectedIndex = -1;
16888 if(this.mode == 'local'){
16889 if(config.queryDelay === undefined){
16890 this.queryDelay = 10;
16892 if(config.minChars === undefined){
16898 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16901 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16902 * rendering into an Roo.Editor, defaults to false)
16905 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16906 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16909 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16912 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16913 * the dropdown list (defaults to undefined, with no header element)
16917 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16921 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16923 listWidth: undefined,
16925 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16926 * mode = 'remote' or 'text' if mode = 'local')
16928 displayField: undefined,
16931 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16932 * mode = 'remote' or 'value' if mode = 'local').
16933 * Note: use of a valueField requires the user make a selection
16934 * in order for a value to be mapped.
16936 valueField: undefined,
16938 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16943 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16944 * field's data value (defaults to the underlying DOM element's name)
16946 hiddenName: undefined,
16948 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16952 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16954 selectedClass: 'active',
16957 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16961 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16962 * anchor positions (defaults to 'tl-bl')
16964 listAlign: 'tl-bl?',
16966 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16970 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16971 * query specified by the allQuery config option (defaults to 'query')
16973 triggerAction: 'query',
16975 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16976 * (defaults to 4, does not apply if editable = false)
16980 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16981 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16985 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16986 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16990 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16991 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16995 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16996 * when editable = true (defaults to false)
16998 selectOnFocus:false,
17000 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17002 queryParam: 'query',
17004 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17005 * when mode = 'remote' (defaults to 'Loading...')
17007 loadingText: 'Loading...',
17009 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17013 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17017 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17018 * traditional select (defaults to true)
17022 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17026 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17030 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17031 * listWidth has a higher value)
17035 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17036 * allow the user to set arbitrary text into the field (defaults to false)
17038 forceSelection:false,
17040 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17041 * if typeAhead = true (defaults to 250)
17043 typeAheadDelay : 250,
17045 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17046 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17048 valueNotFoundText : undefined,
17050 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17052 blockFocus : false,
17055 * @cfg {Boolean} disableClear Disable showing of clear button.
17057 disableClear : false,
17059 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17061 alwaysQuery : false,
17064 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17069 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17071 invalidClass : "has-warning",
17074 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17076 validClass : "has-success",
17079 * @cfg {Boolean} specialFilter (true|false) special filter default false
17081 specialFilter : false,
17084 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17086 mobileTouchView : true,
17089 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17091 useNativeIOS : false,
17094 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17096 mobile_restrict_height : false,
17098 ios_options : false,
17110 btnPosition : 'right',
17111 triggerList : true,
17112 showToggleBtn : true,
17114 emptyResultText: 'Empty',
17115 triggerText : 'Select',
17119 // element that contains real text value.. (when hidden is used..)
17121 getAutoCreate : function()
17126 * Render classic select for iso
17129 if(Roo.isIOS && this.useNativeIOS){
17130 cfg = this.getAutoCreateNativeIOS();
17138 if(Roo.isTouch && this.mobileTouchView){
17139 cfg = this.getAutoCreateTouchView();
17146 if(!this.tickable){
17147 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17152 * ComboBox with tickable selections
17155 var align = this.labelAlign || this.parentLabelAlign();
17158 cls : 'form-group roo-combobox-tickable' //input-group
17161 var btn_text_select = '';
17162 var btn_text_done = '';
17163 var btn_text_cancel = '';
17165 if (this.btn_text_show) {
17166 btn_text_select = 'Select';
17167 btn_text_done = 'Done';
17168 btn_text_cancel = 'Cancel';
17173 cls : 'tickable-buttons',
17178 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17179 //html : this.triggerText
17180 html: btn_text_select
17186 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17188 html: btn_text_done
17194 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17196 html: btn_text_cancel
17202 buttons.cn.unshift({
17204 cls: 'roo-select2-search-field-input'
17210 Roo.each(buttons.cn, function(c){
17212 c.cls += ' btn-' + _this.size;
17215 if (_this.disabled) {
17222 style : 'display: contents',
17227 cls: 'form-hidden-field'
17231 cls: 'roo-select2-choices',
17235 cls: 'roo-select2-search-field',
17246 cls: 'roo-select2-container input-group roo-select2-container-multi',
17252 // cls: 'typeahead typeahead-long dropdown-menu',
17253 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17258 if(this.hasFeedback && !this.allowBlank){
17262 cls: 'glyphicon form-control-feedback'
17265 combobox.cn.push(feedback);
17272 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17273 tooltip : 'This field is required'
17275 if (Roo.bootstrap.version == 4) {
17278 style : 'display:none'
17281 if (align ==='left' && this.fieldLabel.length) {
17283 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17290 cls : 'control-label col-form-label',
17291 html : this.fieldLabel
17303 var labelCfg = cfg.cn[1];
17304 var contentCfg = cfg.cn[2];
17307 if(this.indicatorpos == 'right'){
17313 cls : 'control-label col-form-label',
17317 html : this.fieldLabel
17333 labelCfg = cfg.cn[0];
17334 contentCfg = cfg.cn[1];
17338 if(this.labelWidth > 12){
17339 labelCfg.style = "width: " + this.labelWidth + 'px';
17341 if(this.width * 1 > 0){
17342 contentCfg.style = "width: " + this.width + 'px';
17344 if(this.labelWidth < 13 && this.labelmd == 0){
17345 this.labelmd = this.labelWidth;
17348 if(this.labellg > 0){
17349 labelCfg.cls += ' col-lg-' + this.labellg;
17350 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17353 if(this.labelmd > 0){
17354 labelCfg.cls += ' col-md-' + this.labelmd;
17355 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17358 if(this.labelsm > 0){
17359 labelCfg.cls += ' col-sm-' + this.labelsm;
17360 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17363 if(this.labelxs > 0){
17364 labelCfg.cls += ' col-xs-' + this.labelxs;
17365 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17369 } else if ( this.fieldLabel.length) {
17370 // Roo.log(" label");
17375 //cls : 'input-group-addon',
17376 html : this.fieldLabel
17381 if(this.indicatorpos == 'right'){
17385 //cls : 'input-group-addon',
17386 html : this.fieldLabel
17396 // Roo.log(" no label && no align");
17403 ['xs','sm','md','lg'].map(function(size){
17404 if (settings[size]) {
17405 cfg.cls += ' col-' + size + '-' + settings[size];
17413 _initEventsCalled : false,
17416 initEvents: function()
17418 if (this._initEventsCalled) { // as we call render... prevent looping...
17421 this._initEventsCalled = true;
17424 throw "can not find store for combo";
17427 this.indicator = this.indicatorEl();
17429 this.store = Roo.factory(this.store, Roo.data);
17430 this.store.parent = this;
17432 // if we are building from html. then this element is so complex, that we can not really
17433 // use the rendered HTML.
17434 // so we have to trash and replace the previous code.
17435 if (Roo.XComponent.build_from_html) {
17436 // remove this element....
17437 var e = this.el.dom, k=0;
17438 while (e ) { e = e.previousSibling; ++k;}
17443 this.rendered = false;
17445 this.render(this.parent().getChildContainer(true), k);
17448 if(Roo.isIOS && this.useNativeIOS){
17449 this.initIOSView();
17457 if(Roo.isTouch && this.mobileTouchView){
17458 this.initTouchView();
17463 this.initTickableEvents();
17467 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17469 if(this.hiddenName){
17471 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17473 this.hiddenField.dom.value =
17474 this.hiddenValue !== undefined ? this.hiddenValue :
17475 this.value !== undefined ? this.value : '';
17477 // prevent input submission
17478 this.el.dom.removeAttribute('name');
17479 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17484 // this.el.dom.setAttribute('autocomplete', 'off');
17487 var cls = 'x-combo-list';
17489 //this.list = new Roo.Layer({
17490 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17496 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17497 _this.list.setWidth(lw);
17500 this.list.on('mouseover', this.onViewOver, this);
17501 this.list.on('mousemove', this.onViewMove, this);
17502 this.list.on('scroll', this.onViewScroll, this);
17505 this.list.swallowEvent('mousewheel');
17506 this.assetHeight = 0;
17509 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17510 this.assetHeight += this.header.getHeight();
17513 this.innerList = this.list.createChild({cls:cls+'-inner'});
17514 this.innerList.on('mouseover', this.onViewOver, this);
17515 this.innerList.on('mousemove', this.onViewMove, this);
17516 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17518 if(this.allowBlank && !this.pageSize && !this.disableClear){
17519 this.footer = this.list.createChild({cls:cls+'-ft'});
17520 this.pageTb = new Roo.Toolbar(this.footer);
17524 this.footer = this.list.createChild({cls:cls+'-ft'});
17525 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17526 {pageSize: this.pageSize});
17530 if (this.pageTb && this.allowBlank && !this.disableClear) {
17532 this.pageTb.add(new Roo.Toolbar.Fill(), {
17533 cls: 'x-btn-icon x-btn-clear',
17535 handler: function()
17538 _this.clearValue();
17539 _this.onSelect(false, -1);
17544 this.assetHeight += this.footer.getHeight();
17549 this.tpl = Roo.bootstrap.version == 4 ?
17550 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17551 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17554 this.view = new Roo.View(this.list, this.tpl, {
17555 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17557 //this.view.wrapEl.setDisplayed(false);
17558 this.view.on('click', this.onViewClick, this);
17561 this.store.on('beforeload', this.onBeforeLoad, this);
17562 this.store.on('load', this.onLoad, this);
17563 this.store.on('loadexception', this.onLoadException, this);
17565 if(this.resizable){
17566 this.resizer = new Roo.Resizable(this.list, {
17567 pinned:true, handles:'se'
17569 this.resizer.on('resize', function(r, w, h){
17570 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17571 this.listWidth = w;
17572 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17573 this.restrictHeight();
17575 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17578 if(!this.editable){
17579 this.editable = true;
17580 this.setEditable(false);
17585 if (typeof(this.events.add.listeners) != 'undefined') {
17587 this.addicon = this.wrap.createChild(
17588 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17590 this.addicon.on('click', function(e) {
17591 this.fireEvent('add', this);
17594 if (typeof(this.events.edit.listeners) != 'undefined') {
17596 this.editicon = this.wrap.createChild(
17597 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17598 if (this.addicon) {
17599 this.editicon.setStyle('margin-left', '40px');
17601 this.editicon.on('click', function(e) {
17603 // we fire even if inothing is selected..
17604 this.fireEvent('edit', this, this.lastData );
17610 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17611 "up" : function(e){
17612 this.inKeyMode = true;
17616 "down" : function(e){
17617 if(!this.isExpanded()){
17618 this.onTriggerClick();
17620 this.inKeyMode = true;
17625 "enter" : function(e){
17626 // this.onViewClick();
17630 if(this.fireEvent("specialkey", this, e)){
17631 this.onViewClick(false);
17637 "esc" : function(e){
17641 "tab" : function(e){
17644 if(this.fireEvent("specialkey", this, e)){
17645 this.onViewClick(false);
17653 doRelay : function(foo, bar, hname){
17654 if(hname == 'down' || this.scope.isExpanded()){
17655 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17664 this.queryDelay = Math.max(this.queryDelay || 10,
17665 this.mode == 'local' ? 10 : 250);
17668 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17670 if(this.typeAhead){
17671 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17673 if(this.editable !== false){
17674 this.inputEl().on("keyup", this.onKeyUp, this);
17676 if(this.forceSelection){
17677 this.inputEl().on('blur', this.doForce, this);
17681 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17682 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17686 initTickableEvents: function()
17690 if(this.hiddenName){
17692 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17694 this.hiddenField.dom.value =
17695 this.hiddenValue !== undefined ? this.hiddenValue :
17696 this.value !== undefined ? this.value : '';
17698 // prevent input submission
17699 this.el.dom.removeAttribute('name');
17700 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17705 // this.list = this.el.select('ul.dropdown-menu',true).first();
17707 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17708 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17709 if(this.triggerList){
17710 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17713 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17714 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17716 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17717 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17719 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17720 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17722 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17723 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17724 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17727 this.cancelBtn.hide();
17732 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17733 _this.list.setWidth(lw);
17736 this.list.on('mouseover', this.onViewOver, this);
17737 this.list.on('mousemove', this.onViewMove, this);
17739 this.list.on('scroll', this.onViewScroll, this);
17742 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17743 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17746 this.view = new Roo.View(this.list, this.tpl, {
17751 selectedClass: this.selectedClass
17754 //this.view.wrapEl.setDisplayed(false);
17755 this.view.on('click', this.onViewClick, this);
17759 this.store.on('beforeload', this.onBeforeLoad, this);
17760 this.store.on('load', this.onLoad, this);
17761 this.store.on('loadexception', this.onLoadException, this);
17764 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17765 "up" : function(e){
17766 this.inKeyMode = true;
17770 "down" : function(e){
17771 this.inKeyMode = true;
17775 "enter" : function(e){
17776 if(this.fireEvent("specialkey", this, e)){
17777 this.onViewClick(false);
17783 "esc" : function(e){
17784 this.onTickableFooterButtonClick(e, false, false);
17787 "tab" : function(e){
17788 this.fireEvent("specialkey", this, e);
17790 this.onTickableFooterButtonClick(e, false, false);
17797 doRelay : function(e, fn, key){
17798 if(this.scope.isExpanded()){
17799 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17808 this.queryDelay = Math.max(this.queryDelay || 10,
17809 this.mode == 'local' ? 10 : 250);
17812 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17814 if(this.typeAhead){
17815 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17818 if(this.editable !== false){
17819 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17822 this.indicator = this.indicatorEl();
17824 if(this.indicator){
17825 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17826 this.indicator.hide();
17831 onDestroy : function(){
17833 this.view.setStore(null);
17834 this.view.el.removeAllListeners();
17835 this.view.el.remove();
17836 this.view.purgeListeners();
17839 this.list.dom.innerHTML = '';
17843 this.store.un('beforeload', this.onBeforeLoad, this);
17844 this.store.un('load', this.onLoad, this);
17845 this.store.un('loadexception', this.onLoadException, this);
17847 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17851 fireKey : function(e){
17852 if(e.isNavKeyPress() && !this.list.isVisible()){
17853 this.fireEvent("specialkey", this, e);
17858 onResize: function(w, h)
17862 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17864 // if(typeof w != 'number'){
17865 // // we do not handle it!?!?
17868 // var tw = this.trigger.getWidth();
17869 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17870 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17872 // this.inputEl().setWidth( this.adjustWidth('input', x));
17874 // //this.trigger.setStyle('left', x+'px');
17876 // if(this.list && this.listWidth === undefined){
17877 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17878 // this.list.setWidth(lw);
17879 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17887 * Allow or prevent the user from directly editing the field text. If false is passed,
17888 * the user will only be able to select from the items defined in the dropdown list. This method
17889 * is the runtime equivalent of setting the 'editable' config option at config time.
17890 * @param {Boolean} value True to allow the user to directly edit the field text
17892 setEditable : function(value){
17893 if(value == this.editable){
17896 this.editable = value;
17898 this.inputEl().dom.setAttribute('readOnly', true);
17899 this.inputEl().on('mousedown', this.onTriggerClick, this);
17900 this.inputEl().addClass('x-combo-noedit');
17902 this.inputEl().dom.removeAttribute('readOnly');
17903 this.inputEl().un('mousedown', this.onTriggerClick, this);
17904 this.inputEl().removeClass('x-combo-noedit');
17910 onBeforeLoad : function(combo,opts){
17911 if(!this.hasFocus){
17915 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17917 this.restrictHeight();
17918 this.selectedIndex = -1;
17922 onLoad : function(){
17924 this.hasQuery = false;
17926 if(!this.hasFocus){
17930 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17931 this.loading.hide();
17934 if(this.store.getCount() > 0){
17937 this.restrictHeight();
17938 if(this.lastQuery == this.allQuery){
17939 if(this.editable && !this.tickable){
17940 this.inputEl().dom.select();
17944 !this.selectByValue(this.value, true) &&
17947 !this.store.lastOptions ||
17948 typeof(this.store.lastOptions.add) == 'undefined' ||
17949 this.store.lastOptions.add != true
17952 this.select(0, true);
17955 if(this.autoFocus){
17958 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17959 this.taTask.delay(this.typeAheadDelay);
17963 this.onEmptyResults();
17969 onLoadException : function()
17971 this.hasQuery = false;
17973 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17974 this.loading.hide();
17977 if(this.tickable && this.editable){
17982 // only causes errors at present
17983 //Roo.log(this.store.reader.jsonData);
17984 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17986 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17992 onTypeAhead : function(){
17993 if(this.store.getCount() > 0){
17994 var r = this.store.getAt(0);
17995 var newValue = r.data[this.displayField];
17996 var len = newValue.length;
17997 var selStart = this.getRawValue().length;
17999 if(selStart != len){
18000 this.setRawValue(newValue);
18001 this.selectText(selStart, newValue.length);
18007 onSelect : function(record, index){
18009 if(this.fireEvent('beforeselect', this, record, index) !== false){
18011 this.setFromData(index > -1 ? record.data : false);
18014 this.fireEvent('select', this, record, index);
18019 * Returns the currently selected field value or empty string if no value is set.
18020 * @return {String} value The selected value
18022 getValue : function()
18024 if(Roo.isIOS && this.useNativeIOS){
18025 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18029 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18032 if(this.valueField){
18033 return typeof this.value != 'undefined' ? this.value : '';
18035 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18039 getRawValue : function()
18041 if(Roo.isIOS && this.useNativeIOS){
18042 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18045 var v = this.inputEl().getValue();
18051 * Clears any text/value currently set in the field
18053 clearValue : function(){
18055 if(this.hiddenField){
18056 this.hiddenField.dom.value = '';
18059 this.setRawValue('');
18060 this.lastSelectionText = '';
18061 this.lastData = false;
18063 var close = this.closeTriggerEl();
18074 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18075 * will be displayed in the field. If the value does not match the data value of an existing item,
18076 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18077 * Otherwise the field will be blank (although the value will still be set).
18078 * @param {String} value The value to match
18080 setValue : function(v)
18082 if(Roo.isIOS && this.useNativeIOS){
18083 this.setIOSValue(v);
18093 if(this.valueField){
18094 var r = this.findRecord(this.valueField, v);
18096 text = r.data[this.displayField];
18097 }else if(this.valueNotFoundText !== undefined){
18098 text = this.valueNotFoundText;
18101 this.lastSelectionText = text;
18102 if(this.hiddenField){
18103 this.hiddenField.dom.value = v;
18105 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18108 var close = this.closeTriggerEl();
18111 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18117 * @property {Object} the last set data for the element
18122 * Sets the value of the field based on a object which is related to the record format for the store.
18123 * @param {Object} value the value to set as. or false on reset?
18125 setFromData : function(o){
18132 var dv = ''; // display value
18133 var vv = ''; // value value..
18135 if (this.displayField) {
18136 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18138 // this is an error condition!!!
18139 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18142 if(this.valueField){
18143 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18146 var close = this.closeTriggerEl();
18149 if(dv.length || vv * 1 > 0){
18151 this.blockFocus=true;
18157 if(this.hiddenField){
18158 this.hiddenField.dom.value = vv;
18160 this.lastSelectionText = dv;
18161 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18165 // no hidden field.. - we store the value in 'value', but still display
18166 // display field!!!!
18167 this.lastSelectionText = dv;
18168 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18175 reset : function(){
18176 // overridden so that last data is reset..
18183 this.setValue(this.originalValue);
18184 //this.clearInvalid();
18185 this.lastData = false;
18187 this.view.clearSelections();
18193 findRecord : function(prop, value){
18195 if(this.store.getCount() > 0){
18196 this.store.each(function(r){
18197 if(r.data[prop] == value){
18207 getName: function()
18209 // returns hidden if it's set..
18210 if (!this.rendered) {return ''};
18211 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18215 onViewMove : function(e, t){
18216 this.inKeyMode = false;
18220 onViewOver : function(e, t){
18221 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18224 var item = this.view.findItemFromChild(t);
18227 var index = this.view.indexOf(item);
18228 this.select(index, false);
18233 onViewClick : function(view, doFocus, el, e)
18235 var index = this.view.getSelectedIndexes()[0];
18237 var r = this.store.getAt(index);
18241 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18248 Roo.each(this.tickItems, function(v,k){
18250 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18252 _this.tickItems.splice(k, 1);
18254 if(typeof(e) == 'undefined' && view == false){
18255 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18267 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18268 this.tickItems.push(r.data);
18271 if(typeof(e) == 'undefined' && view == false){
18272 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18279 this.onSelect(r, index);
18281 if(doFocus !== false && !this.blockFocus){
18282 this.inputEl().focus();
18287 restrictHeight : function(){
18288 //this.innerList.dom.style.height = '';
18289 //var inner = this.innerList.dom;
18290 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18291 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18292 //this.list.beginUpdate();
18293 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18294 this.list.alignTo(this.inputEl(), this.listAlign);
18295 this.list.alignTo(this.inputEl(), this.listAlign);
18296 //this.list.endUpdate();
18300 onEmptyResults : function(){
18302 if(this.tickable && this.editable){
18303 this.hasFocus = false;
18304 this.restrictHeight();
18312 * Returns true if the dropdown list is expanded, else false.
18314 isExpanded : function(){
18315 return this.list.isVisible();
18319 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18320 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18321 * @param {String} value The data value of the item to select
18322 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18323 * selected item if it is not currently in view (defaults to true)
18324 * @return {Boolean} True if the value matched an item in the list, else false
18326 selectByValue : function(v, scrollIntoView){
18327 if(v !== undefined && v !== null){
18328 var r = this.findRecord(this.valueField || this.displayField, v);
18330 this.select(this.store.indexOf(r), scrollIntoView);
18338 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18339 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18340 * @param {Number} index The zero-based index of the list item to select
18341 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18342 * selected item if it is not currently in view (defaults to true)
18344 select : function(index, scrollIntoView){
18345 this.selectedIndex = index;
18346 this.view.select(index);
18347 if(scrollIntoView !== false){
18348 var el = this.view.getNode(index);
18350 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18353 this.list.scrollChildIntoView(el, false);
18359 selectNext : function(){
18360 var ct = this.store.getCount();
18362 if(this.selectedIndex == -1){
18364 }else if(this.selectedIndex < ct-1){
18365 this.select(this.selectedIndex+1);
18371 selectPrev : function(){
18372 var ct = this.store.getCount();
18374 if(this.selectedIndex == -1){
18376 }else if(this.selectedIndex != 0){
18377 this.select(this.selectedIndex-1);
18383 onKeyUp : function(e){
18384 if(this.editable !== false && !e.isSpecialKey()){
18385 this.lastKey = e.getKey();
18386 this.dqTask.delay(this.queryDelay);
18391 validateBlur : function(){
18392 return !this.list || !this.list.isVisible();
18396 initQuery : function(){
18398 var v = this.getRawValue();
18400 if(this.tickable && this.editable){
18401 v = this.tickableInputEl().getValue();
18408 doForce : function(){
18409 if(this.inputEl().dom.value.length > 0){
18410 this.inputEl().dom.value =
18411 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18417 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18418 * query allowing the query action to be canceled if needed.
18419 * @param {String} query The SQL query to execute
18420 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18421 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18422 * saved in the current store (defaults to false)
18424 doQuery : function(q, forceAll){
18426 if(q === undefined || q === null){
18431 forceAll: forceAll,
18435 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18440 forceAll = qe.forceAll;
18441 if(forceAll === true || (q.length >= this.minChars)){
18443 this.hasQuery = true;
18445 if(this.lastQuery != q || this.alwaysQuery){
18446 this.lastQuery = q;
18447 if(this.mode == 'local'){
18448 this.selectedIndex = -1;
18450 this.store.clearFilter();
18453 if(this.specialFilter){
18454 this.fireEvent('specialfilter', this);
18459 this.store.filter(this.displayField, q);
18462 this.store.fireEvent("datachanged", this.store);
18469 this.store.baseParams[this.queryParam] = q;
18471 var options = {params : this.getParams(q)};
18474 options.add = true;
18475 options.params.start = this.page * this.pageSize;
18478 this.store.load(options);
18481 * this code will make the page width larger, at the beginning, the list not align correctly,
18482 * we should expand the list on onLoad
18483 * so command out it
18488 this.selectedIndex = -1;
18493 this.loadNext = false;
18497 getParams : function(q){
18499 //p[this.queryParam] = q;
18503 p.limit = this.pageSize;
18509 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18511 collapse : function(){
18512 if(!this.isExpanded()){
18518 this.hasFocus = false;
18522 this.cancelBtn.hide();
18523 this.trigger.show();
18526 this.tickableInputEl().dom.value = '';
18527 this.tickableInputEl().blur();
18532 Roo.get(document).un('mousedown', this.collapseIf, this);
18533 Roo.get(document).un('mousewheel', this.collapseIf, this);
18534 if (!this.editable) {
18535 Roo.get(document).un('keydown', this.listKeyPress, this);
18537 this.fireEvent('collapse', this);
18543 collapseIf : function(e){
18544 var in_combo = e.within(this.el);
18545 var in_list = e.within(this.list);
18546 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18548 if (in_combo || in_list || is_list) {
18549 //e.stopPropagation();
18554 this.onTickableFooterButtonClick(e, false, false);
18562 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18564 expand : function(){
18566 if(this.isExpanded() || !this.hasFocus){
18570 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18571 this.list.setWidth(lw);
18577 this.restrictHeight();
18581 this.tickItems = Roo.apply([], this.item);
18584 this.cancelBtn.show();
18585 this.trigger.hide();
18588 this.tickableInputEl().focus();
18593 Roo.get(document).on('mousedown', this.collapseIf, this);
18594 Roo.get(document).on('mousewheel', this.collapseIf, this);
18595 if (!this.editable) {
18596 Roo.get(document).on('keydown', this.listKeyPress, this);
18599 this.fireEvent('expand', this);
18603 // Implements the default empty TriggerField.onTriggerClick function
18604 onTriggerClick : function(e)
18606 Roo.log('trigger click');
18608 if(this.disabled || !this.triggerList){
18613 this.loadNext = false;
18615 if(this.isExpanded()){
18617 if (!this.blockFocus) {
18618 this.inputEl().focus();
18622 this.hasFocus = true;
18623 if(this.triggerAction == 'all') {
18624 this.doQuery(this.allQuery, true);
18626 this.doQuery(this.getRawValue());
18628 if (!this.blockFocus) {
18629 this.inputEl().focus();
18634 onTickableTriggerClick : function(e)
18641 this.loadNext = false;
18642 this.hasFocus = true;
18644 if(this.triggerAction == 'all') {
18645 this.doQuery(this.allQuery, true);
18647 this.doQuery(this.getRawValue());
18651 onSearchFieldClick : function(e)
18653 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18654 this.onTickableFooterButtonClick(e, false, false);
18658 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18663 this.loadNext = false;
18664 this.hasFocus = true;
18666 if(this.triggerAction == 'all') {
18667 this.doQuery(this.allQuery, true);
18669 this.doQuery(this.getRawValue());
18673 listKeyPress : function(e)
18675 //Roo.log('listkeypress');
18676 // scroll to first matching element based on key pres..
18677 if (e.isSpecialKey()) {
18680 var k = String.fromCharCode(e.getKey()).toUpperCase();
18683 var csel = this.view.getSelectedNodes();
18684 var cselitem = false;
18686 var ix = this.view.indexOf(csel[0]);
18687 cselitem = this.store.getAt(ix);
18688 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18694 this.store.each(function(v) {
18696 // start at existing selection.
18697 if (cselitem.id == v.id) {
18703 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18704 match = this.store.indexOf(v);
18710 if (match === false) {
18711 return true; // no more action?
18714 this.view.select(match);
18715 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18716 sn.scrollIntoView(sn.dom.parentNode, false);
18719 onViewScroll : function(e, t){
18721 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){
18725 this.hasQuery = true;
18727 this.loading = this.list.select('.loading', true).first();
18729 if(this.loading === null){
18730 this.list.createChild({
18732 cls: 'loading roo-select2-more-results roo-select2-active',
18733 html: 'Loading more results...'
18736 this.loading = this.list.select('.loading', true).first();
18738 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18740 this.loading.hide();
18743 this.loading.show();
18748 this.loadNext = true;
18750 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18755 addItem : function(o)
18757 var dv = ''; // display value
18759 if (this.displayField) {
18760 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18762 // this is an error condition!!!
18763 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18770 var choice = this.choices.createChild({
18772 cls: 'roo-select2-search-choice',
18781 cls: 'roo-select2-search-choice-close fa fa-times',
18786 }, this.searchField);
18788 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18790 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18798 this.inputEl().dom.value = '';
18803 onRemoveItem : function(e, _self, o)
18805 e.preventDefault();
18807 this.lastItem = Roo.apply([], this.item);
18809 var index = this.item.indexOf(o.data) * 1;
18812 Roo.log('not this item?!');
18816 this.item.splice(index, 1);
18821 this.fireEvent('remove', this, e);
18827 syncValue : function()
18829 if(!this.item.length){
18836 Roo.each(this.item, function(i){
18837 if(_this.valueField){
18838 value.push(i[_this.valueField]);
18845 this.value = value.join(',');
18847 if(this.hiddenField){
18848 this.hiddenField.dom.value = this.value;
18851 this.store.fireEvent("datachanged", this.store);
18856 clearItem : function()
18858 if(!this.multiple){
18864 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18872 if(this.tickable && !Roo.isTouch){
18873 this.view.refresh();
18877 inputEl: function ()
18879 if(Roo.isIOS && this.useNativeIOS){
18880 return this.el.select('select.roo-ios-select', true).first();
18883 if(Roo.isTouch && this.mobileTouchView){
18884 return this.el.select('input.form-control',true).first();
18888 return this.searchField;
18891 return this.el.select('input.form-control',true).first();
18894 onTickableFooterButtonClick : function(e, btn, el)
18896 e.preventDefault();
18898 this.lastItem = Roo.apply([], this.item);
18900 if(btn && btn.name == 'cancel'){
18901 this.tickItems = Roo.apply([], this.item);
18910 Roo.each(this.tickItems, function(o){
18918 validate : function()
18920 if(this.getVisibilityEl().hasClass('hidden')){
18924 var v = this.getRawValue();
18927 v = this.getValue();
18930 if(this.disabled || this.allowBlank || v.length){
18935 this.markInvalid();
18939 tickableInputEl : function()
18941 if(!this.tickable || !this.editable){
18942 return this.inputEl();
18945 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18949 getAutoCreateTouchView : function()
18954 cls: 'form-group' //input-group
18960 type : this.inputType,
18961 cls : 'form-control x-combo-noedit',
18962 autocomplete: 'new-password',
18963 placeholder : this.placeholder || '',
18968 input.name = this.name;
18972 input.cls += ' input-' + this.size;
18975 if (this.disabled) {
18976 input.disabled = true;
18980 cls : 'roo-combobox-wrap',
18987 inputblock.cls += ' input-group';
18989 inputblock.cn.unshift({
18991 cls : 'input-group-addon input-group-prepend input-group-text',
18996 if(this.removable && !this.multiple){
18997 inputblock.cls += ' roo-removable';
18999 inputblock.cn.push({
19002 cls : 'roo-combo-removable-btn close'
19006 if(this.hasFeedback && !this.allowBlank){
19008 inputblock.cls += ' has-feedback';
19010 inputblock.cn.push({
19012 cls: 'glyphicon form-control-feedback'
19019 inputblock.cls += (this.before) ? '' : ' input-group';
19021 inputblock.cn.push({
19023 cls : 'input-group-addon input-group-append input-group-text',
19029 var ibwrap = inputblock;
19034 cls: 'roo-select2-choices',
19038 cls: 'roo-select2-search-field',
19051 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19056 cls: 'form-hidden-field'
19062 if(!this.multiple && this.showToggleBtn){
19068 if (this.caret != false) {
19071 cls: 'fa fa-' + this.caret
19078 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19080 Roo.bootstrap.version == 3 ? caret : '',
19083 cls: 'combobox-clear',
19097 combobox.cls += ' roo-select2-container-multi';
19100 var required = this.allowBlank ? {
19102 style: 'display: none'
19105 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19106 tooltip : 'This field is required'
19109 var align = this.labelAlign || this.parentLabelAlign();
19111 if (align ==='left' && this.fieldLabel.length) {
19117 cls : 'control-label col-form-label',
19118 html : this.fieldLabel
19122 cls : 'roo-combobox-wrap ',
19129 var labelCfg = cfg.cn[1];
19130 var contentCfg = cfg.cn[2];
19133 if(this.indicatorpos == 'right'){
19138 cls : 'control-label col-form-label',
19142 html : this.fieldLabel
19148 cls : "roo-combobox-wrap ",
19156 labelCfg = cfg.cn[0];
19157 contentCfg = cfg.cn[1];
19162 if(this.labelWidth > 12){
19163 labelCfg.style = "width: " + this.labelWidth + 'px';
19166 if(this.labelWidth < 13 && this.labelmd == 0){
19167 this.labelmd = this.labelWidth;
19170 if(this.labellg > 0){
19171 labelCfg.cls += ' col-lg-' + this.labellg;
19172 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19175 if(this.labelmd > 0){
19176 labelCfg.cls += ' col-md-' + this.labelmd;
19177 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19180 if(this.labelsm > 0){
19181 labelCfg.cls += ' col-sm-' + this.labelsm;
19182 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19185 if(this.labelxs > 0){
19186 labelCfg.cls += ' col-xs-' + this.labelxs;
19187 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19191 } else if ( this.fieldLabel.length) {
19196 cls : 'control-label',
19197 html : this.fieldLabel
19208 if(this.indicatorpos == 'right'){
19212 cls : 'control-label',
19213 html : this.fieldLabel,
19231 var settings = this;
19233 ['xs','sm','md','lg'].map(function(size){
19234 if (settings[size]) {
19235 cfg.cls += ' col-' + size + '-' + settings[size];
19242 initTouchView : function()
19244 this.renderTouchView();
19246 this.touchViewEl.on('scroll', function(){
19247 this.el.dom.scrollTop = 0;
19250 this.originalValue = this.getValue();
19252 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19254 this.inputEl().on("click", this.showTouchView, this);
19255 if (this.triggerEl) {
19256 this.triggerEl.on("click", this.showTouchView, this);
19260 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19261 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19263 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19265 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19266 this.store.on('load', this.onTouchViewLoad, this);
19267 this.store.on('loadexception', this.onTouchViewLoadException, this);
19269 if(this.hiddenName){
19271 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19273 this.hiddenField.dom.value =
19274 this.hiddenValue !== undefined ? this.hiddenValue :
19275 this.value !== undefined ? this.value : '';
19277 this.el.dom.removeAttribute('name');
19278 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19282 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19283 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19286 if(this.removable && !this.multiple){
19287 var close = this.closeTriggerEl();
19289 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19290 close.on('click', this.removeBtnClick, this, close);
19294 * fix the bug in Safari iOS8
19296 this.inputEl().on("focus", function(e){
19297 document.activeElement.blur();
19300 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19307 renderTouchView : function()
19309 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19310 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19312 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19313 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19315 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19316 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19317 this.touchViewBodyEl.setStyle('overflow', 'auto');
19319 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19320 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19322 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19323 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327 showTouchView : function()
19333 this.touchViewHeaderEl.hide();
19335 if(this.modalTitle.length){
19336 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19337 this.touchViewHeaderEl.show();
19340 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19341 this.touchViewEl.show();
19343 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19345 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19346 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19348 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19350 if(this.modalTitle.length){
19351 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19354 this.touchViewBodyEl.setHeight(bodyHeight);
19358 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19360 this.touchViewEl.addClass(['in','show']);
19363 if(this._touchViewMask){
19364 Roo.get(document.body).addClass("x-body-masked");
19365 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19366 this._touchViewMask.setStyle('z-index', 10000);
19367 this._touchViewMask.addClass('show');
19370 this.doTouchViewQuery();
19374 hideTouchView : function()
19376 this.touchViewEl.removeClass(['in','show']);
19380 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19382 this.touchViewEl.setStyle('display', 'none');
19385 if(this._touchViewMask){
19386 this._touchViewMask.removeClass('show');
19387 Roo.get(document.body).removeClass("x-body-masked");
19391 setTouchViewValue : function()
19398 Roo.each(this.tickItems, function(o){
19403 this.hideTouchView();
19406 doTouchViewQuery : function()
19415 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19419 if(!this.alwaysQuery || this.mode == 'local'){
19420 this.onTouchViewLoad();
19427 onTouchViewBeforeLoad : function(combo,opts)
19433 onTouchViewLoad : function()
19435 if(this.store.getCount() < 1){
19436 this.onTouchViewEmptyResults();
19440 this.clearTouchView();
19442 var rawValue = this.getRawValue();
19444 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19446 this.tickItems = [];
19448 this.store.data.each(function(d, rowIndex){
19449 var row = this.touchViewListGroup.createChild(template);
19451 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19452 row.addClass(d.data.cls);
19455 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19458 html : d.data[this.displayField]
19461 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19462 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19465 row.removeClass('selected');
19466 if(!this.multiple && this.valueField &&
19467 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19470 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19471 row.addClass('selected');
19474 if(this.multiple && this.valueField &&
19475 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19479 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19480 this.tickItems.push(d.data);
19483 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19487 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19489 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19491 if(this.modalTitle.length){
19492 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19495 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19497 if(this.mobile_restrict_height && listHeight < bodyHeight){
19498 this.touchViewBodyEl.setHeight(listHeight);
19503 if(firstChecked && listHeight > bodyHeight){
19504 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19509 onTouchViewLoadException : function()
19511 this.hideTouchView();
19514 onTouchViewEmptyResults : function()
19516 this.clearTouchView();
19518 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19520 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19524 clearTouchView : function()
19526 this.touchViewListGroup.dom.innerHTML = '';
19529 onTouchViewClick : function(e, el, o)
19531 e.preventDefault();
19534 var rowIndex = o.rowIndex;
19536 var r = this.store.getAt(rowIndex);
19538 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19540 if(!this.multiple){
19541 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19542 c.dom.removeAttribute('checked');
19545 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19547 this.setFromData(r.data);
19549 var close = this.closeTriggerEl();
19555 this.hideTouchView();
19557 this.fireEvent('select', this, r, rowIndex);
19562 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19563 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19564 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19568 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19569 this.addItem(r.data);
19570 this.tickItems.push(r.data);
19574 getAutoCreateNativeIOS : function()
19577 cls: 'form-group' //input-group,
19582 cls : 'roo-ios-select'
19586 combobox.name = this.name;
19589 if (this.disabled) {
19590 combobox.disabled = true;
19593 var settings = this;
19595 ['xs','sm','md','lg'].map(function(size){
19596 if (settings[size]) {
19597 cfg.cls += ' col-' + size + '-' + settings[size];
19607 initIOSView : function()
19609 this.store.on('load', this.onIOSViewLoad, this);
19614 onIOSViewLoad : function()
19616 if(this.store.getCount() < 1){
19620 this.clearIOSView();
19622 if(this.allowBlank) {
19624 var default_text = '-- SELECT --';
19626 if(this.placeholder.length){
19627 default_text = this.placeholder;
19630 if(this.emptyTitle.length){
19631 default_text += ' - ' + this.emptyTitle + ' -';
19634 var opt = this.inputEl().createChild({
19637 html : default_text
19641 o[this.valueField] = 0;
19642 o[this.displayField] = default_text;
19644 this.ios_options.push({
19651 this.store.data.each(function(d, rowIndex){
19655 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19656 html = d.data[this.displayField];
19661 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19662 value = d.data[this.valueField];
19671 if(this.value == d.data[this.valueField]){
19672 option['selected'] = true;
19675 var opt = this.inputEl().createChild(option);
19677 this.ios_options.push({
19684 this.inputEl().on('change', function(){
19685 this.fireEvent('select', this);
19690 clearIOSView: function()
19692 this.inputEl().dom.innerHTML = '';
19694 this.ios_options = [];
19697 setIOSValue: function(v)
19701 if(!this.ios_options){
19705 Roo.each(this.ios_options, function(opts){
19707 opts.el.dom.removeAttribute('selected');
19709 if(opts.data[this.valueField] != v){
19713 opts.el.dom.setAttribute('selected', true);
19719 * @cfg {Boolean} grow
19723 * @cfg {Number} growMin
19727 * @cfg {Number} growMax
19736 Roo.apply(Roo.bootstrap.form.ComboBox, {
19740 cls: 'modal-header',
19762 cls: 'list-group-item',
19766 cls: 'roo-combobox-list-group-item-value'
19770 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19784 listItemCheckbox : {
19786 cls: 'list-group-item',
19790 cls: 'roo-combobox-list-group-item-value'
19794 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19810 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19815 cls: 'modal-footer',
19823 cls: 'col-xs-6 text-left',
19826 cls: 'btn btn-danger roo-touch-view-cancel',
19832 cls: 'col-xs-6 text-right',
19835 cls: 'btn btn-success roo-touch-view-ok',
19846 Roo.apply(Roo.bootstrap.form.ComboBox, {
19848 touchViewTemplate : {
19850 cls: 'modal fade roo-combobox-touch-view',
19854 cls: 'modal-dialog',
19855 style : 'position:fixed', // we have to fix position....
19859 cls: 'modal-content',
19861 Roo.bootstrap.form.ComboBox.header,
19862 Roo.bootstrap.form.ComboBox.body,
19863 Roo.bootstrap.form.ComboBox.footer
19872 * Ext JS Library 1.1.1
19873 * Copyright(c) 2006-2007, Ext JS, LLC.
19875 * Originally Released Under LGPL - original licence link has changed is not relivant.
19878 * <script type="text/javascript">
19883 * @extends Roo.util.Observable
19884 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19885 * This class also supports single and multi selection modes. <br>
19886 * Create a data model bound view:
19888 var store = new Roo.data.Store(...);
19890 var view = new Roo.View({
19892 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19894 singleSelect: true,
19895 selectedClass: "ydataview-selected",
19899 // listen for node click?
19900 view.on("click", function(vw, index, node, e){
19901 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19905 dataModel.load("foobar.xml");
19907 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19909 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19910 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19912 * Note: old style constructor is still suported (container, template, config)
19915 * Create a new View
19916 * @param {Object} config The config object
19919 Roo.View = function(config, depreciated_tpl, depreciated_config){
19921 this.parent = false;
19923 if (typeof(depreciated_tpl) == 'undefined') {
19924 // new way.. - universal constructor.
19925 Roo.apply(this, config);
19926 this.el = Roo.get(this.el);
19929 this.el = Roo.get(config);
19930 this.tpl = depreciated_tpl;
19931 Roo.apply(this, depreciated_config);
19933 this.wrapEl = this.el.wrap().wrap();
19934 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19937 if(typeof(this.tpl) == "string"){
19938 this.tpl = new Roo.Template(this.tpl);
19940 // support xtype ctors..
19941 this.tpl = new Roo.factory(this.tpl, Roo);
19945 this.tpl.compile();
19950 * @event beforeclick
19951 * Fires before a click is processed. Returns false to cancel the default action.
19952 * @param {Roo.View} this
19953 * @param {Number} index The index of the target node
19954 * @param {HTMLElement} node The target node
19955 * @param {Roo.EventObject} e The raw event object
19957 "beforeclick" : true,
19960 * Fires when a template node is clicked.
19961 * @param {Roo.View} this
19962 * @param {Number} index The index of the target node
19963 * @param {HTMLElement} node The target node
19964 * @param {Roo.EventObject} e The raw event object
19969 * Fires when a template node is double clicked.
19970 * @param {Roo.View} this
19971 * @param {Number} index The index of the target node
19972 * @param {HTMLElement} node The target node
19973 * @param {Roo.EventObject} e The raw event object
19977 * @event contextmenu
19978 * Fires when a template node is right clicked.
19979 * @param {Roo.View} this
19980 * @param {Number} index The index of the target node
19981 * @param {HTMLElement} node The target node
19982 * @param {Roo.EventObject} e The raw event object
19984 "contextmenu" : true,
19986 * @event selectionchange
19987 * Fires when the selected nodes change.
19988 * @param {Roo.View} this
19989 * @param {Array} selections Array of the selected nodes
19991 "selectionchange" : true,
19994 * @event beforeselect
19995 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19996 * @param {Roo.View} this
19997 * @param {HTMLElement} node The node to be selected
19998 * @param {Array} selections Array of currently selected nodes
20000 "beforeselect" : true,
20002 * @event preparedata
20003 * Fires on every row to render, to allow you to change the data.
20004 * @param {Roo.View} this
20005 * @param {Object} data to be rendered (change this)
20007 "preparedata" : true
20015 "click": this.onClick,
20016 "dblclick": this.onDblClick,
20017 "contextmenu": this.onContextMenu,
20021 this.selections = [];
20023 this.cmp = new Roo.CompositeElementLite([]);
20025 this.store = Roo.factory(this.store, Roo.data);
20026 this.setStore(this.store, true);
20029 if ( this.footer && this.footer.xtype) {
20031 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20033 this.footer.dataSource = this.store;
20034 this.footer.container = fctr;
20035 this.footer = Roo.factory(this.footer, Roo);
20036 fctr.insertFirst(this.el);
20038 // this is a bit insane - as the paging toolbar seems to detach the el..
20039 // dom.parentNode.parentNode.parentNode
20040 // they get detached?
20044 Roo.View.superclass.constructor.call(this);
20049 Roo.extend(Roo.View, Roo.util.Observable, {
20052 * @cfg {Roo.data.Store} store Data store to load data from.
20057 * @cfg {String|Roo.Element} el The container element.
20062 * @cfg {String|Roo.Template} tpl The template used by this View
20066 * @cfg {String} dataName the named area of the template to use as the data area
20067 * Works with domtemplates roo-name="name"
20071 * @cfg {String} selectedClass The css class to add to selected nodes
20073 selectedClass : "x-view-selected",
20075 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20080 * @cfg {String} text to display on mask (default Loading)
20084 * @cfg {Boolean} multiSelect Allow multiple selection
20086 multiSelect : false,
20088 * @cfg {Boolean} singleSelect Allow single selection
20090 singleSelect: false,
20093 * @cfg {Boolean} toggleSelect - selecting
20095 toggleSelect : false,
20098 * @cfg {Boolean} tickable - selecting
20103 * Returns the element this view is bound to.
20104 * @return {Roo.Element}
20106 getEl : function(){
20107 return this.wrapEl;
20113 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20115 refresh : function(){
20116 //Roo.log('refresh');
20119 // if we are using something like 'domtemplate', then
20120 // the what gets used is:
20121 // t.applySubtemplate(NAME, data, wrapping data..)
20122 // the outer template then get' applied with
20123 // the store 'extra data'
20124 // and the body get's added to the
20125 // roo-name="data" node?
20126 // <span class='roo-tpl-{name}'></span> ?????
20130 this.clearSelections();
20131 this.el.update("");
20133 var records = this.store.getRange();
20134 if(records.length < 1) {
20136 // is this valid?? = should it render a template??
20138 this.el.update(this.emptyText);
20142 if (this.dataName) {
20143 this.el.update(t.apply(this.store.meta)); //????
20144 el = this.el.child('.roo-tpl-' + this.dataName);
20147 for(var i = 0, len = records.length; i < len; i++){
20148 var data = this.prepareData(records[i].data, i, records[i]);
20149 this.fireEvent("preparedata", this, data, i, records[i]);
20151 var d = Roo.apply({}, data);
20154 Roo.apply(d, {'roo-id' : Roo.id()});
20158 Roo.each(this.parent.item, function(item){
20159 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20162 Roo.apply(d, {'roo-data-checked' : 'checked'});
20166 html[html.length] = Roo.util.Format.trim(
20168 t.applySubtemplate(this.dataName, d, this.store.meta) :
20175 el.update(html.join(""));
20176 this.nodes = el.dom.childNodes;
20177 this.updateIndexes(0);
20182 * Function to override to reformat the data that is sent to
20183 * the template for each node.
20184 * DEPRICATED - use the preparedata event handler.
20185 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20186 * a JSON object for an UpdateManager bound view).
20188 prepareData : function(data, index, record)
20190 this.fireEvent("preparedata", this, data, index, record);
20194 onUpdate : function(ds, record){
20195 // Roo.log('on update');
20196 this.clearSelections();
20197 var index = this.store.indexOf(record);
20198 var n = this.nodes[index];
20199 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20200 n.parentNode.removeChild(n);
20201 this.updateIndexes(index, index);
20207 onAdd : function(ds, records, index)
20209 //Roo.log(['on Add', ds, records, index] );
20210 this.clearSelections();
20211 if(this.nodes.length == 0){
20215 var n = this.nodes[index];
20216 for(var i = 0, len = records.length; i < len; i++){
20217 var d = this.prepareData(records[i].data, i, records[i]);
20219 this.tpl.insertBefore(n, d);
20222 this.tpl.append(this.el, d);
20225 this.updateIndexes(index);
20228 onRemove : function(ds, record, index){
20229 // Roo.log('onRemove');
20230 this.clearSelections();
20231 var el = this.dataName ?
20232 this.el.child('.roo-tpl-' + this.dataName) :
20235 el.dom.removeChild(this.nodes[index]);
20236 this.updateIndexes(index);
20240 * Refresh an individual node.
20241 * @param {Number} index
20243 refreshNode : function(index){
20244 this.onUpdate(this.store, this.store.getAt(index));
20247 updateIndexes : function(startIndex, endIndex){
20248 var ns = this.nodes;
20249 startIndex = startIndex || 0;
20250 endIndex = endIndex || ns.length - 1;
20251 for(var i = startIndex; i <= endIndex; i++){
20252 ns[i].nodeIndex = i;
20257 * Changes the data store this view uses and refresh the view.
20258 * @param {Store} store
20260 setStore : function(store, initial){
20261 if(!initial && this.store){
20262 this.store.un("datachanged", this.refresh);
20263 this.store.un("add", this.onAdd);
20264 this.store.un("remove", this.onRemove);
20265 this.store.un("update", this.onUpdate);
20266 this.store.un("clear", this.refresh);
20267 this.store.un("beforeload", this.onBeforeLoad);
20268 this.store.un("load", this.onLoad);
20269 this.store.un("loadexception", this.onLoad);
20273 store.on("datachanged", this.refresh, this);
20274 store.on("add", this.onAdd, this);
20275 store.on("remove", this.onRemove, this);
20276 store.on("update", this.onUpdate, this);
20277 store.on("clear", this.refresh, this);
20278 store.on("beforeload", this.onBeforeLoad, this);
20279 store.on("load", this.onLoad, this);
20280 store.on("loadexception", this.onLoad, this);
20288 * onbeforeLoad - masks the loading area.
20291 onBeforeLoad : function(store,opts)
20293 //Roo.log('onBeforeLoad');
20295 this.el.update("");
20297 this.el.mask(this.mask ? this.mask : "Loading" );
20299 onLoad : function ()
20306 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20307 * @param {HTMLElement} node
20308 * @return {HTMLElement} The template node
20310 findItemFromChild : function(node){
20311 var el = this.dataName ?
20312 this.el.child('.roo-tpl-' + this.dataName,true) :
20315 if(!node || node.parentNode == el){
20318 var p = node.parentNode;
20319 while(p && p != el){
20320 if(p.parentNode == el){
20329 onClick : function(e){
20330 var item = this.findItemFromChild(e.getTarget());
20332 var index = this.indexOf(item);
20333 if(this.onItemClick(item, index, e) !== false){
20334 this.fireEvent("click", this, index, item, e);
20337 this.clearSelections();
20342 onContextMenu : function(e){
20343 var item = this.findItemFromChild(e.getTarget());
20345 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20350 onDblClick : function(e){
20351 var item = this.findItemFromChild(e.getTarget());
20353 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20357 onItemClick : function(item, index, e)
20359 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20362 if (this.toggleSelect) {
20363 var m = this.isSelected(item) ? 'unselect' : 'select';
20366 _t[m](item, true, false);
20369 if(this.multiSelect || this.singleSelect){
20370 if(this.multiSelect && e.shiftKey && this.lastSelection){
20371 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20373 this.select(item, this.multiSelect && e.ctrlKey);
20374 this.lastSelection = item;
20377 if(!this.tickable){
20378 e.preventDefault();
20386 * Get the number of selected nodes.
20389 getSelectionCount : function(){
20390 return this.selections.length;
20394 * Get the currently selected nodes.
20395 * @return {Array} An array of HTMLElements
20397 getSelectedNodes : function(){
20398 return this.selections;
20402 * Get the indexes of the selected nodes.
20405 getSelectedIndexes : function(){
20406 var indexes = [], s = this.selections;
20407 for(var i = 0, len = s.length; i < len; i++){
20408 indexes.push(s[i].nodeIndex);
20414 * Clear all selections
20415 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20417 clearSelections : function(suppressEvent){
20418 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20419 this.cmp.elements = this.selections;
20420 this.cmp.removeClass(this.selectedClass);
20421 this.selections = [];
20422 if(!suppressEvent){
20423 this.fireEvent("selectionchange", this, this.selections);
20429 * Returns true if the passed node is selected
20430 * @param {HTMLElement/Number} node The node or node index
20431 * @return {Boolean}
20433 isSelected : function(node){
20434 var s = this.selections;
20438 node = this.getNode(node);
20439 return s.indexOf(node) !== -1;
20444 * @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
20445 * @param {Boolean} keepExisting (optional) true to keep existing selections
20446 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20448 select : function(nodeInfo, keepExisting, suppressEvent){
20449 if(nodeInfo instanceof Array){
20451 this.clearSelections(true);
20453 for(var i = 0, len = nodeInfo.length; i < len; i++){
20454 this.select(nodeInfo[i], true, true);
20458 var node = this.getNode(nodeInfo);
20459 if(!node || this.isSelected(node)){
20460 return; // already selected.
20463 this.clearSelections(true);
20466 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20467 Roo.fly(node).addClass(this.selectedClass);
20468 this.selections.push(node);
20469 if(!suppressEvent){
20470 this.fireEvent("selectionchange", this, this.selections);
20478 * @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
20479 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20480 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20482 unselect : function(nodeInfo, keepExisting, suppressEvent)
20484 if(nodeInfo instanceof Array){
20485 Roo.each(this.selections, function(s) {
20486 this.unselect(s, nodeInfo);
20490 var node = this.getNode(nodeInfo);
20491 if(!node || !this.isSelected(node)){
20492 //Roo.log("not selected");
20493 return; // not selected.
20497 Roo.each(this.selections, function(s) {
20499 Roo.fly(node).removeClass(this.selectedClass);
20506 this.selections= ns;
20507 this.fireEvent("selectionchange", this, this.selections);
20511 * Gets a template node.
20512 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20513 * @return {HTMLElement} The node or null if it wasn't found
20515 getNode : function(nodeInfo){
20516 if(typeof nodeInfo == "string"){
20517 return document.getElementById(nodeInfo);
20518 }else if(typeof nodeInfo == "number"){
20519 return this.nodes[nodeInfo];
20525 * Gets a range template nodes.
20526 * @param {Number} startIndex
20527 * @param {Number} endIndex
20528 * @return {Array} An array of nodes
20530 getNodes : function(start, end){
20531 var ns = this.nodes;
20532 start = start || 0;
20533 end = typeof end == "undefined" ? ns.length - 1 : end;
20536 for(var i = start; i <= end; i++){
20540 for(var i = start; i >= end; i--){
20548 * Finds the index of the passed node
20549 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20550 * @return {Number} The index of the node or -1
20552 indexOf : function(node){
20553 node = this.getNode(node);
20554 if(typeof node.nodeIndex == "number"){
20555 return node.nodeIndex;
20557 var ns = this.nodes;
20558 for(var i = 0, len = ns.length; i < len; i++){
20569 * based on jquery fullcalendar
20573 Roo.bootstrap = Roo.bootstrap || {};
20575 * @class Roo.bootstrap.Calendar
20576 * @extends Roo.bootstrap.Component
20577 * Bootstrap Calendar class
20578 * @cfg {Boolean} loadMask (true|false) default false
20579 * @cfg {Object} header generate the user specific header of the calendar, default false
20582 * Create a new Container
20583 * @param {Object} config The config object
20588 Roo.bootstrap.Calendar = function(config){
20589 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20593 * Fires when a date is selected
20594 * @param {DatePicker} this
20595 * @param {Date} date The selected date
20599 * @event monthchange
20600 * Fires when the displayed month changes
20601 * @param {DatePicker} this
20602 * @param {Date} date The selected month
20604 'monthchange': true,
20606 * @event evententer
20607 * Fires when mouse over an event
20608 * @param {Calendar} this
20609 * @param {event} Event
20611 'evententer': true,
20613 * @event eventleave
20614 * Fires when the mouse leaves an
20615 * @param {Calendar} this
20618 'eventleave': true,
20620 * @event eventclick
20621 * Fires when the mouse click an
20622 * @param {Calendar} this
20631 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20634 * @cfg {Roo.data.Store} store
20635 * The data source for the calendar
20639 * @cfg {Number} startDay
20640 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20648 getAutoCreate : function(){
20651 var fc_button = function(name, corner, style, content ) {
20652 return Roo.apply({},{
20654 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20656 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20659 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20670 style : 'width:100%',
20677 cls : 'fc-header-left',
20679 fc_button('prev', 'left', 'arrow', '‹' ),
20680 fc_button('next', 'right', 'arrow', '›' ),
20681 { tag: 'span', cls: 'fc-header-space' },
20682 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20690 cls : 'fc-header-center',
20694 cls: 'fc-header-title',
20697 html : 'month / year'
20705 cls : 'fc-header-right',
20707 /* fc_button('month', 'left', '', 'month' ),
20708 fc_button('week', '', '', 'week' ),
20709 fc_button('day', 'right', '', 'day' )
20721 header = this.header;
20724 var cal_heads = function() {
20726 // fixme - handle this.
20728 for (var i =0; i < Date.dayNames.length; i++) {
20729 var d = Date.dayNames[i];
20732 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20733 html : d.substring(0,3)
20737 ret[0].cls += ' fc-first';
20738 ret[6].cls += ' fc-last';
20741 var cal_cell = function(n) {
20744 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20749 cls: 'fc-day-number',
20753 cls: 'fc-day-content',
20757 style: 'position: relative;' // height: 17px;
20769 var cal_rows = function() {
20772 for (var r = 0; r < 6; r++) {
20779 for (var i =0; i < Date.dayNames.length; i++) {
20780 var d = Date.dayNames[i];
20781 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20784 row.cn[0].cls+=' fc-first';
20785 row.cn[0].cn[0].style = 'min-height:90px';
20786 row.cn[6].cls+=' fc-last';
20790 ret[0].cls += ' fc-first';
20791 ret[4].cls += ' fc-prev-last';
20792 ret[5].cls += ' fc-last';
20799 cls: 'fc-border-separate',
20800 style : 'width:100%',
20808 cls : 'fc-first fc-last',
20826 cls : 'fc-content',
20827 style : "position: relative;",
20830 cls : 'fc-view fc-view-month fc-grid',
20831 style : 'position: relative',
20832 unselectable : 'on',
20835 cls : 'fc-event-container',
20836 style : 'position:absolute;z-index:8;top:0;left:0;'
20854 initEvents : function()
20857 throw "can not find store for calendar";
20863 style: "text-align:center",
20867 style: "background-color:white;width:50%;margin:250 auto",
20871 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20882 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20884 var size = this.el.select('.fc-content', true).first().getSize();
20885 this.maskEl.setSize(size.width, size.height);
20886 this.maskEl.enableDisplayMode("block");
20887 if(!this.loadMask){
20888 this.maskEl.hide();
20891 this.store = Roo.factory(this.store, Roo.data);
20892 this.store.on('load', this.onLoad, this);
20893 this.store.on('beforeload', this.onBeforeLoad, this);
20897 this.cells = this.el.select('.fc-day',true);
20898 //Roo.log(this.cells);
20899 this.textNodes = this.el.query('.fc-day-number');
20900 this.cells.addClassOnOver('fc-state-hover');
20902 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20903 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20904 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20905 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20907 this.on('monthchange', this.onMonthChange, this);
20909 this.update(new Date().clearTime());
20912 resize : function() {
20913 var sz = this.el.getSize();
20915 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20916 this.el.select('.fc-day-content div',true).setHeight(34);
20921 showPrevMonth : function(e){
20922 this.update(this.activeDate.add("mo", -1));
20924 showToday : function(e){
20925 this.update(new Date().clearTime());
20928 showNextMonth : function(e){
20929 this.update(this.activeDate.add("mo", 1));
20933 showPrevYear : function(){
20934 this.update(this.activeDate.add("y", -1));
20938 showNextYear : function(){
20939 this.update(this.activeDate.add("y", 1));
20944 update : function(date)
20946 var vd = this.activeDate;
20947 this.activeDate = date;
20948 // if(vd && this.el){
20949 // var t = date.getTime();
20950 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20951 // Roo.log('using add remove');
20953 // this.fireEvent('monthchange', this, date);
20955 // this.cells.removeClass("fc-state-highlight");
20956 // this.cells.each(function(c){
20957 // if(c.dateValue == t){
20958 // c.addClass("fc-state-highlight");
20959 // setTimeout(function(){
20960 // try{c.dom.firstChild.focus();}catch(e){}
20970 var days = date.getDaysInMonth();
20972 var firstOfMonth = date.getFirstDateOfMonth();
20973 var startingPos = firstOfMonth.getDay()-this.startDay;
20975 if(startingPos < this.startDay){
20979 var pm = date.add(Date.MONTH, -1);
20980 var prevStart = pm.getDaysInMonth()-startingPos;
20982 this.cells = this.el.select('.fc-day',true);
20983 this.textNodes = this.el.query('.fc-day-number');
20984 this.cells.addClassOnOver('fc-state-hover');
20986 var cells = this.cells.elements;
20987 var textEls = this.textNodes;
20989 Roo.each(cells, function(cell){
20990 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20993 days += startingPos;
20995 // convert everything to numbers so it's fast
20996 var day = 86400000;
20997 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21000 //Roo.log(prevStart);
21002 var today = new Date().clearTime().getTime();
21003 var sel = date.clearTime().getTime();
21004 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21005 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21006 var ddMatch = this.disabledDatesRE;
21007 var ddText = this.disabledDatesText;
21008 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21009 var ddaysText = this.disabledDaysText;
21010 var format = this.format;
21012 var setCellClass = function(cal, cell){
21016 //Roo.log('set Cell Class');
21018 var t = d.getTime();
21022 cell.dateValue = t;
21024 cell.className += " fc-today";
21025 cell.className += " fc-state-highlight";
21026 cell.title = cal.todayText;
21029 // disable highlight in other month..
21030 //cell.className += " fc-state-highlight";
21035 cell.className = " fc-state-disabled";
21036 cell.title = cal.minText;
21040 cell.className = " fc-state-disabled";
21041 cell.title = cal.maxText;
21045 if(ddays.indexOf(d.getDay()) != -1){
21046 cell.title = ddaysText;
21047 cell.className = " fc-state-disabled";
21050 if(ddMatch && format){
21051 var fvalue = d.dateFormat(format);
21052 if(ddMatch.test(fvalue)){
21053 cell.title = ddText.replace("%0", fvalue);
21054 cell.className = " fc-state-disabled";
21058 if (!cell.initialClassName) {
21059 cell.initialClassName = cell.dom.className;
21062 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21067 for(; i < startingPos; i++) {
21068 textEls[i].innerHTML = (++prevStart);
21069 d.setDate(d.getDate()+1);
21071 cells[i].className = "fc-past fc-other-month";
21072 setCellClass(this, cells[i]);
21077 for(; i < days; i++){
21078 intDay = i - startingPos + 1;
21079 textEls[i].innerHTML = (intDay);
21080 d.setDate(d.getDate()+1);
21082 cells[i].className = ''; // "x-date-active";
21083 setCellClass(this, cells[i]);
21087 for(; i < 42; i++) {
21088 textEls[i].innerHTML = (++extraDays);
21089 d.setDate(d.getDate()+1);
21091 cells[i].className = "fc-future fc-other-month";
21092 setCellClass(this, cells[i]);
21095 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21097 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21099 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21100 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21102 if(totalRows != 6){
21103 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21104 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21107 this.fireEvent('monthchange', this, date);
21111 if(!this.internalRender){
21112 var main = this.el.dom.firstChild;
21113 var w = main.offsetWidth;
21114 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21115 Roo.fly(main).setWidth(w);
21116 this.internalRender = true;
21117 // opera does not respect the auto grow header center column
21118 // then, after it gets a width opera refuses to recalculate
21119 // without a second pass
21120 if(Roo.isOpera && !this.secondPass){
21121 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21122 this.secondPass = true;
21123 this.update.defer(10, this, [date]);
21130 findCell : function(dt) {
21131 dt = dt.clearTime().getTime();
21133 this.cells.each(function(c){
21134 //Roo.log("check " +c.dateValue + '?=' + dt);
21135 if(c.dateValue == dt){
21145 findCells : function(ev) {
21146 var s = ev.start.clone().clearTime().getTime();
21148 var e= ev.end.clone().clearTime().getTime();
21151 this.cells.each(function(c){
21152 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21154 if(c.dateValue > e){
21157 if(c.dateValue < s){
21166 // findBestRow: function(cells)
21170 // for (var i =0 ; i < cells.length;i++) {
21171 // ret = Math.max(cells[i].rows || 0,ret);
21178 addItem : function(ev)
21180 // look for vertical location slot in
21181 var cells = this.findCells(ev);
21183 // ev.row = this.findBestRow(cells);
21185 // work out the location.
21189 for(var i =0; i < cells.length; i++) {
21191 cells[i].row = cells[0].row;
21194 cells[i].row = cells[i].row + 1;
21204 if (crow.start.getY() == cells[i].getY()) {
21206 crow.end = cells[i];
21223 cells[0].events.push(ev);
21225 this.calevents.push(ev);
21228 clearEvents: function() {
21230 if(!this.calevents){
21234 Roo.each(this.cells.elements, function(c){
21240 Roo.each(this.calevents, function(e) {
21241 Roo.each(e.els, function(el) {
21242 el.un('mouseenter' ,this.onEventEnter, this);
21243 el.un('mouseleave' ,this.onEventLeave, this);
21248 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21254 renderEvents: function()
21258 this.cells.each(function(c) {
21267 if(c.row != c.events.length){
21268 r = 4 - (4 - (c.row - c.events.length));
21271 c.events = ev.slice(0, r);
21272 c.more = ev.slice(r);
21274 if(c.more.length && c.more.length == 1){
21275 c.events.push(c.more.pop());
21278 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21282 this.cells.each(function(c) {
21284 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21287 for (var e = 0; e < c.events.length; e++){
21288 var ev = c.events[e];
21289 var rows = ev.rows;
21291 for(var i = 0; i < rows.length; i++) {
21293 // how many rows should it span..
21296 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21297 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21299 unselectable : "on",
21302 cls: 'fc-event-inner',
21306 // cls: 'fc-event-time',
21307 // html : cells.length > 1 ? '' : ev.time
21311 cls: 'fc-event-title',
21312 html : String.format('{0}', ev.title)
21319 cls: 'ui-resizable-handle ui-resizable-e',
21320 html : '  '
21327 cfg.cls += ' fc-event-start';
21329 if ((i+1) == rows.length) {
21330 cfg.cls += ' fc-event-end';
21333 var ctr = _this.el.select('.fc-event-container',true).first();
21334 var cg = ctr.createChild(cfg);
21336 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21337 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21339 var r = (c.more.length) ? 1 : 0;
21340 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21341 cg.setWidth(ebox.right - sbox.x -2);
21343 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21344 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21345 cg.on('click', _this.onEventClick, _this, ev);
21356 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21357 style : 'position: absolute',
21358 unselectable : "on",
21361 cls: 'fc-event-inner',
21365 cls: 'fc-event-title',
21373 cls: 'ui-resizable-handle ui-resizable-e',
21374 html : '  '
21380 var ctr = _this.el.select('.fc-event-container',true).first();
21381 var cg = ctr.createChild(cfg);
21383 var sbox = c.select('.fc-day-content',true).first().getBox();
21384 var ebox = c.select('.fc-day-content',true).first().getBox();
21386 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21387 cg.setWidth(ebox.right - sbox.x -2);
21389 cg.on('click', _this.onMoreEventClick, _this, c.more);
21399 onEventEnter: function (e, el,event,d) {
21400 this.fireEvent('evententer', this, el, event);
21403 onEventLeave: function (e, el,event,d) {
21404 this.fireEvent('eventleave', this, el, event);
21407 onEventClick: function (e, el,event,d) {
21408 this.fireEvent('eventclick', this, el, event);
21411 onMonthChange: function () {
21415 onMoreEventClick: function(e, el, more)
21419 this.calpopover.placement = 'right';
21420 this.calpopover.setTitle('More');
21422 this.calpopover.setContent('');
21424 var ctr = this.calpopover.el.select('.popover-content', true).first();
21426 Roo.each(more, function(m){
21428 cls : 'fc-event-hori fc-event-draggable',
21431 var cg = ctr.createChild(cfg);
21433 cg.on('click', _this.onEventClick, _this, m);
21436 this.calpopover.show(el);
21441 onLoad: function ()
21443 this.calevents = [];
21446 if(this.store.getCount() > 0){
21447 this.store.data.each(function(d){
21450 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21451 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21452 time : d.data.start_time,
21453 title : d.data.title,
21454 description : d.data.description,
21455 venue : d.data.venue
21460 this.renderEvents();
21462 if(this.calevents.length && this.loadMask){
21463 this.maskEl.hide();
21467 onBeforeLoad: function()
21469 this.clearEvents();
21471 this.maskEl.show();
21485 * @class Roo.bootstrap.Popover
21486 * @extends Roo.bootstrap.Component
21488 * @children Roo.bootstrap.Component
21489 * Bootstrap Popover class
21490 * @cfg {String} html contents of the popover (or false to use children..)
21491 * @cfg {String} title of popover (or false to hide)
21492 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21493 * @cfg {String} trigger click || hover (or false to trigger manually)
21494 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21495 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21496 * - if false and it has a 'parent' then it will be automatically added to that element
21497 * - if string - Roo.get will be called
21498 * @cfg {Number} delay - delay before showing
21501 * Create a new Popover
21502 * @param {Object} config The config object
21505 Roo.bootstrap.Popover = function(config){
21506 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21512 * After the popover show
21514 * @param {Roo.bootstrap.Popover} this
21519 * After the popover hide
21521 * @param {Roo.bootstrap.Popover} this
21527 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21532 placement : 'right',
21533 trigger : 'hover', // hover
21539 can_build_overlaid : false,
21541 maskEl : false, // the mask element
21544 alignEl : false, // when show is called with an element - this get's stored.
21546 getChildContainer : function()
21548 return this.contentEl;
21551 getPopoverHeader : function()
21553 this.title = true; // flag not to hide it..
21554 this.headerEl.addClass('p-0');
21555 return this.headerEl
21559 getAutoCreate : function(){
21562 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21563 style: 'display:block',
21569 cls : 'popover-inner ',
21573 cls: 'popover-title popover-header',
21574 html : this.title === false ? '' : this.title
21577 cls : 'popover-content popover-body ' + (this.cls || ''),
21578 html : this.html || ''
21589 * @param {string} the title
21591 setTitle: function(str)
21595 this.headerEl.dom.innerHTML = str;
21600 * @param {string} the body content
21602 setContent: function(str)
21605 if (this.contentEl) {
21606 this.contentEl.dom.innerHTML = str;
21610 // as it get's added to the bottom of the page.
21611 onRender : function(ct, position)
21613 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21618 var cfg = Roo.apply({}, this.getAutoCreate());
21622 cfg.cls += ' ' + this.cls;
21625 cfg.style = this.style;
21627 //Roo.log("adding to ");
21628 this.el = Roo.get(document.body).createChild(cfg, position);
21629 // Roo.log(this.el);
21632 this.contentEl = this.el.select('.popover-content',true).first();
21633 this.headerEl = this.el.select('.popover-title',true).first();
21636 if(typeof(this.items) != 'undefined'){
21637 var items = this.items;
21640 for(var i =0;i < items.length;i++) {
21641 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21645 this.items = nitems;
21647 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21648 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21655 resizeMask : function()
21657 this.maskEl.setSize(
21658 Roo.lib.Dom.getViewWidth(true),
21659 Roo.lib.Dom.getViewHeight(true)
21663 initEvents : function()
21667 Roo.bootstrap.Popover.register(this);
21670 this.arrowEl = this.el.select('.arrow',true).first();
21671 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21672 this.el.enableDisplayMode('block');
21676 if (this.over === false && !this.parent()) {
21679 if (this.triggers === false) {
21684 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21685 var triggers = this.trigger ? this.trigger.split(' ') : [];
21686 Roo.each(triggers, function(trigger) {
21688 if (trigger == 'click') {
21689 on_el.on('click', this.toggle, this);
21690 } else if (trigger != 'manual') {
21691 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21692 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21694 on_el.on(eventIn ,this.enter, this);
21695 on_el.on(eventOut, this.leave, this);
21705 toggle : function () {
21706 this.hoverState == 'in' ? this.leave() : this.enter();
21709 enter : function () {
21711 clearTimeout(this.timeout);
21713 this.hoverState = 'in';
21715 if (!this.delay || !this.delay.show) {
21720 this.timeout = setTimeout(function () {
21721 if (_t.hoverState == 'in') {
21724 }, this.delay.show)
21727 leave : function() {
21728 clearTimeout(this.timeout);
21730 this.hoverState = 'out';
21732 if (!this.delay || !this.delay.hide) {
21737 this.timeout = setTimeout(function () {
21738 if (_t.hoverState == 'out') {
21741 }, this.delay.hide)
21745 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21746 * @param {string} (left|right|top|bottom) position
21748 show : function (on_el, placement)
21750 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21751 on_el = on_el || false; // default to false
21754 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21755 on_el = this.parent().el;
21756 } else if (this.over) {
21757 on_el = Roo.get(this.over);
21762 this.alignEl = Roo.get( on_el );
21765 this.render(document.body);
21771 if (this.title === false) {
21772 this.headerEl.hide();
21777 this.el.dom.style.display = 'block';
21780 if (this.alignEl) {
21781 this.updatePosition(this.placement, true);
21784 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21785 var es = this.el.getSize();
21786 var x = Roo.lib.Dom.getViewWidth()/2;
21787 var y = Roo.lib.Dom.getViewHeight()/2;
21788 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21793 //var arrow = this.el.select('.arrow',true).first();
21794 //arrow.set(align[2],
21796 this.el.addClass('in');
21800 this.hoverState = 'in';
21803 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21804 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21805 this.maskEl.dom.style.display = 'block';
21806 this.maskEl.addClass('show');
21808 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21810 this.fireEvent('show', this);
21814 * fire this manually after loading a grid in the table for example
21815 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21816 * @param {Boolean} try and move it if we cant get right position.
21818 updatePosition : function(placement, try_move)
21820 // allow for calling with no parameters
21821 placement = placement ? placement : this.placement;
21822 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21824 this.el.removeClass([
21825 'fade','top','bottom', 'left', 'right','in',
21826 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21828 this.el.addClass(placement + ' bs-popover-' + placement);
21830 if (!this.alignEl ) {
21834 switch (placement) {
21836 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21837 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21838 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21839 //normal display... or moved up/down.
21840 this.el.setXY(offset);
21841 var xy = this.alignEl.getAnchorXY('tr', false);
21843 this.arrowEl.setXY(xy);
21846 // continue through...
21847 return this.updatePosition('left', false);
21851 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21852 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21853 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21854 //normal display... or moved up/down.
21855 this.el.setXY(offset);
21856 var xy = this.alignEl.getAnchorXY('tl', false);
21857 xy[0]-=10;xy[1]+=5; // << fix me
21858 this.arrowEl.setXY(xy);
21862 return this.updatePosition('right', false);
21865 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21866 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21867 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21868 //normal display... or moved up/down.
21869 this.el.setXY(offset);
21870 var xy = this.alignEl.getAnchorXY('t', false);
21871 xy[1]-=10; // << fix me
21872 this.arrowEl.setXY(xy);
21876 return this.updatePosition('bottom', false);
21879 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21880 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21881 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21882 //normal display... or moved up/down.
21883 this.el.setXY(offset);
21884 var xy = this.alignEl.getAnchorXY('b', false);
21885 xy[1]+=2; // << fix me
21886 this.arrowEl.setXY(xy);
21890 return this.updatePosition('top', false);
21901 this.el.setXY([0,0]);
21902 this.el.removeClass('in');
21904 this.hoverState = null;
21905 this.maskEl.hide(); // always..
21906 this.fireEvent('hide', this);
21912 Roo.apply(Roo.bootstrap.Popover, {
21915 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21916 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21917 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21918 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21923 clickHander : false,
21927 onMouseDown : function(e)
21929 if (this.popups.length && !e.getTarget(".roo-popover")) {
21930 /// what is nothing is showing..
21939 register : function(popup)
21941 if (!Roo.bootstrap.Popover.clickHandler) {
21942 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21944 // hide other popups.
21945 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21946 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21947 this.hideAll(); //<< why?
21948 //this.popups.push(popup);
21950 hideAll : function()
21952 this.popups.forEach(function(p) {
21956 onShow : function() {
21957 Roo.bootstrap.Popover.popups.push(this);
21959 onHide : function() {
21960 Roo.bootstrap.Popover.popups.remove(this);
21966 * Card header - holder for the card header elements.
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
21975 * Bootstrap Popover header navigation class
21976 * FIXME? should this go under nav?
21980 * Create a new Popover Header Navigation
21981 * @param {Object} config The config object
21984 Roo.bootstrap.PopoverNav = function(config){
21985 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21988 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
21991 container_method : 'getPopoverHeader'
22009 * @class Roo.bootstrap.Progress
22010 * @extends Roo.bootstrap.Component
22011 * @children Roo.bootstrap.ProgressBar
22012 * Bootstrap Progress class
22013 * @cfg {Boolean} striped striped of the progress bar
22014 * @cfg {Boolean} active animated of the progress bar
22018 * Create a new Progress
22019 * @param {Object} config The config object
22022 Roo.bootstrap.Progress = function(config){
22023 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22026 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22031 getAutoCreate : function(){
22039 cfg.cls += ' progress-striped';
22043 cfg.cls += ' active';
22062 * @class Roo.bootstrap.ProgressBar
22063 * @extends Roo.bootstrap.Component
22064 * Bootstrap ProgressBar class
22065 * @cfg {Number} aria_valuenow aria-value now
22066 * @cfg {Number} aria_valuemin aria-value min
22067 * @cfg {Number} aria_valuemax aria-value max
22068 * @cfg {String} label label for the progress bar
22069 * @cfg {String} panel (success | info | warning | danger )
22070 * @cfg {String} role role of the progress bar
22071 * @cfg {String} sr_only text
22075 * Create a new ProgressBar
22076 * @param {Object} config The config object
22079 Roo.bootstrap.ProgressBar = function(config){
22080 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22083 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22087 aria_valuemax : 100,
22093 getAutoCreate : function()
22098 cls: 'progress-bar',
22099 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22111 cfg.role = this.role;
22114 if(this.aria_valuenow){
22115 cfg['aria-valuenow'] = this.aria_valuenow;
22118 if(this.aria_valuemin){
22119 cfg['aria-valuemin'] = this.aria_valuemin;
22122 if(this.aria_valuemax){
22123 cfg['aria-valuemax'] = this.aria_valuemax;
22126 if(this.label && !this.sr_only){
22127 cfg.html = this.label;
22131 cfg.cls += ' progress-bar-' + this.panel;
22137 update : function(aria_valuenow)
22139 this.aria_valuenow = aria_valuenow;
22141 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22149 * @class Roo.bootstrap.TabGroup
22150 * @extends Roo.bootstrap.Column
22151 * @children Roo.bootstrap.TabPanel
22152 * Bootstrap Column class
22153 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22154 * @cfg {Boolean} carousel true to make the group behave like a carousel
22155 * @cfg {Boolean} bullets show bullets for the panels
22156 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22157 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22158 * @cfg {Boolean} showarrow (true|false) show arrow default true
22161 * Create a new TabGroup
22162 * @param {Object} config The config object
22165 Roo.bootstrap.TabGroup = function(config){
22166 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22168 this.navId = Roo.id();
22171 Roo.bootstrap.TabGroup.register(this);
22175 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22178 transition : false,
22183 slideOnTouch : false,
22186 getAutoCreate : function()
22188 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22190 cfg.cls += ' tab-content';
22192 if (this.carousel) {
22193 cfg.cls += ' carousel slide';
22196 cls : 'carousel-inner',
22200 if(this.bullets && !Roo.isTouch){
22203 cls : 'carousel-bullets',
22207 if(this.bullets_cls){
22208 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22215 cfg.cn[0].cn.push(bullets);
22218 if(this.showarrow){
22219 cfg.cn[0].cn.push({
22221 class : 'carousel-arrow',
22225 class : 'carousel-prev',
22229 class : 'fa fa-chevron-left'
22235 class : 'carousel-next',
22239 class : 'fa fa-chevron-right'
22252 initEvents: function()
22254 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22255 // this.el.on("touchstart", this.onTouchStart, this);
22258 if(this.autoslide){
22261 this.slideFn = window.setInterval(function() {
22262 _this.showPanelNext();
22266 if(this.showarrow){
22267 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22268 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22274 // onTouchStart : function(e, el, o)
22276 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22280 // this.showPanelNext();
22284 getChildContainer : function()
22286 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22290 * register a Navigation item
22291 * @param {Roo.bootstrap.nav.Item} the navitem to add
22293 register : function(item)
22295 this.tabs.push( item);
22296 item.navId = this.navId; // not really needed..
22301 getActivePanel : function()
22304 Roo.each(this.tabs, function(t) {
22314 getPanelByName : function(n)
22317 Roo.each(this.tabs, function(t) {
22318 if (t.tabId == n) {
22326 indexOfPanel : function(p)
22329 Roo.each(this.tabs, function(t,i) {
22330 if (t.tabId == p.tabId) {
22339 * show a specific panel
22340 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22341 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22343 showPanel : function (pan)
22345 if(this.transition || typeof(pan) == 'undefined'){
22346 Roo.log("waiting for the transitionend");
22350 if (typeof(pan) == 'number') {
22351 pan = this.tabs[pan];
22354 if (typeof(pan) == 'string') {
22355 pan = this.getPanelByName(pan);
22358 var cur = this.getActivePanel();
22361 Roo.log('pan or acitve pan is undefined');
22365 if (pan.tabId == this.getActivePanel().tabId) {
22369 if (false === cur.fireEvent('beforedeactivate')) {
22373 if(this.bullets > 0 && !Roo.isTouch){
22374 this.setActiveBullet(this.indexOfPanel(pan));
22377 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22379 //class="carousel-item carousel-item-next carousel-item-left"
22381 this.transition = true;
22382 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22383 var lr = dir == 'next' ? 'left' : 'right';
22384 pan.el.addClass(dir); // or prev
22385 pan.el.addClass('carousel-item-' + dir); // or prev
22386 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22387 cur.el.addClass(lr); // or right
22388 pan.el.addClass(lr);
22389 cur.el.addClass('carousel-item-' +lr); // or right
22390 pan.el.addClass('carousel-item-' +lr);
22394 cur.el.on('transitionend', function() {
22395 Roo.log("trans end?");
22397 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22398 pan.setActive(true);
22400 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22401 cur.setActive(false);
22403 _this.transition = false;
22405 }, this, { single: true } );
22410 cur.setActive(false);
22411 pan.setActive(true);
22416 showPanelNext : function()
22418 var i = this.indexOfPanel(this.getActivePanel());
22420 if (i >= this.tabs.length - 1 && !this.autoslide) {
22424 if (i >= this.tabs.length - 1 && this.autoslide) {
22428 this.showPanel(this.tabs[i+1]);
22431 showPanelPrev : function()
22433 var i = this.indexOfPanel(this.getActivePanel());
22435 if (i < 1 && !this.autoslide) {
22439 if (i < 1 && this.autoslide) {
22440 i = this.tabs.length;
22443 this.showPanel(this.tabs[i-1]);
22447 addBullet: function()
22449 if(!this.bullets || Roo.isTouch){
22452 var ctr = this.el.select('.carousel-bullets',true).first();
22453 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22454 var bullet = ctr.createChild({
22455 cls : 'bullet bullet-' + i
22456 },ctr.dom.lastChild);
22461 bullet.on('click', (function(e, el, o, ii, t){
22463 e.preventDefault();
22465 this.showPanel(ii);
22467 if(this.autoslide && this.slideFn){
22468 clearInterval(this.slideFn);
22469 this.slideFn = window.setInterval(function() {
22470 _this.showPanelNext();
22474 }).createDelegate(this, [i, bullet], true));
22479 setActiveBullet : function(i)
22485 Roo.each(this.el.select('.bullet', true).elements, function(el){
22486 el.removeClass('selected');
22489 var bullet = this.el.select('.bullet-' + i, true).first();
22495 bullet.addClass('selected');
22506 Roo.apply(Roo.bootstrap.TabGroup, {
22510 * register a Navigation Group
22511 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22513 register : function(navgrp)
22515 this.groups[navgrp.navId] = navgrp;
22519 * fetch a Navigation Group based on the navigation ID
22520 * if one does not exist , it will get created.
22521 * @param {string} the navgroup to add
22522 * @returns {Roo.bootstrap.nav.Group} the navgroup
22524 get: function(navId) {
22525 if (typeof(this.groups[navId]) == 'undefined') {
22526 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22528 return this.groups[navId] ;
22543 * @class Roo.bootstrap.TabPanel
22544 * @extends Roo.bootstrap.Component
22545 * @children Roo.bootstrap.Component
22546 * Bootstrap TabPanel class
22547 * @cfg {Boolean} active panel active
22548 * @cfg {String} html panel content
22549 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22550 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22551 * @cfg {String} href click to link..
22552 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22556 * Create a new TabPanel
22557 * @param {Object} config The config object
22560 Roo.bootstrap.TabPanel = function(config){
22561 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22565 * Fires when the active status changes
22566 * @param {Roo.bootstrap.TabPanel} this
22567 * @param {Boolean} state the new state
22572 * @event beforedeactivate
22573 * Fires before a tab is de-activated - can be used to do validation on a form.
22574 * @param {Roo.bootstrap.TabPanel} this
22575 * @return {Boolean} false if there is an error
22578 'beforedeactivate': true
22581 this.tabId = this.tabId || Roo.id();
22585 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22592 touchSlide : false,
22593 getAutoCreate : function(){
22598 // item is needed for carousel - not sure if it has any effect otherwise
22599 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22600 html: this.html || ''
22604 cfg.cls += ' active';
22608 cfg.tabId = this.tabId;
22616 initEvents: function()
22618 var p = this.parent();
22620 this.navId = this.navId || p.navId;
22622 if (typeof(this.navId) != 'undefined') {
22623 // not really needed.. but just in case.. parent should be a NavGroup.
22624 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22628 var i = tg.tabs.length - 1;
22630 if(this.active && tg.bullets > 0 && i < tg.bullets){
22631 tg.setActiveBullet(i);
22635 this.el.on('click', this.onClick, this);
22637 if(Roo.isTouch && this.touchSlide){
22638 this.el.on("touchstart", this.onTouchStart, this);
22639 this.el.on("touchmove", this.onTouchMove, this);
22640 this.el.on("touchend", this.onTouchEnd, this);
22645 onRender : function(ct, position)
22647 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22650 setActive : function(state)
22652 Roo.log("panel - set active " + this.tabId + "=" + state);
22654 this.active = state;
22656 this.el.removeClass('active');
22658 } else if (!this.el.hasClass('active')) {
22659 this.el.addClass('active');
22662 this.fireEvent('changed', this, state);
22665 onClick : function(e)
22667 e.preventDefault();
22669 if(!this.href.length){
22673 window.location.href = this.href;
22682 onTouchStart : function(e)
22684 this.swiping = false;
22686 this.startX = e.browserEvent.touches[0].clientX;
22687 this.startY = e.browserEvent.touches[0].clientY;
22690 onTouchMove : function(e)
22692 this.swiping = true;
22694 this.endX = e.browserEvent.touches[0].clientX;
22695 this.endY = e.browserEvent.touches[0].clientY;
22698 onTouchEnd : function(e)
22705 var tabGroup = this.parent();
22707 if(this.endX > this.startX){ // swiping right
22708 tabGroup.showPanelPrev();
22712 if(this.startX > this.endX){ // swiping left
22713 tabGroup.showPanelNext();
22732 * @class Roo.bootstrap.form.DateField
22733 * @extends Roo.bootstrap.form.Input
22734 * Bootstrap DateField class
22735 * @cfg {Number} weekStart default 0
22736 * @cfg {String} viewMode default empty, (months|years)
22737 * @cfg {String} minViewMode default empty, (months|years)
22738 * @cfg {Number} startDate default -Infinity
22739 * @cfg {Number} endDate default Infinity
22740 * @cfg {Boolean} todayHighlight default false
22741 * @cfg {Boolean} todayBtn default false
22742 * @cfg {Boolean} calendarWeeks default false
22743 * @cfg {Object} daysOfWeekDisabled default empty
22744 * @cfg {Boolean} singleMode default false (true | false)
22746 * @cfg {Boolean} keyboardNavigation default true
22747 * @cfg {String} language default en
22750 * Create a new DateField
22751 * @param {Object} config The config object
22754 Roo.bootstrap.form.DateField = function(config){
22755 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22759 * Fires when this field show.
22760 * @param {Roo.bootstrap.form.DateField} this
22761 * @param {Mixed} date The date value
22766 * Fires when this field hide.
22767 * @param {Roo.bootstrap.form.DateField} this
22768 * @param {Mixed} date The date value
22773 * Fires when select a date.
22774 * @param {Roo.bootstrap.form.DateField} this
22775 * @param {Mixed} date The date value
22779 * @event beforeselect
22780 * Fires when before select a date.
22781 * @param {Roo.bootstrap.form.DateField} this
22782 * @param {Mixed} date The date value
22784 beforeselect : true
22788 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22791 * @cfg {String} format
22792 * The default date format string which can be overriden for localization support. The format must be
22793 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22797 * @cfg {String} altFormats
22798 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22799 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22801 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22809 todayHighlight : false,
22815 keyboardNavigation: true,
22817 calendarWeeks: false,
22819 startDate: -Infinity,
22823 daysOfWeekDisabled: [],
22827 singleMode : false,
22829 UTCDate: function()
22831 return new Date(Date.UTC.apply(Date, arguments));
22834 UTCToday: function()
22836 var today = new Date();
22837 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22840 getDate: function() {
22841 var d = this.getUTCDate();
22842 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22845 getUTCDate: function() {
22849 setDate: function(d) {
22850 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22853 setUTCDate: function(d) {
22855 this.setValue(this.formatDate(this.date));
22858 onRender: function(ct, position)
22861 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22863 this.language = this.language || 'en';
22864 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22865 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22867 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22868 this.format = this.format || 'm/d/y';
22869 this.isInline = false;
22870 this.isInput = true;
22871 this.component = this.el.select('.add-on', true).first() || false;
22872 this.component = (this.component && this.component.length === 0) ? false : this.component;
22873 this.hasInput = this.component && this.inputEl().length;
22875 if (typeof(this.minViewMode === 'string')) {
22876 switch (this.minViewMode) {
22878 this.minViewMode = 1;
22881 this.minViewMode = 2;
22884 this.minViewMode = 0;
22889 if (typeof(this.viewMode === 'string')) {
22890 switch (this.viewMode) {
22903 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22905 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22907 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22909 this.picker().on('mousedown', this.onMousedown, this);
22910 this.picker().on('click', this.onClick, this);
22912 this.picker().addClass('datepicker-dropdown');
22914 this.startViewMode = this.viewMode;
22916 if(this.singleMode){
22917 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22918 v.setVisibilityMode(Roo.Element.DISPLAY);
22922 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22923 v.setStyle('width', '189px');
22927 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22928 if(!this.calendarWeeks){
22933 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22934 v.attr('colspan', function(i, val){
22935 return parseInt(val) + 1;
22940 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22942 this.setStartDate(this.startDate);
22943 this.setEndDate(this.endDate);
22945 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22952 if(this.isInline) {
22957 picker : function()
22959 return this.pickerEl;
22960 // return this.el.select('.datepicker', true).first();
22963 fillDow: function()
22965 var dowCnt = this.weekStart;
22974 if(this.calendarWeeks){
22982 while (dowCnt < this.weekStart + 7) {
22986 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22990 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22993 fillMonths: function()
22996 var months = this.picker().select('>.datepicker-months td', true).first();
22998 months.dom.innerHTML = '';
23004 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23007 months.createChild(month);
23014 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;
23016 if (this.date < this.startDate) {
23017 this.viewDate = new Date(this.startDate);
23018 } else if (this.date > this.endDate) {
23019 this.viewDate = new Date(this.endDate);
23021 this.viewDate = new Date(this.date);
23029 var d = new Date(this.viewDate),
23030 year = d.getUTCFullYear(),
23031 month = d.getUTCMonth(),
23032 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23033 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23034 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23035 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23036 currentDate = this.date && this.date.valueOf(),
23037 today = this.UTCToday();
23039 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23041 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23043 // this.picker.select('>tfoot th.today').
23044 // .text(dates[this.language].today)
23045 // .toggle(this.todayBtn !== false);
23047 this.updateNavArrows();
23050 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23052 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23054 prevMonth.setUTCDate(day);
23056 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23058 var nextMonth = new Date(prevMonth);
23060 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23062 nextMonth = nextMonth.valueOf();
23064 var fillMonths = false;
23066 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23068 while(prevMonth.valueOf() <= nextMonth) {
23071 if (prevMonth.getUTCDay() === this.weekStart) {
23073 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23081 if(this.calendarWeeks){
23082 // ISO 8601: First week contains first thursday.
23083 // ISO also states week starts on Monday, but we can be more abstract here.
23085 // Start of current week: based on weekstart/current date
23086 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23087 // Thursday of this week
23088 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23089 // First Thursday of year, year from thursday
23090 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23091 // Calendar week: ms between thursdays, div ms per day, div 7 days
23092 calWeek = (th - yth) / 864e5 / 7 + 1;
23094 fillMonths.cn.push({
23102 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23104 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23107 if (this.todayHighlight &&
23108 prevMonth.getUTCFullYear() == today.getFullYear() &&
23109 prevMonth.getUTCMonth() == today.getMonth() &&
23110 prevMonth.getUTCDate() == today.getDate()) {
23111 clsName += ' today';
23114 if (currentDate && prevMonth.valueOf() === currentDate) {
23115 clsName += ' active';
23118 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23119 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23120 clsName += ' disabled';
23123 fillMonths.cn.push({
23125 cls: 'day ' + clsName,
23126 html: prevMonth.getDate()
23129 prevMonth.setDate(prevMonth.getDate()+1);
23132 var currentYear = this.date && this.date.getUTCFullYear();
23133 var currentMonth = this.date && this.date.getUTCMonth();
23135 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23137 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23138 v.removeClass('active');
23140 if(currentYear === year && k === currentMonth){
23141 v.addClass('active');
23144 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23145 v.addClass('disabled');
23151 year = parseInt(year/10, 10) * 10;
23153 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23155 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23158 for (var i = -1; i < 11; i++) {
23159 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23161 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23169 showMode: function(dir)
23172 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23175 Roo.each(this.picker().select('>div',true).elements, function(v){
23176 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23179 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23184 if(this.isInline) {
23188 this.picker().removeClass(['bottom', 'top']);
23190 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23192 * place to the top of element!
23196 this.picker().addClass('top');
23197 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23202 this.picker().addClass('bottom');
23204 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23207 parseDate : function(value)
23209 if(!value || value instanceof Date){
23212 var v = Date.parseDate(value, this.format);
23213 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23214 v = Date.parseDate(value, 'Y-m-d');
23216 if(!v && this.altFormats){
23217 if(!this.altFormatsArray){
23218 this.altFormatsArray = this.altFormats.split("|");
23220 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23221 v = Date.parseDate(value, this.altFormatsArray[i]);
23227 formatDate : function(date, fmt)
23229 return (!date || !(date instanceof Date)) ?
23230 date : date.dateFormat(fmt || this.format);
23233 onFocus : function()
23235 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23239 onBlur : function()
23241 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23243 var d = this.inputEl().getValue();
23250 showPopup : function()
23252 this.picker().show();
23256 this.fireEvent('showpopup', this, this.date);
23259 hidePopup : function()
23261 if(this.isInline) {
23264 this.picker().hide();
23265 this.viewMode = this.startViewMode;
23268 this.fireEvent('hidepopup', this, this.date);
23272 onMousedown: function(e)
23274 e.stopPropagation();
23275 e.preventDefault();
23280 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23284 setValue: function(v)
23286 if(this.fireEvent('beforeselect', this, v) !== false){
23287 var d = new Date(this.parseDate(v) ).clearTime();
23289 if(isNaN(d.getTime())){
23290 this.date = this.viewDate = '';
23291 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23295 v = this.formatDate(d);
23297 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23299 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23303 this.fireEvent('select', this, this.date);
23307 getValue: function()
23309 return this.formatDate(this.date);
23312 fireKey: function(e)
23314 if (!this.picker().isVisible()){
23315 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23321 var dateChanged = false,
23323 newDate, newViewDate;
23328 e.preventDefault();
23332 if (!this.keyboardNavigation) {
23335 dir = e.keyCode == 37 ? -1 : 1;
23338 newDate = this.moveYear(this.date, dir);
23339 newViewDate = this.moveYear(this.viewDate, dir);
23340 } else if (e.shiftKey){
23341 newDate = this.moveMonth(this.date, dir);
23342 newViewDate = this.moveMonth(this.viewDate, dir);
23344 newDate = new Date(this.date);
23345 newDate.setUTCDate(this.date.getUTCDate() + dir);
23346 newViewDate = new Date(this.viewDate);
23347 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23349 if (this.dateWithinRange(newDate)){
23350 this.date = newDate;
23351 this.viewDate = newViewDate;
23352 this.setValue(this.formatDate(this.date));
23354 e.preventDefault();
23355 dateChanged = true;
23360 if (!this.keyboardNavigation) {
23363 dir = e.keyCode == 38 ? -1 : 1;
23365 newDate = this.moveYear(this.date, dir);
23366 newViewDate = this.moveYear(this.viewDate, dir);
23367 } else if (e.shiftKey){
23368 newDate = this.moveMonth(this.date, dir);
23369 newViewDate = this.moveMonth(this.viewDate, dir);
23371 newDate = new Date(this.date);
23372 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23373 newViewDate = new Date(this.viewDate);
23374 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23376 if (this.dateWithinRange(newDate)){
23377 this.date = newDate;
23378 this.viewDate = newViewDate;
23379 this.setValue(this.formatDate(this.date));
23381 e.preventDefault();
23382 dateChanged = true;
23386 this.setValue(this.formatDate(this.date));
23388 e.preventDefault();
23391 this.setValue(this.formatDate(this.date));
23405 onClick: function(e)
23407 e.stopPropagation();
23408 e.preventDefault();
23410 var target = e.getTarget();
23412 if(target.nodeName.toLowerCase() === 'i'){
23413 target = Roo.get(target).dom.parentNode;
23416 var nodeName = target.nodeName;
23417 var className = target.className;
23418 var html = target.innerHTML;
23419 //Roo.log(nodeName);
23421 switch(nodeName.toLowerCase()) {
23423 switch(className) {
23429 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23430 switch(this.viewMode){
23432 this.viewDate = this.moveMonth(this.viewDate, dir);
23436 this.viewDate = this.moveYear(this.viewDate, dir);
23442 var date = new Date();
23443 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23445 this.setValue(this.formatDate(this.date));
23452 if (className.indexOf('disabled') < 0) {
23453 if (!this.viewDate) {
23454 this.viewDate = new Date();
23456 this.viewDate.setUTCDate(1);
23457 if (className.indexOf('month') > -1) {
23458 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23460 var year = parseInt(html, 10) || 0;
23461 this.viewDate.setUTCFullYear(year);
23465 if(this.singleMode){
23466 this.setValue(this.formatDate(this.viewDate));
23477 //Roo.log(className);
23478 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23479 var day = parseInt(html, 10) || 1;
23480 var year = (this.viewDate || new Date()).getUTCFullYear(),
23481 month = (this.viewDate || new Date()).getUTCMonth();
23483 if (className.indexOf('old') > -1) {
23490 } else if (className.indexOf('new') > -1) {
23498 //Roo.log([year,month,day]);
23499 this.date = this.UTCDate(year, month, day,0,0,0,0);
23500 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23502 //Roo.log(this.formatDate(this.date));
23503 this.setValue(this.formatDate(this.date));
23510 setStartDate: function(startDate)
23512 this.startDate = startDate || -Infinity;
23513 if (this.startDate !== -Infinity) {
23514 this.startDate = this.parseDate(this.startDate);
23517 this.updateNavArrows();
23520 setEndDate: function(endDate)
23522 this.endDate = endDate || Infinity;
23523 if (this.endDate !== Infinity) {
23524 this.endDate = this.parseDate(this.endDate);
23527 this.updateNavArrows();
23530 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23532 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23533 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23534 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23536 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23537 return parseInt(d, 10);
23540 this.updateNavArrows();
23543 updateNavArrows: function()
23545 if(this.singleMode){
23549 var d = new Date(this.viewDate),
23550 year = d.getUTCFullYear(),
23551 month = d.getUTCMonth();
23553 Roo.each(this.picker().select('.prev', true).elements, function(v){
23555 switch (this.viewMode) {
23558 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23564 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23571 Roo.each(this.picker().select('.next', true).elements, function(v){
23573 switch (this.viewMode) {
23576 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23582 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23590 moveMonth: function(date, dir)
23595 var new_date = new Date(date.valueOf()),
23596 day = new_date.getUTCDate(),
23597 month = new_date.getUTCMonth(),
23598 mag = Math.abs(dir),
23600 dir = dir > 0 ? 1 : -1;
23603 // If going back one month, make sure month is not current month
23604 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23606 return new_date.getUTCMonth() == month;
23608 // If going forward one month, make sure month is as expected
23609 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23611 return new_date.getUTCMonth() != new_month;
23613 new_month = month + dir;
23614 new_date.setUTCMonth(new_month);
23615 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23616 if (new_month < 0 || new_month > 11) {
23617 new_month = (new_month + 12) % 12;
23620 // For magnitudes >1, move one month at a time...
23621 for (var i=0; i<mag; i++) {
23622 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23623 new_date = this.moveMonth(new_date, dir);
23625 // ...then reset the day, keeping it in the new month
23626 new_month = new_date.getUTCMonth();
23627 new_date.setUTCDate(day);
23629 return new_month != new_date.getUTCMonth();
23632 // Common date-resetting loop -- if date is beyond end of month, make it
23635 new_date.setUTCDate(--day);
23636 new_date.setUTCMonth(new_month);
23641 moveYear: function(date, dir)
23643 return this.moveMonth(date, dir*12);
23646 dateWithinRange: function(date)
23648 return date >= this.startDate && date <= this.endDate;
23654 this.picker().remove();
23657 validateValue : function(value)
23659 if(this.getVisibilityEl().hasClass('hidden')){
23663 if(value.length < 1) {
23664 if(this.allowBlank){
23670 if(value.length < this.minLength){
23673 if(value.length > this.maxLength){
23677 var vt = Roo.form.VTypes;
23678 if(!vt[this.vtype](value, this)){
23682 if(typeof this.validator == "function"){
23683 var msg = this.validator(value);
23689 if(this.regex && !this.regex.test(value)){
23693 if(typeof(this.parseDate(value)) == 'undefined'){
23697 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23701 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23711 this.date = this.viewDate = '';
23713 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23718 Roo.apply(Roo.bootstrap.form.DateField, {
23729 html: '<i class="fa fa-arrow-left"/>'
23739 html: '<i class="fa fa-arrow-right"/>'
23781 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23782 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23783 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23784 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23785 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23798 navFnc: 'FullYear',
23803 navFnc: 'FullYear',
23808 Roo.apply(Roo.bootstrap.form.DateField, {
23812 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23816 cls: 'datepicker-days',
23820 cls: 'table-condensed',
23822 Roo.bootstrap.form.DateField.head,
23826 Roo.bootstrap.form.DateField.footer
23833 cls: 'datepicker-months',
23837 cls: 'table-condensed',
23839 Roo.bootstrap.form.DateField.head,
23840 Roo.bootstrap.form.DateField.content,
23841 Roo.bootstrap.form.DateField.footer
23848 cls: 'datepicker-years',
23852 cls: 'table-condensed',
23854 Roo.bootstrap.form.DateField.head,
23855 Roo.bootstrap.form.DateField.content,
23856 Roo.bootstrap.form.DateField.footer
23875 * @class Roo.bootstrap.form.TimeField
23876 * @extends Roo.bootstrap.form.Input
23877 * Bootstrap DateField class
23881 * Create a new TimeField
23882 * @param {Object} config The config object
23885 Roo.bootstrap.form.TimeField = function(config){
23886 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23890 * Fires when this field show.
23891 * @param {Roo.bootstrap.form.DateField} thisthis
23892 * @param {Mixed} date The date value
23897 * Fires when this field hide.
23898 * @param {Roo.bootstrap.form.DateField} this
23899 * @param {Mixed} date The date value
23904 * Fires when select a date.
23905 * @param {Roo.bootstrap.form.DateField} this
23906 * @param {Mixed} date The date value
23912 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23915 * @cfg {String} format
23916 * The default time format string which can be overriden for localization support. The format must be
23917 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23921 getAutoCreate : function()
23923 this.after = '<i class="fa far fa-clock"></i>';
23924 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23928 onRender: function(ct, position)
23931 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23933 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23935 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23937 this.pop = this.picker().select('>.datepicker-time',true).first();
23938 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23940 this.picker().on('mousedown', this.onMousedown, this);
23941 this.picker().on('click', this.onClick, this);
23943 this.picker().addClass('datepicker-dropdown');
23948 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23949 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23950 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23951 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23952 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23953 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23957 fireKey: function(e){
23958 if (!this.picker().isVisible()){
23959 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23965 e.preventDefault();
23973 this.onTogglePeriod();
23976 this.onIncrementMinutes();
23979 this.onDecrementMinutes();
23988 onClick: function(e) {
23989 e.stopPropagation();
23990 e.preventDefault();
23993 picker : function()
23995 return this.pickerEl;
23998 fillTime: function()
24000 var time = this.pop.select('tbody', true).first();
24002 time.dom.innerHTML = '';
24017 cls: 'hours-up fa fas fa-chevron-up'
24037 cls: 'minutes-up fa fas fa-chevron-up'
24058 cls: 'timepicker-hour',
24073 cls: 'timepicker-minute',
24088 cls: 'btn btn-primary period',
24110 cls: 'hours-down fa fas fa-chevron-down'
24130 cls: 'minutes-down fa fas fa-chevron-down'
24148 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24155 var hours = this.time.getHours();
24156 var minutes = this.time.getMinutes();
24169 hours = hours - 12;
24173 hours = '0' + hours;
24177 minutes = '0' + minutes;
24180 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24181 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24182 this.pop.select('button', true).first().dom.innerHTML = period;
24188 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24190 var cls = ['bottom'];
24192 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24199 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24203 //this.picker().setXY(20000,20000);
24204 this.picker().addClass(cls.join('-'));
24208 Roo.each(cls, function(c){
24213 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24214 //_this.picker().setTop(_this.inputEl().getHeight());
24218 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24220 //_this.picker().setTop(0 - _this.picker().getHeight());
24225 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24229 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24237 onFocus : function()
24239 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24243 onBlur : function()
24245 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24251 this.picker().show();
24256 this.fireEvent('show', this, this.date);
24261 this.picker().hide();
24264 this.fireEvent('hide', this, this.date);
24267 setTime : function()
24270 this.setValue(this.time.format(this.format));
24272 this.fireEvent('select', this, this.date);
24277 onMousedown: function(e){
24278 e.stopPropagation();
24279 e.preventDefault();
24282 onIncrementHours: function()
24284 Roo.log('onIncrementHours');
24285 this.time = this.time.add(Date.HOUR, 1);
24290 onDecrementHours: function()
24292 Roo.log('onDecrementHours');
24293 this.time = this.time.add(Date.HOUR, -1);
24297 onIncrementMinutes: function()
24299 Roo.log('onIncrementMinutes');
24300 this.time = this.time.add(Date.MINUTE, 1);
24304 onDecrementMinutes: function()
24306 Roo.log('onDecrementMinutes');
24307 this.time = this.time.add(Date.MINUTE, -1);
24311 onTogglePeriod: function()
24313 Roo.log('onTogglePeriod');
24314 this.time = this.time.add(Date.HOUR, 12);
24322 Roo.apply(Roo.bootstrap.form.TimeField, {
24326 cls: 'datepicker dropdown-menu',
24330 cls: 'datepicker-time',
24334 cls: 'table-condensed',
24363 cls: 'btn btn-info ok',
24391 * @class Roo.bootstrap.form.MonthField
24392 * @extends Roo.bootstrap.form.Input
24393 * Bootstrap MonthField class
24395 * @cfg {String} language default en
24398 * Create a new MonthField
24399 * @param {Object} config The config object
24402 Roo.bootstrap.form.MonthField = function(config){
24403 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24408 * Fires when this field show.
24409 * @param {Roo.bootstrap.form.MonthField} this
24410 * @param {Mixed} date The date value
24415 * Fires when this field hide.
24416 * @param {Roo.bootstrap.form.MonthField} this
24417 * @param {Mixed} date The date value
24422 * Fires when select a date.
24423 * @param {Roo.bootstrap.form.MonthField} this
24424 * @param {String} oldvalue The old value
24425 * @param {String} newvalue The new value
24431 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24433 onRender: function(ct, position)
24436 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24438 this.language = this.language || 'en';
24439 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24440 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24442 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24443 this.isInline = false;
24444 this.isInput = true;
24445 this.component = this.el.select('.add-on', true).first() || false;
24446 this.component = (this.component && this.component.length === 0) ? false : this.component;
24447 this.hasInput = this.component && this.inputEL().length;
24449 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24451 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24453 this.picker().on('mousedown', this.onMousedown, this);
24454 this.picker().on('click', this.onClick, this);
24456 this.picker().addClass('datepicker-dropdown');
24458 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24459 v.setStyle('width', '189px');
24466 if(this.isInline) {
24472 setValue: function(v, suppressEvent)
24474 var o = this.getValue();
24476 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24480 if(suppressEvent !== true){
24481 this.fireEvent('select', this, o, v);
24486 getValue: function()
24491 onClick: function(e)
24493 e.stopPropagation();
24494 e.preventDefault();
24496 var target = e.getTarget();
24498 if(target.nodeName.toLowerCase() === 'i'){
24499 target = Roo.get(target).dom.parentNode;
24502 var nodeName = target.nodeName;
24503 var className = target.className;
24504 var html = target.innerHTML;
24506 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24510 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24512 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24518 picker : function()
24520 return this.pickerEl;
24523 fillMonths: function()
24526 var months = this.picker().select('>.datepicker-months td', true).first();
24528 months.dom.innerHTML = '';
24534 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24537 months.createChild(month);
24546 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24547 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24550 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24551 e.removeClass('active');
24553 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24554 e.addClass('active');
24561 if(this.isInline) {
24565 this.picker().removeClass(['bottom', 'top']);
24567 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24569 * place to the top of element!
24573 this.picker().addClass('top');
24574 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24579 this.picker().addClass('bottom');
24581 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24584 onFocus : function()
24586 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24590 onBlur : function()
24592 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24594 var d = this.inputEl().getValue();
24603 this.picker().show();
24604 this.picker().select('>.datepicker-months', true).first().show();
24608 this.fireEvent('show', this, this.date);
24613 if(this.isInline) {
24616 this.picker().hide();
24617 this.fireEvent('hide', this, this.date);
24621 onMousedown: function(e)
24623 e.stopPropagation();
24624 e.preventDefault();
24629 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24633 fireKey: function(e)
24635 if (!this.picker().isVisible()){
24636 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24647 e.preventDefault();
24651 dir = e.keyCode == 37 ? -1 : 1;
24653 this.vIndex = this.vIndex + dir;
24655 if(this.vIndex < 0){
24659 if(this.vIndex > 11){
24663 if(isNaN(this.vIndex)){
24667 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24673 dir = e.keyCode == 38 ? -1 : 1;
24675 this.vIndex = this.vIndex + dir * 4;
24677 if(this.vIndex < 0){
24681 if(this.vIndex > 11){
24685 if(isNaN(this.vIndex)){
24689 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24694 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24695 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24699 e.preventDefault();
24702 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24703 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24719 this.picker().remove();
24724 Roo.apply(Roo.bootstrap.form.MonthField, {
24743 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24744 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24749 Roo.apply(Roo.bootstrap.form.MonthField, {
24753 cls: 'datepicker dropdown-menu roo-dynamic',
24757 cls: 'datepicker-months',
24761 cls: 'table-condensed',
24763 Roo.bootstrap.form.DateField.content
24783 * @class Roo.bootstrap.form.CheckBox
24784 * @extends Roo.bootstrap.form.Input
24785 * Bootstrap CheckBox class
24787 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24788 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24789 * @cfg {String} boxLabel The text that appears beside the checkbox
24790 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24791 * @cfg {Boolean} checked initnal the element
24792 * @cfg {Boolean} inline inline the element (default false)
24793 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24794 * @cfg {String} tooltip label tooltip
24797 * Create a new CheckBox
24798 * @param {Object} config The config object
24801 Roo.bootstrap.form.CheckBox = function(config){
24802 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24807 * Fires when the element is checked or unchecked.
24808 * @param {Roo.bootstrap.form.CheckBox} this This input
24809 * @param {Boolean} checked The new checked value
24814 * Fires when the element is click.
24815 * @param {Roo.bootstrap.form.CheckBox} this This input
24822 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24824 inputType: 'checkbox',
24833 // checkbox success does not make any sense really..
24838 getAutoCreate : function()
24840 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24846 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24849 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24855 type : this.inputType,
24856 value : this.inputValue,
24857 cls : 'roo-' + this.inputType, //'form-box',
24858 placeholder : this.placeholder || ''
24862 if(this.inputType != 'radio'){
24866 cls : 'roo-hidden-value',
24867 value : this.checked ? this.inputValue : this.valueOff
24872 if (this.weight) { // Validity check?
24873 cfg.cls += " " + this.inputType + "-" + this.weight;
24876 if (this.disabled) {
24877 input.disabled=true;
24881 input.checked = this.checked;
24886 input.name = this.name;
24888 if(this.inputType != 'radio'){
24889 hidden.name = this.name;
24890 input.name = '_hidden_' + this.name;
24895 input.cls += ' input-' + this.size;
24900 ['xs','sm','md','lg'].map(function(size){
24901 if (settings[size]) {
24902 cfg.cls += ' col-' + size + '-' + settings[size];
24906 var inputblock = input;
24908 if (this.before || this.after) {
24911 cls : 'input-group',
24916 inputblock.cn.push({
24918 cls : 'input-group-addon',
24923 inputblock.cn.push(input);
24925 if(this.inputType != 'radio'){
24926 inputblock.cn.push(hidden);
24930 inputblock.cn.push({
24932 cls : 'input-group-addon',
24938 var boxLabelCfg = false;
24944 //'for': id, // box label is handled by onclick - so no for...
24946 html: this.boxLabel
24949 boxLabelCfg.tooltip = this.tooltip;
24955 if (align ==='left' && this.fieldLabel.length) {
24956 // Roo.log("left and has label");
24961 cls : 'control-label',
24962 html : this.fieldLabel
24973 cfg.cn[1].cn.push(boxLabelCfg);
24976 if(this.labelWidth > 12){
24977 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24980 if(this.labelWidth < 13 && this.labelmd == 0){
24981 this.labelmd = this.labelWidth;
24984 if(this.labellg > 0){
24985 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24986 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24989 if(this.labelmd > 0){
24990 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24991 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24994 if(this.labelsm > 0){
24995 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24996 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24999 if(this.labelxs > 0){
25000 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25001 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25004 } else if ( this.fieldLabel.length) {
25005 // Roo.log(" label");
25009 tag: this.boxLabel ? 'span' : 'label',
25011 cls: 'control-label box-input-label',
25012 //cls : 'input-group-addon',
25013 html : this.fieldLabel
25020 cfg.cn.push(boxLabelCfg);
25025 // Roo.log(" no label && no align");
25026 cfg.cn = [ inputblock ] ;
25028 cfg.cn.push(boxLabelCfg);
25036 if(this.inputType != 'radio'){
25037 cfg.cn.push(hidden);
25045 * return the real input element.
25047 inputEl: function ()
25049 return this.el.select('input.roo-' + this.inputType,true).first();
25051 hiddenEl: function ()
25053 return this.el.select('input.roo-hidden-value',true).first();
25056 labelEl: function()
25058 return this.el.select('label.control-label',true).first();
25060 /* depricated... */
25064 return this.labelEl();
25067 boxLabelEl: function()
25069 return this.el.select('label.box-label',true).first();
25072 initEvents : function()
25074 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25076 this.inputEl().on('click', this.onClick, this);
25078 if (this.boxLabel) {
25079 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25082 this.startValue = this.getValue();
25085 Roo.bootstrap.form.CheckBox.register(this);
25089 onClick : function(e)
25091 if(this.fireEvent('click', this, e) !== false){
25092 this.setChecked(!this.checked);
25097 setChecked : function(state,suppressEvent)
25099 this.startValue = this.getValue();
25101 if(this.inputType == 'radio'){
25103 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25104 e.dom.checked = false;
25107 this.inputEl().dom.checked = true;
25109 this.inputEl().dom.value = this.inputValue;
25111 if(suppressEvent !== true){
25112 this.fireEvent('check', this, true);
25120 this.checked = state;
25122 this.inputEl().dom.checked = state;
25125 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25127 if(suppressEvent !== true){
25128 this.fireEvent('check', this, state);
25134 getValue : function()
25136 if(this.inputType == 'radio'){
25137 return this.getGroupValue();
25140 return this.hiddenEl().dom.value;
25144 getGroupValue : function()
25146 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25150 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25153 setValue : function(v,suppressEvent)
25155 if(this.inputType == 'radio'){
25156 this.setGroupValue(v, suppressEvent);
25160 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25165 setGroupValue : function(v, suppressEvent)
25167 this.startValue = this.getValue();
25169 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25170 e.dom.checked = false;
25172 if(e.dom.value == v){
25173 e.dom.checked = true;
25177 if(suppressEvent !== true){
25178 this.fireEvent('check', this, true);
25186 validate : function()
25188 if(this.getVisibilityEl().hasClass('hidden')){
25194 (this.inputType == 'radio' && this.validateRadio()) ||
25195 (this.inputType == 'checkbox' && this.validateCheckbox())
25201 this.markInvalid();
25205 validateRadio : function()
25207 if(this.getVisibilityEl().hasClass('hidden')){
25211 if(this.allowBlank){
25217 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25218 if(!e.dom.checked){
25230 validateCheckbox : function()
25233 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25234 //return (this.getValue() == this.inputValue) ? true : false;
25237 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25245 for(var i in group){
25246 if(group[i].el.isVisible(true)){
25254 for(var i in group){
25259 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25266 * Mark this field as valid
25268 markValid : function()
25272 this.fireEvent('valid', this);
25274 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25277 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25284 if(this.inputType == 'radio'){
25285 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25286 var fg = e.findParent('.form-group', false, true);
25287 if (Roo.bootstrap.version == 3) {
25288 fg.removeClass([_this.invalidClass, _this.validClass]);
25289 fg.addClass(_this.validClass);
25291 fg.removeClass(['is-valid', 'is-invalid']);
25292 fg.addClass('is-valid');
25300 var fg = this.el.findParent('.form-group', false, true);
25301 if (Roo.bootstrap.version == 3) {
25302 fg.removeClass([this.invalidClass, this.validClass]);
25303 fg.addClass(this.validClass);
25305 fg.removeClass(['is-valid', 'is-invalid']);
25306 fg.addClass('is-valid');
25311 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25317 for(var i in group){
25318 var fg = group[i].el.findParent('.form-group', false, true);
25319 if (Roo.bootstrap.version == 3) {
25320 fg.removeClass([this.invalidClass, this.validClass]);
25321 fg.addClass(this.validClass);
25323 fg.removeClass(['is-valid', 'is-invalid']);
25324 fg.addClass('is-valid');
25330 * Mark this field as invalid
25331 * @param {String} msg The validation message
25333 markInvalid : function(msg)
25335 if(this.allowBlank){
25341 this.fireEvent('invalid', this, msg);
25343 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25346 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25350 label.markInvalid();
25353 if(this.inputType == 'radio'){
25355 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25356 var fg = e.findParent('.form-group', false, true);
25357 if (Roo.bootstrap.version == 3) {
25358 fg.removeClass([_this.invalidClass, _this.validClass]);
25359 fg.addClass(_this.invalidClass);
25361 fg.removeClass(['is-invalid', 'is-valid']);
25362 fg.addClass('is-invalid');
25370 var fg = this.el.findParent('.form-group', false, true);
25371 if (Roo.bootstrap.version == 3) {
25372 fg.removeClass([_this.invalidClass, _this.validClass]);
25373 fg.addClass(_this.invalidClass);
25375 fg.removeClass(['is-invalid', 'is-valid']);
25376 fg.addClass('is-invalid');
25381 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25387 for(var i in group){
25388 var fg = group[i].el.findParent('.form-group', false, true);
25389 if (Roo.bootstrap.version == 3) {
25390 fg.removeClass([_this.invalidClass, _this.validClass]);
25391 fg.addClass(_this.invalidClass);
25393 fg.removeClass(['is-invalid', 'is-valid']);
25394 fg.addClass('is-invalid');
25400 clearInvalid : function()
25402 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25404 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25406 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25408 if (label && label.iconEl) {
25409 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25410 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25414 disable : function()
25416 if(this.inputType != 'radio'){
25417 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25424 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25425 _this.getActionEl().addClass(this.disabledClass);
25426 e.dom.disabled = true;
25430 this.disabled = true;
25431 this.fireEvent("disable", this);
25435 enable : function()
25437 if(this.inputType != 'radio'){
25438 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25445 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25446 _this.getActionEl().removeClass(this.disabledClass);
25447 e.dom.disabled = false;
25451 this.disabled = false;
25452 this.fireEvent("enable", this);
25456 setBoxLabel : function(v)
25461 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25467 Roo.apply(Roo.bootstrap.form.CheckBox, {
25472 * register a CheckBox Group
25473 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25475 register : function(checkbox)
25477 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25478 this.groups[checkbox.groupId] = {};
25481 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25485 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25489 * fetch a CheckBox Group based on the group ID
25490 * @param {string} the group ID
25491 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25493 get: function(groupId) {
25494 if (typeof(this.groups[groupId]) == 'undefined') {
25498 return this.groups[groupId] ;
25511 * @class Roo.bootstrap.form.Radio
25512 * @extends Roo.bootstrap.Component
25513 * Bootstrap Radio class
25514 * @cfg {String} boxLabel - the label associated
25515 * @cfg {String} value - the value of radio
25518 * Create a new Radio
25519 * @param {Object} config The config object
25521 Roo.bootstrap.form.Radio = function(config){
25522 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25526 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25532 getAutoCreate : function()
25536 cls : 'form-group radio',
25541 html : this.boxLabel
25549 initEvents : function()
25551 this.parent().register(this);
25553 this.el.on('click', this.onClick, this);
25557 onClick : function(e)
25559 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25560 this.setChecked(true);
25564 setChecked : function(state, suppressEvent)
25566 this.parent().setValue(this.value, suppressEvent);
25570 setBoxLabel : function(v)
25575 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25590 * @class Roo.bootstrap.form.SecurePass
25591 * @extends Roo.bootstrap.form.Input
25592 * Bootstrap SecurePass class
25596 * Create a new SecurePass
25597 * @param {Object} config The config object
25600 Roo.bootstrap.form.SecurePass = function (config) {
25601 // these go here, so the translation tool can replace them..
25603 PwdEmpty: "Please type a password, and then retype it to confirm.",
25604 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25605 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25606 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25607 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25608 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25609 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25610 TooWeak: "Your password is Too Weak."
25612 this.meterLabel = "Password strength:";
25613 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25614 this.meterClass = [
25615 "roo-password-meter-tooweak",
25616 "roo-password-meter-weak",
25617 "roo-password-meter-medium",
25618 "roo-password-meter-strong",
25619 "roo-password-meter-grey"
25624 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25627 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25629 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25631 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25632 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25633 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25634 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25635 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25636 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25637 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25647 * @cfg {String/Object} Label for the strength meter (defaults to
25648 * 'Password strength:')
25653 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25654 * ['Weak', 'Medium', 'Strong'])
25657 pwdStrengths: false,
25670 initEvents: function ()
25672 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25674 if (this.el.is('input[type=password]') && Roo.isSafari) {
25675 this.el.on('keydown', this.SafariOnKeyDown, this);
25678 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25681 onRender: function (ct, position)
25683 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25684 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25685 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25687 this.trigger.createChild({
25692 cls: 'roo-password-meter-grey col-xs-12',
25695 //width: this.meterWidth + 'px'
25699 cls: 'roo-password-meter-text'
25705 if (this.hideTrigger) {
25706 this.trigger.setDisplayed(false);
25708 this.setSize(this.width || '', this.height || '');
25711 onDestroy: function ()
25713 if (this.trigger) {
25714 this.trigger.removeAllListeners();
25715 this.trigger.remove();
25718 this.wrap.remove();
25720 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25723 checkStrength: function ()
25725 var pwd = this.inputEl().getValue();
25726 if (pwd == this._lastPwd) {
25731 if (this.ClientSideStrongPassword(pwd)) {
25733 } else if (this.ClientSideMediumPassword(pwd)) {
25735 } else if (this.ClientSideWeakPassword(pwd)) {
25741 Roo.log('strength1: ' + strength);
25743 //var pm = this.trigger.child('div/div/div').dom;
25744 var pm = this.trigger.child('div/div');
25745 pm.removeClass(this.meterClass);
25746 pm.addClass(this.meterClass[strength]);
25749 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25751 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25753 this._lastPwd = pwd;
25757 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25759 this._lastPwd = '';
25761 var pm = this.trigger.child('div/div');
25762 pm.removeClass(this.meterClass);
25763 pm.addClass('roo-password-meter-grey');
25766 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25769 this.inputEl().dom.type='password';
25772 validateValue: function (value)
25774 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25777 if (value.length == 0) {
25778 if (this.allowBlank) {
25779 this.clearInvalid();
25783 this.markInvalid(this.errors.PwdEmpty);
25784 this.errorMsg = this.errors.PwdEmpty;
25792 if (!value.match(/[\x21-\x7e]+/)) {
25793 this.markInvalid(this.errors.PwdBadChar);
25794 this.errorMsg = this.errors.PwdBadChar;
25797 if (value.length < 6) {
25798 this.markInvalid(this.errors.PwdShort);
25799 this.errorMsg = this.errors.PwdShort;
25802 if (value.length > 16) {
25803 this.markInvalid(this.errors.PwdLong);
25804 this.errorMsg = this.errors.PwdLong;
25808 if (this.ClientSideStrongPassword(value)) {
25810 } else if (this.ClientSideMediumPassword(value)) {
25812 } else if (this.ClientSideWeakPassword(value)) {
25819 if (strength < 2) {
25820 //this.markInvalid(this.errors.TooWeak);
25821 this.errorMsg = this.errors.TooWeak;
25826 console.log('strength2: ' + strength);
25828 //var pm = this.trigger.child('div/div/div').dom;
25830 var pm = this.trigger.child('div/div');
25831 pm.removeClass(this.meterClass);
25832 pm.addClass(this.meterClass[strength]);
25834 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25836 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25838 this.errorMsg = '';
25842 CharacterSetChecks: function (type)
25845 this.fResult = false;
25848 isctype: function (character, type)
25851 case this.kCapitalLetter:
25852 if (character >= 'A' && character <= 'Z') {
25857 case this.kSmallLetter:
25858 if (character >= 'a' && character <= 'z') {
25864 if (character >= '0' && character <= '9') {
25869 case this.kPunctuation:
25870 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25881 IsLongEnough: function (pwd, size)
25883 return !(pwd == null || isNaN(size) || pwd.length < size);
25886 SpansEnoughCharacterSets: function (word, nb)
25888 if (!this.IsLongEnough(word, nb))
25893 var characterSetChecks = new Array(
25894 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25895 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25898 for (var index = 0; index < word.length; ++index) {
25899 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25900 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25901 characterSetChecks[nCharSet].fResult = true;
25908 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25909 if (characterSetChecks[nCharSet].fResult) {
25914 if (nCharSets < nb) {
25920 ClientSideStrongPassword: function (pwd)
25922 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25925 ClientSideMediumPassword: function (pwd)
25927 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25930 ClientSideWeakPassword: function (pwd)
25932 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25935 })//<script type="text/javascript">
25938 * Based Ext JS Library 1.1.1
25939 * Copyright(c) 2006-2007, Ext JS, LLC.
25945 * @class Roo.HtmlEditorCore
25946 * @extends Roo.Component
25947 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25949 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25952 Roo.HtmlEditorCore = function(config){
25955 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25960 * @event initialize
25961 * Fires when the editor is fully initialized (including the iframe)
25962 * @param {Roo.HtmlEditorCore} this
25967 * Fires when the editor is first receives the focus. Any insertion must wait
25968 * until after this event.
25969 * @param {Roo.HtmlEditorCore} this
25973 * @event beforesync
25974 * Fires before the textarea is updated with content from the editor iframe. Return false
25975 * to cancel the sync.
25976 * @param {Roo.HtmlEditorCore} this
25977 * @param {String} html
25981 * @event beforepush
25982 * Fires before the iframe editor is updated with content from the textarea. Return false
25983 * to cancel the push.
25984 * @param {Roo.HtmlEditorCore} this
25985 * @param {String} html
25990 * Fires when the textarea is updated with content from the editor iframe.
25991 * @param {Roo.HtmlEditorCore} this
25992 * @param {String} html
25997 * Fires when the iframe editor is updated with content from the textarea.
25998 * @param {Roo.HtmlEditorCore} this
25999 * @param {String} html
26004 * @event editorevent
26005 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26006 * @param {Roo.HtmlEditorCore} this
26012 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26014 // defaults : white / black...
26015 this.applyBlacklists();
26022 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26026 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26032 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26037 * @cfg {Number} height (in pixels)
26041 * @cfg {Number} width (in pixels)
26046 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26049 stylesheets: false,
26052 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26054 allowComments: false,
26058 // private properties
26059 validationEvent : false,
26061 initialized : false,
26063 sourceEditMode : false,
26064 onFocus : Roo.emptyFn,
26066 hideMode:'offsets',
26070 // blacklist + whitelisted elements..
26077 * Protected method that will not generally be called directly. It
26078 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26079 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26081 getDocMarkup : function(){
26085 // inherit styels from page...??
26086 if (this.stylesheets === false) {
26088 Roo.get(document.head).select('style').each(function(node) {
26089 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26092 Roo.get(document.head).select('link').each(function(node) {
26093 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26096 } else if (!this.stylesheets.length) {
26098 st = '<style type="text/css">' +
26099 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26102 for (var i in this.stylesheets) {
26103 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26108 st += '<style type="text/css">' +
26109 'IMG { cursor: pointer } ' +
26112 var cls = 'roo-htmleditor-body';
26114 if(this.bodyCls.length){
26115 cls += ' ' + this.bodyCls;
26118 return '<html><head>' + st +
26119 //<style type="text/css">' +
26120 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26122 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26126 onRender : function(ct, position)
26129 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26130 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26133 this.el.dom.style.border = '0 none';
26134 this.el.dom.setAttribute('tabIndex', -1);
26135 this.el.addClass('x-hidden hide');
26139 if(Roo.isIE){ // fix IE 1px bogus margin
26140 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26144 this.frameId = Roo.id();
26148 var iframe = this.owner.wrap.createChild({
26150 cls: 'form-control', // bootstrap..
26152 name: this.frameId,
26153 frameBorder : 'no',
26154 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26159 this.iframe = iframe.dom;
26161 this.assignDocWin();
26163 this.doc.designMode = 'on';
26166 this.doc.write(this.getDocMarkup());
26170 var task = { // must defer to wait for browser to be ready
26172 //console.log("run task?" + this.doc.readyState);
26173 this.assignDocWin();
26174 if(this.doc.body || this.doc.readyState == 'complete'){
26176 this.doc.designMode="on";
26180 Roo.TaskMgr.stop(task);
26181 this.initEditor.defer(10, this);
26188 Roo.TaskMgr.start(task);
26193 onResize : function(w, h)
26195 Roo.log('resize: ' +w + ',' + h );
26196 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26200 if(typeof w == 'number'){
26202 this.iframe.style.width = w + 'px';
26204 if(typeof h == 'number'){
26206 this.iframe.style.height = h + 'px';
26208 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26215 * Toggles the editor between standard and source edit mode.
26216 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26218 toggleSourceEdit : function(sourceEditMode){
26220 this.sourceEditMode = sourceEditMode === true;
26222 if(this.sourceEditMode){
26224 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26227 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26228 //this.iframe.className = '';
26231 //this.setSize(this.owner.wrap.getSize());
26232 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26239 * Protected method that will not generally be called directly. If you need/want
26240 * custom HTML cleanup, this is the method you should override.
26241 * @param {String} html The HTML to be cleaned
26242 * return {String} The cleaned HTML
26244 cleanHtml : function(html){
26245 html = String(html);
26246 if(html.length > 5){
26247 if(Roo.isSafari){ // strip safari nonsense
26248 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26251 if(html == ' '){
26258 * HTML Editor -> Textarea
26259 * Protected method that will not generally be called directly. Syncs the contents
26260 * of the editor iframe with the textarea.
26262 syncValue : function(){
26263 if(this.initialized){
26264 var bd = (this.doc.body || this.doc.documentElement);
26265 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26266 var html = bd.innerHTML;
26268 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26269 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26271 html = '<div style="'+m[0]+'">' + html + '</div>';
26274 html = this.cleanHtml(html);
26275 // fix up the special chars.. normaly like back quotes in word...
26276 // however we do not want to do this with chinese..
26277 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26279 var cc = match.charCodeAt();
26281 // Get the character value, handling surrogate pairs
26282 if (match.length == 2) {
26283 // It's a surrogate pair, calculate the Unicode code point
26284 var high = match.charCodeAt(0) - 0xD800;
26285 var low = match.charCodeAt(1) - 0xDC00;
26286 cc = (high * 0x400) + low + 0x10000;
26288 (cc >= 0x4E00 && cc < 0xA000 ) ||
26289 (cc >= 0x3400 && cc < 0x4E00 ) ||
26290 (cc >= 0xf900 && cc < 0xfb00 )
26295 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26296 return "&#" + cc + ";";
26303 if(this.owner.fireEvent('beforesync', this, html) !== false){
26304 this.el.dom.value = html;
26305 this.owner.fireEvent('sync', this, html);
26311 * Protected method that will not generally be called directly. Pushes the value of the textarea
26312 * into the iframe editor.
26314 pushValue : function(){
26315 if(this.initialized){
26316 var v = this.el.dom.value.trim();
26318 // if(v.length < 1){
26322 if(this.owner.fireEvent('beforepush', this, v) !== false){
26323 var d = (this.doc.body || this.doc.documentElement);
26325 this.cleanUpPaste();
26326 this.el.dom.value = d.innerHTML;
26327 this.owner.fireEvent('push', this, v);
26333 deferFocus : function(){
26334 this.focus.defer(10, this);
26338 focus : function(){
26339 if(this.win && !this.sourceEditMode){
26346 assignDocWin: function()
26348 var iframe = this.iframe;
26351 this.doc = iframe.contentWindow.document;
26352 this.win = iframe.contentWindow;
26354 // if (!Roo.get(this.frameId)) {
26357 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26358 // this.win = Roo.get(this.frameId).dom.contentWindow;
26360 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26364 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26365 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26370 initEditor : function(){
26371 //console.log("INIT EDITOR");
26372 this.assignDocWin();
26376 this.doc.designMode="on";
26378 this.doc.write(this.getDocMarkup());
26381 var dbody = (this.doc.body || this.doc.documentElement);
26382 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26383 // this copies styles from the containing element into thsi one..
26384 // not sure why we need all of this..
26385 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26387 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26388 //ss['background-attachment'] = 'fixed'; // w3c
26389 dbody.bgProperties = 'fixed'; // ie
26390 //Roo.DomHelper.applyStyles(dbody, ss);
26391 Roo.EventManager.on(this.doc, {
26392 //'mousedown': this.onEditorEvent,
26393 'mouseup': this.onEditorEvent,
26394 'dblclick': this.onEditorEvent,
26395 'click': this.onEditorEvent,
26396 'keyup': this.onEditorEvent,
26401 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26403 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26404 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26406 this.initialized = true;
26408 this.owner.fireEvent('initialize', this);
26413 onDestroy : function(){
26419 //for (var i =0; i < this.toolbars.length;i++) {
26420 // // fixme - ask toolbars for heights?
26421 // this.toolbars[i].onDestroy();
26424 //this.wrap.dom.innerHTML = '';
26425 //this.wrap.remove();
26430 onFirstFocus : function(){
26432 this.assignDocWin();
26435 this.activated = true;
26438 if(Roo.isGecko){ // prevent silly gecko errors
26440 var s = this.win.getSelection();
26441 if(!s.focusNode || s.focusNode.nodeType != 3){
26442 var r = s.getRangeAt(0);
26443 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26448 this.execCmd('useCSS', true);
26449 this.execCmd('styleWithCSS', false);
26452 this.owner.fireEvent('activate', this);
26456 adjustFont: function(btn){
26457 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26458 //if(Roo.isSafari){ // safari
26461 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26462 if(Roo.isSafari){ // safari
26463 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26464 v = (v < 10) ? 10 : v;
26465 v = (v > 48) ? 48 : v;
26466 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26471 v = Math.max(1, v+adjust);
26473 this.execCmd('FontSize', v );
26476 onEditorEvent : function(e)
26478 this.owner.fireEvent('editorevent', this, e);
26479 // this.updateToolbar();
26480 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26483 insertTag : function(tg)
26485 // could be a bit smarter... -> wrap the current selected tRoo..
26486 if (tg.toLowerCase() == 'span' ||
26487 tg.toLowerCase() == 'code' ||
26488 tg.toLowerCase() == 'sup' ||
26489 tg.toLowerCase() == 'sub'
26492 range = this.createRange(this.getSelection());
26493 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26494 wrappingNode.appendChild(range.extractContents());
26495 range.insertNode(wrappingNode);
26502 this.execCmd("formatblock", tg);
26506 insertText : function(txt)
26510 var range = this.createRange();
26511 range.deleteContents();
26512 //alert(Sender.getAttribute('label'));
26514 range.insertNode(this.doc.createTextNode(txt));
26520 * Executes a Midas editor command on the editor document and performs necessary focus and
26521 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26522 * @param {String} cmd The Midas command
26523 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26525 relayCmd : function(cmd, value){
26527 this.execCmd(cmd, value);
26528 this.owner.fireEvent('editorevent', this);
26529 //this.updateToolbar();
26530 this.owner.deferFocus();
26534 * Executes a Midas editor command directly on the editor document.
26535 * For visual commands, you should use {@link #relayCmd} instead.
26536 * <b>This should only be called after the editor is initialized.</b>
26537 * @param {String} cmd The Midas command
26538 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26540 execCmd : function(cmd, value){
26541 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26548 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26550 * @param {String} text | dom node..
26552 insertAtCursor : function(text)
26555 if(!this.activated){
26561 var r = this.doc.selection.createRange();
26572 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26576 // from jquery ui (MIT licenced)
26578 var win = this.win;
26580 if (win.getSelection && win.getSelection().getRangeAt) {
26581 range = win.getSelection().getRangeAt(0);
26582 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26583 range.insertNode(node);
26584 } else if (win.document.selection && win.document.selection.createRange) {
26585 // no firefox support
26586 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26587 win.document.selection.createRange().pasteHTML(txt);
26589 // no firefox support
26590 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26591 this.execCmd('InsertHTML', txt);
26600 mozKeyPress : function(e){
26602 var c = e.getCharCode(), cmd;
26605 c = String.fromCharCode(c).toLowerCase();
26619 this.cleanUpPaste.defer(100, this);
26627 e.preventDefault();
26635 fixKeys : function(){ // load time branching for fastest keydown performance
26637 return function(e){
26638 var k = e.getKey(), r;
26641 r = this.doc.selection.createRange();
26644 r.pasteHTML('    ');
26651 r = this.doc.selection.createRange();
26653 var target = r.parentElement();
26654 if(!target || target.tagName.toLowerCase() != 'li'){
26656 r.pasteHTML('<br />');
26662 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26663 this.cleanUpPaste.defer(100, this);
26669 }else if(Roo.isOpera){
26670 return function(e){
26671 var k = e.getKey();
26675 this.execCmd('InsertHTML','    ');
26678 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679 this.cleanUpPaste.defer(100, this);
26684 }else if(Roo.isSafari){
26685 return function(e){
26686 var k = e.getKey();
26690 this.execCmd('InsertText','\t');
26694 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695 this.cleanUpPaste.defer(100, this);
26703 getAllAncestors: function()
26705 var p = this.getSelectedNode();
26708 a.push(p); // push blank onto stack..
26709 p = this.getParentElement();
26713 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26717 a.push(this.doc.body);
26721 lastSelNode : false,
26724 getSelection : function()
26726 this.assignDocWin();
26727 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26730 getSelectedNode: function()
26732 // this may only work on Gecko!!!
26734 // should we cache this!!!!
26739 var range = this.createRange(this.getSelection()).cloneRange();
26742 var parent = range.parentElement();
26744 var testRange = range.duplicate();
26745 testRange.moveToElementText(parent);
26746 if (testRange.inRange(range)) {
26749 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26752 parent = parent.parentElement;
26757 // is ancestor a text element.
26758 var ac = range.commonAncestorContainer;
26759 if (ac.nodeType == 3) {
26760 ac = ac.parentNode;
26763 var ar = ac.childNodes;
26766 var other_nodes = [];
26767 var has_other_nodes = false;
26768 for (var i=0;i<ar.length;i++) {
26769 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26772 // fullly contained node.
26774 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26779 // probably selected..
26780 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26781 other_nodes.push(ar[i]);
26785 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26790 has_other_nodes = true;
26792 if (!nodes.length && other_nodes.length) {
26793 nodes= other_nodes;
26795 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26801 createRange: function(sel)
26803 // this has strange effects when using with
26804 // top toolbar - not sure if it's a great idea.
26805 //this.editor.contentWindow.focus();
26806 if (typeof sel != "undefined") {
26808 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26810 return this.doc.createRange();
26813 return this.doc.createRange();
26816 getParentElement: function()
26819 this.assignDocWin();
26820 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26822 var range = this.createRange(sel);
26825 var p = range.commonAncestorContainer;
26826 while (p.nodeType == 3) { // text node
26837 * Range intersection.. the hard stuff...
26841 * [ -- selected range --- ]
26845 * if end is before start or hits it. fail.
26846 * if start is after end or hits it fail.
26848 * if either hits (but other is outside. - then it's not
26854 // @see http://www.thismuchiknow.co.uk/?p=64.
26855 rangeIntersectsNode : function(range, node)
26857 var nodeRange = node.ownerDocument.createRange();
26859 nodeRange.selectNode(node);
26861 nodeRange.selectNodeContents(node);
26864 var rangeStartRange = range.cloneRange();
26865 rangeStartRange.collapse(true);
26867 var rangeEndRange = range.cloneRange();
26868 rangeEndRange.collapse(false);
26870 var nodeStartRange = nodeRange.cloneRange();
26871 nodeStartRange.collapse(true);
26873 var nodeEndRange = nodeRange.cloneRange();
26874 nodeEndRange.collapse(false);
26876 return rangeStartRange.compareBoundaryPoints(
26877 Range.START_TO_START, nodeEndRange) == -1 &&
26878 rangeEndRange.compareBoundaryPoints(
26879 Range.START_TO_START, nodeStartRange) == 1;
26883 rangeCompareNode : function(range, node)
26885 var nodeRange = node.ownerDocument.createRange();
26887 nodeRange.selectNode(node);
26889 nodeRange.selectNodeContents(node);
26893 range.collapse(true);
26895 nodeRange.collapse(true);
26897 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26898 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26900 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26902 var nodeIsBefore = ss == 1;
26903 var nodeIsAfter = ee == -1;
26905 if (nodeIsBefore && nodeIsAfter) {
26908 if (!nodeIsBefore && nodeIsAfter) {
26909 return 1; //right trailed.
26912 if (nodeIsBefore && !nodeIsAfter) {
26913 return 2; // left trailed.
26919 // private? - in a new class?
26920 cleanUpPaste : function()
26922 // cleans up the whole document..
26923 Roo.log('cleanuppaste');
26925 this.cleanUpChildren(this.doc.body);
26926 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26927 if (clean != this.doc.body.innerHTML) {
26928 this.doc.body.innerHTML = clean;
26933 cleanWordChars : function(input) {// change the chars to hex code
26934 var he = Roo.HtmlEditorCore;
26936 var output = input;
26937 Roo.each(he.swapCodes, function(sw) {
26938 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26940 output = output.replace(swapper, sw[1]);
26947 cleanUpChildren : function (n)
26949 if (!n.childNodes.length) {
26952 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26953 this.cleanUpChild(n.childNodes[i]);
26960 cleanUpChild : function (node)
26963 //console.log(node);
26964 if (node.nodeName == "#text") {
26965 // clean up silly Windows -- stuff?
26968 if (node.nodeName == "#comment") {
26969 if (!this.allowComments) {
26970 node.parentNode.removeChild(node);
26972 // clean up silly Windows -- stuff?
26975 var lcname = node.tagName.toLowerCase();
26976 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26977 // whitelist of tags..
26979 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26981 node.parentNode.removeChild(node);
26986 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26988 // spans with no attributes - just remove them..
26989 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26990 remove_keep_children = true;
26993 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26994 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26996 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26997 // remove_keep_children = true;
27000 if (remove_keep_children) {
27001 this.cleanUpChildren(node);
27002 // inserts everything just before this node...
27003 while (node.childNodes.length) {
27004 var cn = node.childNodes[0];
27005 node.removeChild(cn);
27006 node.parentNode.insertBefore(cn, node);
27008 node.parentNode.removeChild(node);
27012 if (!node.attributes || !node.attributes.length) {
27017 this.cleanUpChildren(node);
27021 function cleanAttr(n,v)
27024 if (v.match(/^\./) || v.match(/^\//)) {
27027 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27030 if (v.match(/^#/)) {
27033 if (v.match(/^\{/)) { // allow template editing.
27036 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27037 node.removeAttribute(n);
27041 var cwhite = this.cwhite;
27042 var cblack = this.cblack;
27044 function cleanStyle(n,v)
27046 if (v.match(/expression/)) { //XSS?? should we even bother..
27047 node.removeAttribute(n);
27051 var parts = v.split(/;/);
27054 Roo.each(parts, function(p) {
27055 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27059 var l = p.split(':').shift().replace(/\s+/g,'');
27060 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27062 if ( cwhite.length && cblack.indexOf(l) > -1) {
27063 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27064 //node.removeAttribute(n);
27068 // only allow 'c whitelisted system attributes'
27069 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27070 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27071 //node.removeAttribute(n);
27081 if (clean.length) {
27082 node.setAttribute(n, clean.join(';'));
27084 node.removeAttribute(n);
27090 for (var i = node.attributes.length-1; i > -1 ; i--) {
27091 var a = node.attributes[i];
27094 if (a.name.toLowerCase().substr(0,2)=='on') {
27095 node.removeAttribute(a.name);
27098 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27099 node.removeAttribute(a.name);
27102 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27103 cleanAttr(a.name,a.value); // fixme..
27106 if (a.name == 'style') {
27107 cleanStyle(a.name,a.value);
27110 /// clean up MS crap..
27111 // tecnically this should be a list of valid class'es..
27114 if (a.name == 'class') {
27115 if (a.value.match(/^Mso/)) {
27116 node.removeAttribute('class');
27119 if (a.value.match(/^body$/)) {
27120 node.removeAttribute('class');
27131 this.cleanUpChildren(node);
27137 * Clean up MS wordisms...
27139 cleanWord : function(node)
27142 this.cleanWord(this.doc.body);
27147 node.nodeName == 'SPAN' &&
27148 !node.hasAttributes() &&
27149 node.childNodes.length == 1 &&
27150 node.firstChild.nodeName == "#text"
27152 var textNode = node.firstChild;
27153 node.removeChild(textNode);
27154 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27155 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27157 node.parentNode.insertBefore(textNode, node);
27158 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27159 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27161 node.parentNode.removeChild(node);
27164 if (node.nodeName == "#text") {
27165 // clean up silly Windows -- stuff?
27168 if (node.nodeName == "#comment") {
27169 node.parentNode.removeChild(node);
27170 // clean up silly Windows -- stuff?
27174 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27175 node.parentNode.removeChild(node);
27178 //Roo.log(node.tagName);
27179 // remove - but keep children..
27180 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27181 //Roo.log('-- removed');
27182 while (node.childNodes.length) {
27183 var cn = node.childNodes[0];
27184 node.removeChild(cn);
27185 node.parentNode.insertBefore(cn, node);
27186 // move node to parent - and clean it..
27187 this.cleanWord(cn);
27189 node.parentNode.removeChild(node);
27190 /// no need to iterate chidlren = it's got none..
27191 //this.iterateChildren(node, this.cleanWord);
27195 if (node.className.length) {
27197 var cn = node.className.split(/\W+/);
27199 Roo.each(cn, function(cls) {
27200 if (cls.match(/Mso[a-zA-Z]+/)) {
27205 node.className = cna.length ? cna.join(' ') : '';
27207 node.removeAttribute("class");
27211 if (node.hasAttribute("lang")) {
27212 node.removeAttribute("lang");
27215 if (node.hasAttribute("style")) {
27217 var styles = node.getAttribute("style").split(";");
27219 Roo.each(styles, function(s) {
27220 if (!s.match(/:/)) {
27223 var kv = s.split(":");
27224 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27227 // what ever is left... we allow.
27230 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27231 if (!nstyle.length) {
27232 node.removeAttribute('style');
27235 this.iterateChildren(node, this.cleanWord);
27241 * iterateChildren of a Node, calling fn each time, using this as the scole..
27242 * @param {DomNode} node node to iterate children of.
27243 * @param {Function} fn method of this class to call on each item.
27245 iterateChildren : function(node, fn)
27247 if (!node.childNodes.length) {
27250 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27251 fn.call(this, node.childNodes[i])
27257 * cleanTableWidths.
27259 * Quite often pasting from word etc.. results in tables with column and widths.
27260 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27263 cleanTableWidths : function(node)
27268 this.cleanTableWidths(this.doc.body);
27273 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27276 Roo.log(node.tagName);
27277 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27278 this.iterateChildren(node, this.cleanTableWidths);
27281 if (node.hasAttribute('width')) {
27282 node.removeAttribute('width');
27286 if (node.hasAttribute("style")) {
27289 var styles = node.getAttribute("style").split(";");
27291 Roo.each(styles, function(s) {
27292 if (!s.match(/:/)) {
27295 var kv = s.split(":");
27296 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27299 // what ever is left... we allow.
27302 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27303 if (!nstyle.length) {
27304 node.removeAttribute('style');
27308 this.iterateChildren(node, this.cleanTableWidths);
27316 domToHTML : function(currentElement, depth, nopadtext) {
27318 depth = depth || 0;
27319 nopadtext = nopadtext || false;
27321 if (!currentElement) {
27322 return this.domToHTML(this.doc.body);
27325 //Roo.log(currentElement);
27327 var allText = false;
27328 var nodeName = currentElement.nodeName;
27329 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27331 if (nodeName == '#text') {
27333 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27338 if (nodeName != 'BODY') {
27341 // Prints the node tagName, such as <A>, <IMG>, etc
27344 for(i = 0; i < currentElement.attributes.length;i++) {
27346 var aname = currentElement.attributes.item(i).name;
27347 if (!currentElement.attributes.item(i).value.length) {
27350 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27353 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27362 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27365 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27370 // Traverse the tree
27372 var currentElementChild = currentElement.childNodes.item(i);
27373 var allText = true;
27374 var innerHTML = '';
27376 while (currentElementChild) {
27377 // Formatting code (indent the tree so it looks nice on the screen)
27378 var nopad = nopadtext;
27379 if (lastnode == 'SPAN') {
27383 if (currentElementChild.nodeName == '#text') {
27384 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27385 toadd = nopadtext ? toadd : toadd.trim();
27386 if (!nopad && toadd.length > 80) {
27387 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27389 innerHTML += toadd;
27392 currentElementChild = currentElement.childNodes.item(i);
27398 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27400 // Recursively traverse the tree structure of the child node
27401 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27402 lastnode = currentElementChild.nodeName;
27404 currentElementChild=currentElement.childNodes.item(i);
27410 // The remaining code is mostly for formatting the tree
27411 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27416 ret+= "</"+tagName+">";
27422 applyBlacklists : function()
27424 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27425 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27429 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27430 if (b.indexOf(tag) > -1) {
27433 this.white.push(tag);
27437 Roo.each(w, function(tag) {
27438 if (b.indexOf(tag) > -1) {
27441 if (this.white.indexOf(tag) > -1) {
27444 this.white.push(tag);
27449 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27450 if (w.indexOf(tag) > -1) {
27453 this.black.push(tag);
27457 Roo.each(b, function(tag) {
27458 if (w.indexOf(tag) > -1) {
27461 if (this.black.indexOf(tag) > -1) {
27464 this.black.push(tag);
27469 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27470 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27474 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27475 if (b.indexOf(tag) > -1) {
27478 this.cwhite.push(tag);
27482 Roo.each(w, function(tag) {
27483 if (b.indexOf(tag) > -1) {
27486 if (this.cwhite.indexOf(tag) > -1) {
27489 this.cwhite.push(tag);
27494 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27495 if (w.indexOf(tag) > -1) {
27498 this.cblack.push(tag);
27502 Roo.each(b, function(tag) {
27503 if (w.indexOf(tag) > -1) {
27506 if (this.cblack.indexOf(tag) > -1) {
27509 this.cblack.push(tag);
27514 setStylesheets : function(stylesheets)
27516 if(typeof(stylesheets) == 'string'){
27517 Roo.get(this.iframe.contentDocument.head).createChild({
27519 rel : 'stylesheet',
27528 Roo.each(stylesheets, function(s) {
27533 Roo.get(_this.iframe.contentDocument.head).createChild({
27535 rel : 'stylesheet',
27544 removeStylesheets : function()
27548 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27553 setStyle : function(style)
27555 Roo.get(this.iframe.contentDocument.head).createChild({
27564 // hide stuff that is not compatible
27578 * @event specialkey
27582 * @cfg {String} fieldClass @hide
27585 * @cfg {String} focusClass @hide
27588 * @cfg {String} autoCreate @hide
27591 * @cfg {String} inputType @hide
27594 * @cfg {String} invalidClass @hide
27597 * @cfg {String} invalidText @hide
27600 * @cfg {String} msgFx @hide
27603 * @cfg {String} validateOnBlur @hide
27607 Roo.HtmlEditorCore.white = [
27608 'area', 'br', 'img', 'input', 'hr', 'wbr',
27610 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27611 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27612 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27613 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27614 'table', 'ul', 'xmp',
27616 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27619 'dir', 'menu', 'ol', 'ul', 'dl',
27625 Roo.HtmlEditorCore.black = [
27626 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27628 'base', 'basefont', 'bgsound', 'blink', 'body',
27629 'frame', 'frameset', 'head', 'html', 'ilayer',
27630 'iframe', 'layer', 'link', 'meta', 'object',
27631 'script', 'style' ,'title', 'xml' // clean later..
27633 Roo.HtmlEditorCore.clean = [
27634 'script', 'style', 'title', 'xml'
27636 Roo.HtmlEditorCore.remove = [
27641 Roo.HtmlEditorCore.ablack = [
27645 Roo.HtmlEditorCore.aclean = [
27646 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27650 Roo.HtmlEditorCore.pwhite= [
27651 'http', 'https', 'mailto'
27654 // white listed style attributes.
27655 Roo.HtmlEditorCore.cwhite= [
27656 // 'text-align', /// default is to allow most things..
27662 // black listed style attributes.
27663 Roo.HtmlEditorCore.cblack= [
27664 // 'font-size' -- this can be set by the project
27668 Roo.HtmlEditorCore.swapCodes =[
27669 [ 8211, "–" ],
27670 [ 8212, "—" ],
27687 * @class Roo.bootstrap.form.HtmlEditor
27688 * @extends Roo.bootstrap.form.TextArea
27689 * Bootstrap HtmlEditor class
27692 * Create a new HtmlEditor
27693 * @param {Object} config The config object
27696 Roo.bootstrap.form.HtmlEditor = function(config){
27697 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27698 if (!this.toolbars) {
27699 this.toolbars = [];
27702 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27705 * @event initialize
27706 * Fires when the editor is fully initialized (including the iframe)
27707 * @param {HtmlEditor} this
27712 * Fires when the editor is first receives the focus. Any insertion must wait
27713 * until after this event.
27714 * @param {HtmlEditor} this
27718 * @event beforesync
27719 * Fires before the textarea is updated with content from the editor iframe. Return false
27720 * to cancel the sync.
27721 * @param {HtmlEditor} this
27722 * @param {String} html
27726 * @event beforepush
27727 * Fires before the iframe editor is updated with content from the textarea. Return false
27728 * to cancel the push.
27729 * @param {HtmlEditor} this
27730 * @param {String} html
27735 * Fires when the textarea is updated with content from the editor iframe.
27736 * @param {HtmlEditor} this
27737 * @param {String} html
27742 * Fires when the iframe editor is updated with content from the textarea.
27743 * @param {HtmlEditor} this
27744 * @param {String} html
27748 * @event editmodechange
27749 * Fires when the editor switches edit modes
27750 * @param {HtmlEditor} this
27751 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27753 editmodechange: true,
27755 * @event editorevent
27756 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27757 * @param {HtmlEditor} this
27761 * @event firstfocus
27762 * Fires when on first focus - needed by toolbars..
27763 * @param {HtmlEditor} this
27768 * Auto save the htmlEditor value as a file into Events
27769 * @param {HtmlEditor} this
27773 * @event savedpreview
27774 * preview the saved version of htmlEditor
27775 * @param {HtmlEditor} this
27782 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27786 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27791 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27796 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27801 * @cfg {Number} height (in pixels)
27805 * @cfg {Number} width (in pixels)
27810 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27813 stylesheets: false,
27818 // private properties
27819 validationEvent : false,
27821 initialized : false,
27824 onFocus : Roo.emptyFn,
27826 hideMode:'offsets',
27828 tbContainer : false,
27832 toolbarContainer :function() {
27833 return this.wrap.select('.x-html-editor-tb',true).first();
27837 * Protected method that will not generally be called directly. It
27838 * is called when the editor creates its toolbar. Override this method if you need to
27839 * add custom toolbar buttons.
27840 * @param {HtmlEditor} editor
27842 createToolbar : function(){
27843 Roo.log('renewing');
27844 Roo.log("create toolbars");
27846 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27847 this.toolbars[0].render(this.toolbarContainer());
27851 // if (!editor.toolbars || !editor.toolbars.length) {
27852 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27855 // for (var i =0 ; i < editor.toolbars.length;i++) {
27856 // editor.toolbars[i] = Roo.factory(
27857 // typeof(editor.toolbars[i]) == 'string' ?
27858 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27859 // Roo.bootstrap.form.HtmlEditor);
27860 // editor.toolbars[i].init(editor);
27866 onRender : function(ct, position)
27868 // Roo.log("Call onRender: " + this.xtype);
27870 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27872 this.wrap = this.inputEl().wrap({
27873 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27876 this.editorcore.onRender(ct, position);
27878 if (this.resizable) {
27879 this.resizeEl = new Roo.Resizable(this.wrap, {
27883 minHeight : this.height,
27884 height: this.height,
27885 handles : this.resizable,
27888 resize : function(r, w, h) {
27889 _t.onResize(w,h); // -something
27895 this.createToolbar(this);
27898 if(!this.width && this.resizable){
27899 this.setSize(this.wrap.getSize());
27901 if (this.resizeEl) {
27902 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27903 // should trigger onReize..
27909 onResize : function(w, h)
27911 Roo.log('resize: ' +w + ',' + h );
27912 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27916 if(this.inputEl() ){
27917 if(typeof w == 'number'){
27918 var aw = w - this.wrap.getFrameWidth('lr');
27919 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27922 if(typeof h == 'number'){
27923 var tbh = -11; // fixme it needs to tool bar size!
27924 for (var i =0; i < this.toolbars.length;i++) {
27925 // fixme - ask toolbars for heights?
27926 tbh += this.toolbars[i].el.getHeight();
27927 //if (this.toolbars[i].footer) {
27928 // tbh += this.toolbars[i].footer.el.getHeight();
27936 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27937 ah -= 5; // knock a few pixes off for look..
27938 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27942 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27943 this.editorcore.onResize(ew,eh);
27948 * Toggles the editor between standard and source edit mode.
27949 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27951 toggleSourceEdit : function(sourceEditMode)
27953 this.editorcore.toggleSourceEdit(sourceEditMode);
27955 if(this.editorcore.sourceEditMode){
27956 Roo.log('editor - showing textarea');
27959 // Roo.log(this.syncValue());
27961 this.inputEl().removeClass(['hide', 'x-hidden']);
27962 this.inputEl().dom.removeAttribute('tabIndex');
27963 this.inputEl().focus();
27965 Roo.log('editor - hiding textarea');
27967 // Roo.log(this.pushValue());
27970 this.inputEl().addClass(['hide', 'x-hidden']);
27971 this.inputEl().dom.setAttribute('tabIndex', -1);
27972 //this.deferFocus();
27975 if(this.resizable){
27976 this.setSize(this.wrap.getSize());
27979 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27982 // private (for BoxComponent)
27983 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27985 // private (for BoxComponent)
27986 getResizeEl : function(){
27990 // private (for BoxComponent)
27991 getPositionEl : function(){
27996 initEvents : function(){
27997 this.originalValue = this.getValue();
28001 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28004 // markInvalid : Roo.emptyFn,
28006 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28009 // clearInvalid : Roo.emptyFn,
28011 setValue : function(v){
28012 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28013 this.editorcore.pushValue();
28018 deferFocus : function(){
28019 this.focus.defer(10, this);
28023 focus : function(){
28024 this.editorcore.focus();
28030 onDestroy : function(){
28036 for (var i =0; i < this.toolbars.length;i++) {
28037 // fixme - ask toolbars for heights?
28038 this.toolbars[i].onDestroy();
28041 this.wrap.dom.innerHTML = '';
28042 this.wrap.remove();
28047 onFirstFocus : function(){
28048 //Roo.log("onFirstFocus");
28049 this.editorcore.onFirstFocus();
28050 for (var i =0; i < this.toolbars.length;i++) {
28051 this.toolbars[i].onFirstFocus();
28057 syncValue : function()
28059 this.editorcore.syncValue();
28062 pushValue : function()
28064 this.editorcore.pushValue();
28068 // hide stuff that is not compatible
28082 * @event specialkey
28086 * @cfg {String} fieldClass @hide
28089 * @cfg {String} focusClass @hide
28092 * @cfg {String} autoCreate @hide
28095 * @cfg {String} inputType @hide
28099 * @cfg {String} invalidText @hide
28102 * @cfg {String} msgFx @hide
28105 * @cfg {String} validateOnBlur @hide
28114 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28116 * @class Roo.bootstrap.form.HtmlEditorToolbar1
28117 * @extends Roo.bootstrap.nav.Simplebar
28123 new Roo.bootstrap.form.HtmlEditor({
28126 new Roo.bootstrap.form.HtmlEditorToolbar1({
28127 disable : { fonts: 1 , format: 1, ..., ... , ...],
28133 * @cfg {Object} disable List of elements to disable..
28134 * @cfg {Array} btns List of additional buttons.
28138 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28141 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28144 Roo.apply(this, config);
28146 // default disabled, based on 'good practice'..
28147 this.disable = this.disable || {};
28148 Roo.applyIf(this.disable, {
28151 specialElements : true
28153 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28155 this.editor = config.editor;
28156 this.editorcore = config.editor.editorcore;
28158 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28160 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28161 // dont call parent... till later.
28163 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28168 editorcore : false,
28173 "h1","h2","h3","h4","h5","h6",
28175 "abbr", "acronym", "address", "cite", "samp", "var",
28179 onRender : function(ct, position)
28181 // Roo.log("Call onRender: " + this.xtype);
28183 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28185 this.el.dom.style.marginBottom = '0';
28187 var editorcore = this.editorcore;
28188 var editor= this.editor;
28191 var btn = function(id,cmd , toggle, handler, html){
28193 var event = toggle ? 'toggle' : 'click';
28198 xns: Roo.bootstrap,
28202 enableToggle:toggle !== false,
28204 pressed : toggle ? false : null,
28207 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28208 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28214 // var cb_box = function...
28219 xns: Roo.bootstrap,
28224 xns: Roo.bootstrap,
28228 Roo.each(this.formats, function(f) {
28229 style.menu.items.push({
28231 xns: Roo.bootstrap,
28232 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28237 editorcore.insertTag(this.tagname);
28244 children.push(style);
28246 btn('bold',false,true);
28247 btn('italic',false,true);
28248 btn('align-left', 'justifyleft',true);
28249 btn('align-center', 'justifycenter',true);
28250 btn('align-right' , 'justifyright',true);
28251 btn('link', false, false, function(btn) {
28252 //Roo.log("create link?");
28253 var url = prompt(this.createLinkText, this.defaultLinkValue);
28254 if(url && url != 'http:/'+'/'){
28255 this.editorcore.relayCmd('createlink', url);
28258 btn('list','insertunorderedlist',true);
28259 btn('pencil', false,true, function(btn){
28261 this.toggleSourceEdit(btn.pressed);
28264 if (this.editor.btns.length > 0) {
28265 for (var i = 0; i<this.editor.btns.length; i++) {
28266 children.push(this.editor.btns[i]);
28274 xns: Roo.bootstrap,
28279 xns: Roo.bootstrap,
28284 cog.menu.items.push({
28286 xns: Roo.bootstrap,
28287 html : Clean styles,
28292 editorcore.insertTag(this.tagname);
28301 this.xtype = 'NavSimplebar';
28303 for(var i=0;i< children.length;i++) {
28305 this.buttons.add(this.addxtypeChild(children[i]));
28309 editor.on('editorevent', this.updateToolbar, this);
28311 onBtnClick : function(id)
28313 this.editorcore.relayCmd(id);
28314 this.editorcore.focus();
28318 * Protected method that will not generally be called directly. It triggers
28319 * a toolbar update by reading the markup state of the current selection in the editor.
28321 updateToolbar: function(){
28323 if(!this.editorcore.activated){
28324 this.editor.onFirstFocus(); // is this neeed?
28328 var btns = this.buttons;
28329 var doc = this.editorcore.doc;
28330 btns.get('bold').setActive(doc.queryCommandState('bold'));
28331 btns.get('italic').setActive(doc.queryCommandState('italic'));
28332 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28334 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28335 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28336 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28338 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28339 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28342 var ans = this.editorcore.getAllAncestors();
28343 if (this.formatCombo) {
28346 var store = this.formatCombo.store;
28347 this.formatCombo.setValue("");
28348 for (var i =0; i < ans.length;i++) {
28349 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28351 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28359 // hides menus... - so this cant be on a menu...
28360 Roo.bootstrap.MenuMgr.hideAll();
28362 Roo.bootstrap.menu.Manager.hideAll();
28363 //this.editorsyncValue();
28365 onFirstFocus: function() {
28366 this.buttons.each(function(item){
28370 toggleSourceEdit : function(sourceEditMode){
28373 if(sourceEditMode){
28374 Roo.log("disabling buttons");
28375 this.buttons.each( function(item){
28376 if(item.cmd != 'pencil'){
28382 Roo.log("enabling buttons");
28383 if(this.editorcore.initialized){
28384 this.buttons.each( function(item){
28390 Roo.log("calling toggole on editor");
28391 // tell the editor that it's been pressed..
28392 this.editor.toggleSourceEdit(sourceEditMode);
28406 * @class Roo.bootstrap.form.Markdown
28407 * @extends Roo.bootstrap.form.TextArea
28408 * Bootstrap Showdown editable area
28409 * @cfg {string} content
28412 * Create a new Showdown
28415 Roo.bootstrap.form.Markdown = function(config){
28416 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28420 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28424 initEvents : function()
28427 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28428 this.markdownEl = this.el.createChild({
28429 cls : 'roo-markdown-area'
28431 this.inputEl().addClass('d-none');
28432 if (this.getValue() == '') {
28433 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28436 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28438 this.markdownEl.on('click', this.toggleTextEdit, this);
28439 this.on('blur', this.toggleTextEdit, this);
28440 this.on('specialkey', this.resizeTextArea, this);
28443 toggleTextEdit : function()
28445 var sh = this.markdownEl.getHeight();
28446 this.inputEl().addClass('d-none');
28447 this.markdownEl.addClass('d-none');
28448 if (!this.editing) {
28450 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28451 this.inputEl().removeClass('d-none');
28452 this.inputEl().focus();
28453 this.editing = true;
28456 // show showdown...
28457 this.updateMarkdown();
28458 this.markdownEl.removeClass('d-none');
28459 this.editing = false;
28462 updateMarkdown : function()
28464 if (this.getValue() == '') {
28465 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28469 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28472 resizeTextArea: function () {
28475 Roo.log([sh, this.getValue().split("\n").length * 30]);
28476 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28478 setValue : function(val)
28480 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28481 if (!this.editing) {
28482 this.updateMarkdown();
28488 if (!this.editing) {
28489 this.toggleTextEdit();
28497 * Ext JS Library 1.1.1
28498 * Copyright(c) 2006-2007, Ext JS, LLC.
28500 * Originally Released Under LGPL - original licence link has changed is not relivant.
28503 * <script type="text/javascript">
28507 * @class Roo.bootstrap.PagingToolbar
28508 * @extends Roo.bootstrap.nav.Simplebar
28509 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28511 * Create a new PagingToolbar
28512 * @param {Object} config The config object
28513 * @param {Roo.data.Store} store
28515 Roo.bootstrap.PagingToolbar = function(config)
28517 // old args format still supported... - xtype is prefered..
28518 // created from xtype...
28520 this.ds = config.dataSource;
28522 if (config.store && !this.ds) {
28523 this.store= Roo.factory(config.store, Roo.data);
28524 this.ds = this.store;
28525 this.ds.xmodule = this.xmodule || false;
28528 this.toolbarItems = [];
28529 if (config.items) {
28530 this.toolbarItems = config.items;
28533 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28538 this.bind(this.ds);
28541 if (Roo.bootstrap.version == 4) {
28542 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28544 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28549 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28551 * @cfg {Roo.bootstrap.Button} buttons[]
28552 * Buttons for the toolbar
28555 * @cfg {Roo.data.Store} store
28556 * The underlying data store providing the paged data
28559 * @cfg {String/HTMLElement/Element} container
28560 * container The id or element that will contain the toolbar
28563 * @cfg {Boolean} displayInfo
28564 * True to display the displayMsg (defaults to false)
28567 * @cfg {Number} pageSize
28568 * The number of records to display per page (defaults to 20)
28572 * @cfg {String} displayMsg
28573 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28575 displayMsg : 'Displaying {0} - {1} of {2}',
28577 * @cfg {String} emptyMsg
28578 * The message to display when no records are found (defaults to "No data to display")
28580 emptyMsg : 'No data to display',
28582 * Customizable piece of the default paging text (defaults to "Page")
28585 beforePageText : "Page",
28587 * Customizable piece of the default paging text (defaults to "of %0")
28590 afterPageText : "of {0}",
28592 * Customizable piece of the default paging text (defaults to "First Page")
28595 firstText : "First Page",
28597 * Customizable piece of the default paging text (defaults to "Previous Page")
28600 prevText : "Previous Page",
28602 * Customizable piece of the default paging text (defaults to "Next Page")
28605 nextText : "Next Page",
28607 * Customizable piece of the default paging text (defaults to "Last Page")
28610 lastText : "Last Page",
28612 * Customizable piece of the default paging text (defaults to "Refresh")
28615 refreshText : "Refresh",
28619 onRender : function(ct, position)
28621 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28622 this.navgroup.parentId = this.id;
28623 this.navgroup.onRender(this.el, null);
28624 // add the buttons to the navgroup
28626 if(this.displayInfo){
28627 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28628 this.displayEl = this.el.select('.x-paging-info', true).first();
28629 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28630 // this.displayEl = navel.el.select('span',true).first();
28636 Roo.each(_this.buttons, function(e){ // this might need to use render????
28637 Roo.factory(e).render(_this.el);
28641 Roo.each(_this.toolbarItems, function(e) {
28642 _this.navgroup.addItem(e);
28646 this.first = this.navgroup.addItem({
28647 tooltip: this.firstText,
28648 cls: "prev btn-outline-secondary",
28649 html : ' <i class="fa fa-step-backward"></i>',
28651 preventDefault: true,
28652 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28655 this.prev = this.navgroup.addItem({
28656 tooltip: this.prevText,
28657 cls: "prev btn-outline-secondary",
28658 html : ' <i class="fa fa-backward"></i>',
28660 preventDefault: true,
28661 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28663 //this.addSeparator();
28666 var field = this.navgroup.addItem( {
28668 cls : 'x-paging-position btn-outline-secondary',
28670 html : this.beforePageText +
28671 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28672 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28675 this.field = field.el.select('input', true).first();
28676 this.field.on("keydown", this.onPagingKeydown, this);
28677 this.field.on("focus", function(){this.dom.select();});
28680 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28681 //this.field.setHeight(18);
28682 //this.addSeparator();
28683 this.next = this.navgroup.addItem({
28684 tooltip: this.nextText,
28685 cls: "next btn-outline-secondary",
28686 html : ' <i class="fa fa-forward"></i>',
28688 preventDefault: true,
28689 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28691 this.last = this.navgroup.addItem({
28692 tooltip: this.lastText,
28693 html : ' <i class="fa fa-step-forward"></i>',
28694 cls: "next btn-outline-secondary",
28696 preventDefault: true,
28697 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28699 //this.addSeparator();
28700 this.loading = this.navgroup.addItem({
28701 tooltip: this.refreshText,
28702 cls: "btn-outline-secondary",
28703 html : ' <i class="fa fa-refresh"></i>',
28704 preventDefault: true,
28705 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28711 updateInfo : function(){
28712 if(this.displayEl){
28713 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28714 var msg = count == 0 ?
28718 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28720 this.displayEl.update(msg);
28725 onLoad : function(ds, r, o)
28727 this.cursor = o.params && o.params.start ? o.params.start : 0;
28729 var d = this.getPageData(),
28734 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28735 this.field.dom.value = ap;
28736 this.first.setDisabled(ap == 1);
28737 this.prev.setDisabled(ap == 1);
28738 this.next.setDisabled(ap == ps);
28739 this.last.setDisabled(ap == ps);
28740 this.loading.enable();
28745 getPageData : function(){
28746 var total = this.ds.getTotalCount();
28749 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28750 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28755 onLoadError : function(){
28756 this.loading.enable();
28760 onPagingKeydown : function(e){
28761 var k = e.getKey();
28762 var d = this.getPageData();
28764 var v = this.field.dom.value, pageNum;
28765 if(!v || isNaN(pageNum = parseInt(v, 10))){
28766 this.field.dom.value = d.activePage;
28769 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28770 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28773 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))
28775 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28776 this.field.dom.value = pageNum;
28777 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28780 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28782 var v = this.field.dom.value, pageNum;
28783 var increment = (e.shiftKey) ? 10 : 1;
28784 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28787 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28788 this.field.dom.value = d.activePage;
28791 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28793 this.field.dom.value = parseInt(v, 10) + increment;
28794 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28795 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28802 beforeLoad : function(){
28804 this.loading.disable();
28809 onClick : function(which){
28818 ds.load({params:{start: 0, limit: this.pageSize}});
28821 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28824 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28827 var total = ds.getTotalCount();
28828 var extra = total % this.pageSize;
28829 var lastStart = extra ? (total - extra) : total-this.pageSize;
28830 ds.load({params:{start: lastStart, limit: this.pageSize}});
28833 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28839 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28840 * @param {Roo.data.Store} store The data store to unbind
28842 unbind : function(ds){
28843 ds.un("beforeload", this.beforeLoad, this);
28844 ds.un("load", this.onLoad, this);
28845 ds.un("loadexception", this.onLoadError, this);
28846 ds.un("remove", this.updateInfo, this);
28847 ds.un("add", this.updateInfo, this);
28848 this.ds = undefined;
28852 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28853 * @param {Roo.data.Store} store The data store to bind
28855 bind : function(ds){
28856 ds.on("beforeload", this.beforeLoad, this);
28857 ds.on("load", this.onLoad, this);
28858 ds.on("loadexception", this.onLoadError, this);
28859 ds.on("remove", this.updateInfo, this);
28860 ds.on("add", this.updateInfo, this);
28871 * @class Roo.bootstrap.MessageBar
28872 * @extends Roo.bootstrap.Component
28873 * Bootstrap MessageBar class
28874 * @cfg {String} html contents of the MessageBar
28875 * @cfg {String} weight (info | success | warning | danger) default info
28876 * @cfg {String} beforeClass insert the bar before the given class
28877 * @cfg {Boolean} closable (true | false) default false
28878 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28881 * Create a new Element
28882 * @param {Object} config The config object
28885 Roo.bootstrap.MessageBar = function(config){
28886 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28889 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28895 beforeClass: 'bootstrap-sticky-wrap',
28897 getAutoCreate : function(){
28901 cls: 'alert alert-dismissable alert-' + this.weight,
28906 html: this.html || ''
28912 cfg.cls += ' alert-messages-fixed';
28926 onRender : function(ct, position)
28928 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28931 var cfg = Roo.apply({}, this.getAutoCreate());
28935 cfg.cls += ' ' + this.cls;
28938 cfg.style = this.style;
28940 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28942 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28945 this.el.select('>button.close').on('click', this.hide, this);
28951 if (!this.rendered) {
28957 this.fireEvent('show', this);
28963 if (!this.rendered) {
28969 this.fireEvent('hide', this);
28972 update : function()
28974 // var e = this.el.dom.firstChild;
28976 // if(this.closable){
28977 // e = e.nextSibling;
28980 // e.data = this.html || '';
28982 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28998 * @class Roo.bootstrap.Graph
28999 * @extends Roo.bootstrap.Component
29000 * Bootstrap Graph class
29004 @cfg {String} graphtype bar | vbar | pie
29005 @cfg {number} g_x coodinator | centre x (pie)
29006 @cfg {number} g_y coodinator | centre y (pie)
29007 @cfg {number} g_r radius (pie)
29008 @cfg {number} g_height height of the chart (respected by all elements in the set)
29009 @cfg {number} g_width width of the chart (respected by all elements in the set)
29010 @cfg {Object} title The title of the chart
29013 -opts (object) options for the chart
29015 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29016 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29018 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.
29019 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29021 o stretch (boolean)
29023 -opts (object) options for the pie
29026 o startAngle (number)
29027 o endAngle (number)
29031 * Create a new Input
29032 * @param {Object} config The config object
29035 Roo.bootstrap.Graph = function(config){
29036 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29042 * The img click event for the img.
29043 * @param {Roo.EventObject} e
29049 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29060 //g_colors: this.colors,
29067 getAutoCreate : function(){
29078 onRender : function(ct,position){
29081 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29083 if (typeof(Raphael) == 'undefined') {
29084 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29088 this.raphael = Raphael(this.el.dom);
29090 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29091 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29092 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29093 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29095 r.text(160, 10, "Single Series Chart").attr(txtattr);
29096 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29097 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29098 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29100 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29101 r.barchart(330, 10, 300, 220, data1);
29102 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29103 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29106 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29107 // r.barchart(30, 30, 560, 250, xdata, {
29108 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29109 // axis : "0 0 1 1",
29110 // axisxlabels : xdata
29111 // //yvalues : cols,
29114 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29116 // this.load(null,xdata,{
29117 // axis : "0 0 1 1",
29118 // axisxlabels : xdata
29123 load : function(graphtype,xdata,opts)
29125 this.raphael.clear();
29127 graphtype = this.graphtype;
29132 var r = this.raphael,
29133 fin = function () {
29134 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29136 fout = function () {
29137 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29139 pfin = function() {
29140 this.sector.stop();
29141 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29144 this.label[0].stop();
29145 this.label[0].attr({ r: 7.5 });
29146 this.label[1].attr({ "font-weight": 800 });
29149 pfout = function() {
29150 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29153 this.label[0].animate({ r: 5 }, 500, "bounce");
29154 this.label[1].attr({ "font-weight": 400 });
29160 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29163 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29166 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29167 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29169 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29176 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29181 setTitle: function(o)
29186 initEvents: function() {
29189 this.el.on('click', this.onClick, this);
29193 onClick : function(e)
29195 Roo.log('img onclick');
29196 this.fireEvent('click', this, e);
29208 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29211 * @class Roo.bootstrap.dash.NumberBox
29212 * @extends Roo.bootstrap.Component
29213 * Bootstrap NumberBox class
29214 * @cfg {String} headline Box headline
29215 * @cfg {String} content Box content
29216 * @cfg {String} icon Box icon
29217 * @cfg {String} footer Footer text
29218 * @cfg {String} fhref Footer href
29221 * Create a new NumberBox
29222 * @param {Object} config The config object
29226 Roo.bootstrap.dash.NumberBox = function(config){
29227 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29231 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29240 getAutoCreate : function(){
29244 cls : 'small-box ',
29252 cls : 'roo-headline',
29253 html : this.headline
29257 cls : 'roo-content',
29258 html : this.content
29272 cls : 'ion ' + this.icon
29281 cls : 'small-box-footer',
29282 href : this.fhref || '#',
29286 cfg.cn.push(footer);
29293 onRender : function(ct,position){
29294 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29301 setHeadline: function (value)
29303 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29306 setFooter: function (value, href)
29308 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29311 this.el.select('a.small-box-footer',true).first().attr('href', href);
29316 setContent: function (value)
29318 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29321 initEvents: function()
29335 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29338 * @class Roo.bootstrap.dash.TabBox
29339 * @extends Roo.bootstrap.Component
29340 * @children Roo.bootstrap.dash.TabPane
29341 * Bootstrap TabBox class
29342 * @cfg {String} title Title of the TabBox
29343 * @cfg {String} icon Icon of the TabBox
29344 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29345 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29348 * Create a new TabBox
29349 * @param {Object} config The config object
29353 Roo.bootstrap.dash.TabBox = function(config){
29354 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29359 * When a pane is added
29360 * @param {Roo.bootstrap.dash.TabPane} pane
29364 * @event activatepane
29365 * When a pane is activated
29366 * @param {Roo.bootstrap.dash.TabPane} pane
29368 "activatepane" : true
29376 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29381 tabScrollable : false,
29383 getChildContainer : function()
29385 return this.el.select('.tab-content', true).first();
29388 getAutoCreate : function(){
29392 cls: 'pull-left header',
29400 cls: 'fa ' + this.icon
29406 cls: 'nav nav-tabs pull-right',
29412 if(this.tabScrollable){
29419 cls: 'nav nav-tabs pull-right',
29430 cls: 'nav-tabs-custom',
29435 cls: 'tab-content no-padding',
29443 initEvents : function()
29445 //Roo.log('add add pane handler');
29446 this.on('addpane', this.onAddPane, this);
29449 * Updates the box title
29450 * @param {String} html to set the title to.
29452 setTitle : function(value)
29454 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29456 onAddPane : function(pane)
29458 this.panes.push(pane);
29459 //Roo.log('addpane');
29461 // tabs are rendere left to right..
29462 if(!this.showtabs){
29466 var ctr = this.el.select('.nav-tabs', true).first();
29469 var existing = ctr.select('.nav-tab',true);
29470 var qty = existing.getCount();;
29473 var tab = ctr.createChild({
29475 cls : 'nav-tab' + (qty ? '' : ' active'),
29483 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29486 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29488 pane.el.addClass('active');
29493 onTabClick : function(ev,un,ob,pane)
29495 //Roo.log('tab - prev default');
29496 ev.preventDefault();
29499 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29500 pane.tab.addClass('active');
29501 //Roo.log(pane.title);
29502 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29503 // technically we should have a deactivate event.. but maybe add later.
29504 // and it should not de-activate the selected tab...
29505 this.fireEvent('activatepane', pane);
29506 pane.el.addClass('active');
29507 pane.fireEvent('activate');
29512 getActivePane : function()
29515 Roo.each(this.panes, function(p) {
29516 if(p.el.hasClass('active')){
29537 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29539 * @class Roo.bootstrap.TabPane
29540 * @extends Roo.bootstrap.Component
29541 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29542 * Bootstrap TabPane class
29543 * @cfg {Boolean} active (false | true) Default false
29544 * @cfg {String} title title of panel
29548 * Create a new TabPane
29549 * @param {Object} config The config object
29552 Roo.bootstrap.dash.TabPane = function(config){
29553 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29559 * When a pane is activated
29560 * @param {Roo.bootstrap.dash.TabPane} pane
29567 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29572 // the tabBox that this is attached to.
29575 getAutoCreate : function()
29583 cfg.cls += ' active';
29588 initEvents : function()
29590 //Roo.log('trigger add pane handler');
29591 this.parent().fireEvent('addpane', this)
29595 * Updates the tab title
29596 * @param {String} html to set the title to.
29598 setTitle: function(str)
29604 this.tab.select('a', true).first().dom.innerHTML = str;
29623 * @class Roo.bootstrap.Tooltip
29624 * Bootstrap Tooltip class
29625 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29626 * to determine which dom element triggers the tooltip.
29628 * It needs to add support for additional attributes like tooltip-position
29631 * Create a new Toolti
29632 * @param {Object} config The config object
29635 Roo.bootstrap.Tooltip = function(config){
29636 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29638 this.alignment = Roo.bootstrap.Tooltip.alignment;
29640 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29641 this.alignment = config.alignment;
29646 Roo.apply(Roo.bootstrap.Tooltip, {
29648 * @function init initialize tooltip monitoring.
29652 currentTip : false,
29653 currentRegion : false,
29659 Roo.get(document).on('mouseover', this.enter ,this);
29660 Roo.get(document).on('mouseout', this.leave, this);
29663 this.currentTip = new Roo.bootstrap.Tooltip();
29666 enter : function(ev)
29668 var dom = ev.getTarget();
29670 //Roo.log(['enter',dom]);
29671 var el = Roo.fly(dom);
29672 if (this.currentEl) {
29674 //Roo.log(this.currentEl);
29675 //Roo.log(this.currentEl.contains(dom));
29676 if (this.currentEl == el) {
29679 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29685 if (this.currentTip.el) {
29686 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29690 if(!el || el.dom == document){
29696 if (!el.attr('tooltip')) {
29697 pel = el.findParent("[tooltip]");
29699 bindEl = Roo.get(pel);
29705 // you can not look for children, as if el is the body.. then everythign is the child..
29706 if (!pel && !el.attr('tooltip')) { //
29707 if (!el.select("[tooltip]").elements.length) {
29710 // is the mouse over this child...?
29711 bindEl = el.select("[tooltip]").first();
29712 var xy = ev.getXY();
29713 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29714 //Roo.log("not in region.");
29717 //Roo.log("child element over..");
29720 this.currentEl = el;
29721 this.currentTip.bind(bindEl);
29722 this.currentRegion = Roo.lib.Region.getRegion(dom);
29723 this.currentTip.enter();
29726 leave : function(ev)
29728 var dom = ev.getTarget();
29729 //Roo.log(['leave',dom]);
29730 if (!this.currentEl) {
29735 if (dom != this.currentEl.dom) {
29738 var xy = ev.getXY();
29739 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29742 // only activate leave if mouse cursor is outside... bounding box..
29747 if (this.currentTip) {
29748 this.currentTip.leave();
29750 //Roo.log('clear currentEl');
29751 this.currentEl = false;
29756 'left' : ['r-l', [-2,0], 'right'],
29757 'right' : ['l-r', [2,0], 'left'],
29758 'bottom' : ['t-b', [0,2], 'top'],
29759 'top' : [ 'b-t', [0,-2], 'bottom']
29765 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29770 delay : null, // can be { show : 300 , hide: 500}
29774 hoverState : null, //???
29776 placement : 'bottom',
29780 getAutoCreate : function(){
29787 cls : 'tooltip-arrow arrow'
29790 cls : 'tooltip-inner'
29797 bind : function(el)
29802 initEvents : function()
29804 this.arrowEl = this.el.select('.arrow', true).first();
29805 this.innerEl = this.el.select('.tooltip-inner', true).first();
29808 enter : function () {
29810 if (this.timeout != null) {
29811 clearTimeout(this.timeout);
29814 this.hoverState = 'in';
29815 //Roo.log("enter - show");
29816 if (!this.delay || !this.delay.show) {
29821 this.timeout = setTimeout(function () {
29822 if (_t.hoverState == 'in') {
29825 }, this.delay.show);
29829 clearTimeout(this.timeout);
29831 this.hoverState = 'out';
29832 if (!this.delay || !this.delay.hide) {
29838 this.timeout = setTimeout(function () {
29839 //Roo.log("leave - timeout");
29841 if (_t.hoverState == 'out') {
29843 Roo.bootstrap.Tooltip.currentEl = false;
29848 show : function (msg)
29851 this.render(document.body);
29854 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29856 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29858 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29860 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29861 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29863 var placement = typeof this.placement == 'function' ?
29864 this.placement.call(this, this.el, on_el) :
29867 var autoToken = /\s?auto?\s?/i;
29868 var autoPlace = autoToken.test(placement);
29870 placement = placement.replace(autoToken, '') || 'top';
29874 //this.el.setXY([0,0]);
29876 //this.el.dom.style.display='block';
29878 //this.el.appendTo(on_el);
29880 var p = this.getPosition();
29881 var box = this.el.getBox();
29887 var align = this.alignment[placement];
29889 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29891 if(placement == 'top' || placement == 'bottom'){
29893 placement = 'right';
29896 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29897 placement = 'left';
29900 var scroll = Roo.select('body', true).first().getScroll();
29902 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29906 align = this.alignment[placement];
29908 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29912 var elems = document.getElementsByTagName('div');
29913 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29914 for (var i = 0; i < elems.length; i++) {
29915 var zindex = Number.parseInt(
29916 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29919 if (zindex > highest) {
29926 this.el.dom.style.zIndex = highest;
29928 this.el.alignTo(this.bindEl, align[0],align[1]);
29929 //var arrow = this.el.select('.arrow',true).first();
29930 //arrow.set(align[2],
29932 this.el.addClass(placement);
29933 this.el.addClass("bs-tooltip-"+ placement);
29935 this.el.addClass('in fade show');
29937 this.hoverState = null;
29939 if (this.el.hasClass('fade')) {
29954 //this.el.setXY([0,0]);
29955 this.el.removeClass(['show', 'in']);
29971 * @class Roo.bootstrap.LocationPicker
29972 * @extends Roo.bootstrap.Component
29973 * Bootstrap LocationPicker class
29974 * @cfg {Number} latitude Position when init default 0
29975 * @cfg {Number} longitude Position when init default 0
29976 * @cfg {Number} zoom default 15
29977 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29978 * @cfg {Boolean} mapTypeControl default false
29979 * @cfg {Boolean} disableDoubleClickZoom default false
29980 * @cfg {Boolean} scrollwheel default true
29981 * @cfg {Boolean} streetViewControl default false
29982 * @cfg {Number} radius default 0
29983 * @cfg {String} locationName
29984 * @cfg {Boolean} draggable default true
29985 * @cfg {Boolean} enableAutocomplete default false
29986 * @cfg {Boolean} enableReverseGeocode default true
29987 * @cfg {String} markerTitle
29990 * Create a new LocationPicker
29991 * @param {Object} config The config object
29995 Roo.bootstrap.LocationPicker = function(config){
29997 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30002 * Fires when the picker initialized.
30003 * @param {Roo.bootstrap.LocationPicker} this
30004 * @param {Google Location} location
30008 * @event positionchanged
30009 * Fires when the picker position changed.
30010 * @param {Roo.bootstrap.LocationPicker} this
30011 * @param {Google Location} location
30013 positionchanged : true,
30016 * Fires when the map resize.
30017 * @param {Roo.bootstrap.LocationPicker} this
30022 * Fires when the map show.
30023 * @param {Roo.bootstrap.LocationPicker} this
30028 * Fires when the map hide.
30029 * @param {Roo.bootstrap.LocationPicker} this
30034 * Fires when click the map.
30035 * @param {Roo.bootstrap.LocationPicker} this
30036 * @param {Map event} e
30040 * @event mapRightClick
30041 * Fires when right click the map.
30042 * @param {Roo.bootstrap.LocationPicker} this
30043 * @param {Map event} e
30045 mapRightClick : true,
30047 * @event markerClick
30048 * Fires when click the marker.
30049 * @param {Roo.bootstrap.LocationPicker} this
30050 * @param {Map event} e
30052 markerClick : true,
30054 * @event markerRightClick
30055 * Fires when right click the marker.
30056 * @param {Roo.bootstrap.LocationPicker} this
30057 * @param {Map event} e
30059 markerRightClick : true,
30061 * @event OverlayViewDraw
30062 * Fires when OverlayView Draw
30063 * @param {Roo.bootstrap.LocationPicker} this
30065 OverlayViewDraw : true,
30067 * @event OverlayViewOnAdd
30068 * Fires when OverlayView Draw
30069 * @param {Roo.bootstrap.LocationPicker} this
30071 OverlayViewOnAdd : true,
30073 * @event OverlayViewOnRemove
30074 * Fires when OverlayView Draw
30075 * @param {Roo.bootstrap.LocationPicker} this
30077 OverlayViewOnRemove : true,
30079 * @event OverlayViewShow
30080 * Fires when OverlayView Draw
30081 * @param {Roo.bootstrap.LocationPicker} this
30082 * @param {Pixel} cpx
30084 OverlayViewShow : true,
30086 * @event OverlayViewHide
30087 * Fires when OverlayView Draw
30088 * @param {Roo.bootstrap.LocationPicker} this
30090 OverlayViewHide : true,
30092 * @event loadexception
30093 * Fires when load google lib failed.
30094 * @param {Roo.bootstrap.LocationPicker} this
30096 loadexception : true
30101 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30103 gMapContext: false,
30109 mapTypeControl: false,
30110 disableDoubleClickZoom: false,
30112 streetViewControl: false,
30116 enableAutocomplete: false,
30117 enableReverseGeocode: true,
30120 getAutoCreate: function()
30125 cls: 'roo-location-picker'
30131 initEvents: function(ct, position)
30133 if(!this.el.getWidth() || this.isApplied()){
30137 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30142 initial: function()
30144 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30145 this.fireEvent('loadexception', this);
30149 if(!this.mapTypeId){
30150 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30153 this.gMapContext = this.GMapContext();
30155 this.initOverlayView();
30157 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30161 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30162 _this.setPosition(_this.gMapContext.marker.position);
30165 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30166 _this.fireEvent('mapClick', this, event);
30170 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30171 _this.fireEvent('mapRightClick', this, event);
30175 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30176 _this.fireEvent('markerClick', this, event);
30180 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30181 _this.fireEvent('markerRightClick', this, event);
30185 this.setPosition(this.gMapContext.location);
30187 this.fireEvent('initial', this, this.gMapContext.location);
30190 initOverlayView: function()
30194 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30198 _this.fireEvent('OverlayViewDraw', _this);
30203 _this.fireEvent('OverlayViewOnAdd', _this);
30206 onRemove: function()
30208 _this.fireEvent('OverlayViewOnRemove', _this);
30211 show: function(cpx)
30213 _this.fireEvent('OverlayViewShow', _this, cpx);
30218 _this.fireEvent('OverlayViewHide', _this);
30224 fromLatLngToContainerPixel: function(event)
30226 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30229 isApplied: function()
30231 return this.getGmapContext() == false ? false : true;
30234 getGmapContext: function()
30236 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30239 GMapContext: function()
30241 var position = new google.maps.LatLng(this.latitude, this.longitude);
30243 var _map = new google.maps.Map(this.el.dom, {
30246 mapTypeId: this.mapTypeId,
30247 mapTypeControl: this.mapTypeControl,
30248 disableDoubleClickZoom: this.disableDoubleClickZoom,
30249 scrollwheel: this.scrollwheel,
30250 streetViewControl: this.streetViewControl,
30251 locationName: this.locationName,
30252 draggable: this.draggable,
30253 enableAutocomplete: this.enableAutocomplete,
30254 enableReverseGeocode: this.enableReverseGeocode
30257 var _marker = new google.maps.Marker({
30258 position: position,
30260 title: this.markerTitle,
30261 draggable: this.draggable
30268 location: position,
30269 radius: this.radius,
30270 locationName: this.locationName,
30271 addressComponents: {
30272 formatted_address: null,
30273 addressLine1: null,
30274 addressLine2: null,
30276 streetNumber: null,
30280 stateOrProvince: null
30283 domContainer: this.el.dom,
30284 geodecoder: new google.maps.Geocoder()
30288 drawCircle: function(center, radius, options)
30290 if (this.gMapContext.circle != null) {
30291 this.gMapContext.circle.setMap(null);
30295 options = Roo.apply({}, options, {
30296 strokeColor: "#0000FF",
30297 strokeOpacity: .35,
30299 fillColor: "#0000FF",
30303 options.map = this.gMapContext.map;
30304 options.radius = radius;
30305 options.center = center;
30306 this.gMapContext.circle = new google.maps.Circle(options);
30307 return this.gMapContext.circle;
30313 setPosition: function(location)
30315 this.gMapContext.location = location;
30316 this.gMapContext.marker.setPosition(location);
30317 this.gMapContext.map.panTo(location);
30318 this.drawCircle(location, this.gMapContext.radius, {});
30322 if (this.gMapContext.settings.enableReverseGeocode) {
30323 this.gMapContext.geodecoder.geocode({
30324 latLng: this.gMapContext.location
30325 }, function(results, status) {
30327 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30328 _this.gMapContext.locationName = results[0].formatted_address;
30329 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30331 _this.fireEvent('positionchanged', this, location);
30338 this.fireEvent('positionchanged', this, location);
30343 google.maps.event.trigger(this.gMapContext.map, "resize");
30345 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30347 this.fireEvent('resize', this);
30350 setPositionByLatLng: function(latitude, longitude)
30352 this.setPosition(new google.maps.LatLng(latitude, longitude));
30355 getCurrentPosition: function()
30358 latitude: this.gMapContext.location.lat(),
30359 longitude: this.gMapContext.location.lng()
30363 getAddressName: function()
30365 return this.gMapContext.locationName;
30368 getAddressComponents: function()
30370 return this.gMapContext.addressComponents;
30373 address_component_from_google_geocode: function(address_components)
30377 for (var i = 0; i < address_components.length; i++) {
30378 var component = address_components[i];
30379 if (component.types.indexOf("postal_code") >= 0) {
30380 result.postalCode = component.short_name;
30381 } else if (component.types.indexOf("street_number") >= 0) {
30382 result.streetNumber = component.short_name;
30383 } else if (component.types.indexOf("route") >= 0) {
30384 result.streetName = component.short_name;
30385 } else if (component.types.indexOf("neighborhood") >= 0) {
30386 result.city = component.short_name;
30387 } else if (component.types.indexOf("locality") >= 0) {
30388 result.city = component.short_name;
30389 } else if (component.types.indexOf("sublocality") >= 0) {
30390 result.district = component.short_name;
30391 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30392 result.stateOrProvince = component.short_name;
30393 } else if (component.types.indexOf("country") >= 0) {
30394 result.country = component.short_name;
30398 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30399 result.addressLine2 = "";
30403 setZoomLevel: function(zoom)
30405 this.gMapContext.map.setZoom(zoom);
30418 this.fireEvent('show', this);
30429 this.fireEvent('hide', this);
30434 Roo.apply(Roo.bootstrap.LocationPicker, {
30436 OverlayView : function(map, options)
30438 options = options || {};
30445 * @class Roo.bootstrap.Alert
30446 * @extends Roo.bootstrap.Component
30447 * Bootstrap Alert class - shows an alert area box
30449 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30450 Enter a valid email address
30453 * @cfg {String} title The title of alert
30454 * @cfg {String} html The content of alert
30455 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30456 * @cfg {String} fa font-awesomeicon
30457 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30458 * @cfg {Boolean} close true to show a x closer
30462 * Create a new alert
30463 * @param {Object} config The config object
30467 Roo.bootstrap.Alert = function(config){
30468 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30472 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30478 faicon: false, // BC
30482 getAutoCreate : function()
30494 style : this.close ? '' : 'display:none'
30498 cls : 'roo-alert-icon'
30503 cls : 'roo-alert-title',
30508 cls : 'roo-alert-text',
30515 cfg.cn[0].cls += ' fa ' + this.faicon;
30518 cfg.cn[0].cls += ' fa ' + this.fa;
30522 cfg.cls += ' alert-' + this.weight;
30528 initEvents: function()
30530 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30531 this.titleEl = this.el.select('.roo-alert-title',true).first();
30532 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30533 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30534 if (this.seconds > 0) {
30535 this.hide.defer(this.seconds, this);
30539 * Set the Title Message HTML
30540 * @param {String} html
30542 setTitle : function(str)
30544 this.titleEl.dom.innerHTML = str;
30548 * Set the Body Message HTML
30549 * @param {String} html
30551 setHtml : function(str)
30553 this.htmlEl.dom.innerHTML = str;
30556 * Set the Weight of the alert
30557 * @param {String} (success|info|warning|danger) weight
30560 setWeight : function(weight)
30563 this.el.removeClass('alert-' + this.weight);
30566 this.weight = weight;
30568 this.el.addClass('alert-' + this.weight);
30571 * Set the Icon of the alert
30572 * @param {String} see fontawsome names (name without the 'fa-' bit)
30574 setIcon : function(icon)
30577 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30580 this.faicon = icon;
30582 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30607 * @class Roo.bootstrap.UploadCropbox
30608 * @extends Roo.bootstrap.Component
30609 * Bootstrap UploadCropbox class
30610 * @cfg {String} emptyText show when image has been loaded
30611 * @cfg {String} rotateNotify show when image too small to rotate
30612 * @cfg {Number} errorTimeout default 3000
30613 * @cfg {Number} minWidth default 300
30614 * @cfg {Number} minHeight default 300
30615 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30616 * @cfg {Boolean} isDocument (true|false) default false
30617 * @cfg {String} url action url
30618 * @cfg {String} paramName default 'imageUpload'
30619 * @cfg {String} method default POST
30620 * @cfg {Boolean} loadMask (true|false) default true
30621 * @cfg {Boolean} loadingText default 'Loading...'
30624 * Create a new UploadCropbox
30625 * @param {Object} config The config object
30628 Roo.bootstrap.UploadCropbox = function(config){
30629 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30633 * @event beforeselectfile
30634 * Fire before select file
30635 * @param {Roo.bootstrap.UploadCropbox} this
30637 "beforeselectfile" : true,
30640 * Fire after initEvent
30641 * @param {Roo.bootstrap.UploadCropbox} this
30646 * Fire after initEvent
30647 * @param {Roo.bootstrap.UploadCropbox} this
30648 * @param {String} data
30653 * Fire when preparing the file data
30654 * @param {Roo.bootstrap.UploadCropbox} this
30655 * @param {Object} file
30660 * Fire when get exception
30661 * @param {Roo.bootstrap.UploadCropbox} this
30662 * @param {XMLHttpRequest} xhr
30664 "exception" : true,
30666 * @event beforeloadcanvas
30667 * Fire before load the canvas
30668 * @param {Roo.bootstrap.UploadCropbox} this
30669 * @param {String} src
30671 "beforeloadcanvas" : true,
30674 * Fire when trash image
30675 * @param {Roo.bootstrap.UploadCropbox} this
30680 * Fire when download the image
30681 * @param {Roo.bootstrap.UploadCropbox} this
30685 * @event footerbuttonclick
30686 * Fire when footerbuttonclick
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {String} type
30690 "footerbuttonclick" : true,
30694 * @param {Roo.bootstrap.UploadCropbox} this
30699 * Fire when rotate the image
30700 * @param {Roo.bootstrap.UploadCropbox} this
30701 * @param {String} pos
30706 * Fire when inspect the file
30707 * @param {Roo.bootstrap.UploadCropbox} this
30708 * @param {Object} file
30713 * Fire when xhr upload the file
30714 * @param {Roo.bootstrap.UploadCropbox} this
30715 * @param {Object} data
30720 * Fire when arrange the file data
30721 * @param {Roo.bootstrap.UploadCropbox} this
30722 * @param {Object} formData
30727 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30730 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30732 emptyText : 'Click to upload image',
30733 rotateNotify : 'Image is too small to rotate',
30734 errorTimeout : 3000,
30748 cropType : 'image/jpeg',
30750 canvasLoaded : false,
30751 isDocument : false,
30753 paramName : 'imageUpload',
30755 loadingText : 'Loading...',
30758 getAutoCreate : function()
30762 cls : 'roo-upload-cropbox',
30766 cls : 'roo-upload-cropbox-selector',
30771 cls : 'roo-upload-cropbox-body',
30772 style : 'cursor:pointer',
30776 cls : 'roo-upload-cropbox-preview'
30780 cls : 'roo-upload-cropbox-thumb'
30784 cls : 'roo-upload-cropbox-empty-notify',
30785 html : this.emptyText
30789 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30790 html : this.rotateNotify
30796 cls : 'roo-upload-cropbox-footer',
30799 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30809 onRender : function(ct, position)
30811 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30813 if (this.buttons.length) {
30815 Roo.each(this.buttons, function(bb) {
30817 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30819 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30825 this.maskEl = this.el;
30829 initEvents : function()
30831 this.urlAPI = (window.createObjectURL && window) ||
30832 (window.URL && URL.revokeObjectURL && URL) ||
30833 (window.webkitURL && webkitURL);
30835 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30836 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30838 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30839 this.selectorEl.hide();
30841 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30842 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30844 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30845 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30846 this.thumbEl.hide();
30848 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30849 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30851 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30852 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30853 this.errorEl.hide();
30855 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30856 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30857 this.footerEl.hide();
30859 this.setThumbBoxSize();
30865 this.fireEvent('initial', this);
30872 window.addEventListener("resize", function() { _this.resize(); } );
30874 this.bodyEl.on('click', this.beforeSelectFile, this);
30877 this.bodyEl.on('touchstart', this.onTouchStart, this);
30878 this.bodyEl.on('touchmove', this.onTouchMove, this);
30879 this.bodyEl.on('touchend', this.onTouchEnd, this);
30883 this.bodyEl.on('mousedown', this.onMouseDown, this);
30884 this.bodyEl.on('mousemove', this.onMouseMove, this);
30885 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30886 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30887 Roo.get(document).on('mouseup', this.onMouseUp, this);
30890 this.selectorEl.on('change', this.onFileSelected, this);
30896 this.baseScale = 1;
30898 this.baseRotate = 1;
30899 this.dragable = false;
30900 this.pinching = false;
30903 this.cropData = false;
30904 this.notifyEl.dom.innerHTML = this.emptyText;
30906 this.selectorEl.dom.value = '';
30910 resize : function()
30912 if(this.fireEvent('resize', this) != false){
30913 this.setThumbBoxPosition();
30914 this.setCanvasPosition();
30918 onFooterButtonClick : function(e, el, o, type)
30921 case 'rotate-left' :
30922 this.onRotateLeft(e);
30924 case 'rotate-right' :
30925 this.onRotateRight(e);
30928 this.beforeSelectFile(e);
30943 this.fireEvent('footerbuttonclick', this, type);
30946 beforeSelectFile : function(e)
30948 e.preventDefault();
30950 if(this.fireEvent('beforeselectfile', this) != false){
30951 this.selectorEl.dom.click();
30955 onFileSelected : function(e)
30957 e.preventDefault();
30959 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30963 var file = this.selectorEl.dom.files[0];
30965 if(this.fireEvent('inspect', this, file) != false){
30966 this.prepare(file);
30971 trash : function(e)
30973 this.fireEvent('trash', this);
30976 download : function(e)
30978 this.fireEvent('download', this);
30981 loadCanvas : function(src)
30983 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30987 this.imageEl = document.createElement('img');
30991 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30993 this.imageEl.src = src;
30997 onLoadCanvas : function()
30999 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31000 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31002 this.bodyEl.un('click', this.beforeSelectFile, this);
31004 this.notifyEl.hide();
31005 this.thumbEl.show();
31006 this.footerEl.show();
31008 this.baseRotateLevel();
31010 if(this.isDocument){
31011 this.setThumbBoxSize();
31014 this.setThumbBoxPosition();
31016 this.baseScaleLevel();
31022 this.canvasLoaded = true;
31025 this.maskEl.unmask();
31030 setCanvasPosition : function()
31032 if(!this.canvasEl){
31036 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31037 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31039 this.previewEl.setLeft(pw);
31040 this.previewEl.setTop(ph);
31044 onMouseDown : function(e)
31048 this.dragable = true;
31049 this.pinching = false;
31051 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31052 this.dragable = false;
31056 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31057 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31061 onMouseMove : function(e)
31065 if(!this.canvasLoaded){
31069 if (!this.dragable){
31073 var minX = Math.ceil(this.thumbEl.getLeft(true));
31074 var minY = Math.ceil(this.thumbEl.getTop(true));
31076 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31077 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31079 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31080 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31082 x = x - this.mouseX;
31083 y = y - this.mouseY;
31085 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31086 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31088 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31089 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31091 this.previewEl.setLeft(bgX);
31092 this.previewEl.setTop(bgY);
31094 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31095 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098 onMouseUp : function(e)
31102 this.dragable = false;
31105 onMouseWheel : function(e)
31109 this.startScale = this.scale;
31111 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31113 if(!this.zoomable()){
31114 this.scale = this.startScale;
31123 zoomable : function()
31125 var minScale = this.thumbEl.getWidth() / this.minWidth;
31127 if(this.minWidth < this.minHeight){
31128 minScale = this.thumbEl.getHeight() / this.minHeight;
31131 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31132 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31136 (this.rotate == 0 || this.rotate == 180) &&
31138 width > this.imageEl.OriginWidth ||
31139 height > this.imageEl.OriginHeight ||
31140 (width < this.minWidth && height < this.minHeight)
31148 (this.rotate == 90 || this.rotate == 270) &&
31150 width > this.imageEl.OriginWidth ||
31151 height > this.imageEl.OriginHeight ||
31152 (width < this.minHeight && height < this.minWidth)
31159 !this.isDocument &&
31160 (this.rotate == 0 || this.rotate == 180) &&
31162 width < this.minWidth ||
31163 width > this.imageEl.OriginWidth ||
31164 height < this.minHeight ||
31165 height > this.imageEl.OriginHeight
31172 !this.isDocument &&
31173 (this.rotate == 90 || this.rotate == 270) &&
31175 width < this.minHeight ||
31176 width > this.imageEl.OriginWidth ||
31177 height < this.minWidth ||
31178 height > this.imageEl.OriginHeight
31188 onRotateLeft : function(e)
31190 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31192 var minScale = this.thumbEl.getWidth() / this.minWidth;
31194 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31195 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31197 this.startScale = this.scale;
31199 while (this.getScaleLevel() < minScale){
31201 this.scale = this.scale + 1;
31203 if(!this.zoomable()){
31208 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31209 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31214 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31221 this.scale = this.startScale;
31223 this.onRotateFail();
31228 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31230 if(this.isDocument){
31231 this.setThumbBoxSize();
31232 this.setThumbBoxPosition();
31233 this.setCanvasPosition();
31238 this.fireEvent('rotate', this, 'left');
31242 onRotateRight : function(e)
31244 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31246 var minScale = this.thumbEl.getWidth() / this.minWidth;
31248 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31249 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31251 this.startScale = this.scale;
31253 while (this.getScaleLevel() < minScale){
31255 this.scale = this.scale + 1;
31257 if(!this.zoomable()){
31262 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31263 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31268 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31275 this.scale = this.startScale;
31277 this.onRotateFail();
31282 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31284 if(this.isDocument){
31285 this.setThumbBoxSize();
31286 this.setThumbBoxPosition();
31287 this.setCanvasPosition();
31292 this.fireEvent('rotate', this, 'right');
31295 onRotateFail : function()
31297 this.errorEl.show(true);
31301 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31306 this.previewEl.dom.innerHTML = '';
31308 var canvasEl = document.createElement("canvas");
31310 var contextEl = canvasEl.getContext("2d");
31312 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31313 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31314 var center = this.imageEl.OriginWidth / 2;
31316 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31317 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31318 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31319 center = this.imageEl.OriginHeight / 2;
31322 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31324 contextEl.translate(center, center);
31325 contextEl.rotate(this.rotate * Math.PI / 180);
31327 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31329 this.canvasEl = document.createElement("canvas");
31331 this.contextEl = this.canvasEl.getContext("2d");
31333 switch (this.rotate) {
31336 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31337 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31339 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31344 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31345 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31347 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31348 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);
31352 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31357 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31358 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31360 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31361 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);
31365 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);
31370 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31371 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31373 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31374 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31378 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);
31385 this.previewEl.appendChild(this.canvasEl);
31387 this.setCanvasPosition();
31392 if(!this.canvasLoaded){
31396 var imageCanvas = document.createElement("canvas");
31398 var imageContext = imageCanvas.getContext("2d");
31400 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31401 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31403 var center = imageCanvas.width / 2;
31405 imageContext.translate(center, center);
31407 imageContext.rotate(this.rotate * Math.PI / 180);
31409 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31411 var canvas = document.createElement("canvas");
31413 var context = canvas.getContext("2d");
31415 canvas.width = this.minWidth;
31416 canvas.height = this.minHeight;
31418 switch (this.rotate) {
31421 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31422 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31424 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31425 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31427 var targetWidth = this.minWidth - 2 * x;
31428 var targetHeight = this.minHeight - 2 * y;
31432 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31433 scale = targetWidth / width;
31436 if(x > 0 && y == 0){
31437 scale = targetHeight / height;
31440 if(x > 0 && y > 0){
31441 scale = targetWidth / width;
31443 if(width < height){
31444 scale = targetHeight / height;
31448 context.scale(scale, scale);
31450 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31451 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31453 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31454 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31456 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31461 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31462 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31464 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31465 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31467 var targetWidth = this.minWidth - 2 * x;
31468 var targetHeight = this.minHeight - 2 * y;
31472 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31473 scale = targetWidth / width;
31476 if(x > 0 && y == 0){
31477 scale = targetHeight / height;
31480 if(x > 0 && y > 0){
31481 scale = targetWidth / width;
31483 if(width < height){
31484 scale = targetHeight / height;
31488 context.scale(scale, scale);
31490 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31491 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31493 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31494 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31496 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31498 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31503 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31504 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31506 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31509 var targetWidth = this.minWidth - 2 * x;
31510 var targetHeight = this.minHeight - 2 * y;
31514 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515 scale = targetWidth / width;
31518 if(x > 0 && y == 0){
31519 scale = targetHeight / height;
31522 if(x > 0 && y > 0){
31523 scale = targetWidth / width;
31525 if(width < height){
31526 scale = targetHeight / height;
31530 context.scale(scale, scale);
31532 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31535 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31538 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31539 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31541 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31546 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31547 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31549 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31550 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31552 var targetWidth = this.minWidth - 2 * x;
31553 var targetHeight = this.minHeight - 2 * y;
31557 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31558 scale = targetWidth / width;
31561 if(x > 0 && y == 0){
31562 scale = targetHeight / height;
31565 if(x > 0 && y > 0){
31566 scale = targetWidth / width;
31568 if(width < height){
31569 scale = targetHeight / height;
31573 context.scale(scale, scale);
31575 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31576 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31578 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31579 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31581 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31583 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31590 this.cropData = canvas.toDataURL(this.cropType);
31592 if(this.fireEvent('crop', this, this.cropData) !== false){
31593 this.process(this.file, this.cropData);
31600 setThumbBoxSize : function()
31604 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31605 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31606 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31608 this.minWidth = width;
31609 this.minHeight = height;
31611 if(this.rotate == 90 || this.rotate == 270){
31612 this.minWidth = height;
31613 this.minHeight = width;
31618 width = Math.ceil(this.minWidth * height / this.minHeight);
31620 if(this.minWidth > this.minHeight){
31622 height = Math.ceil(this.minHeight * width / this.minWidth);
31625 this.thumbEl.setStyle({
31626 width : width + 'px',
31627 height : height + 'px'
31634 setThumbBoxPosition : function()
31636 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31637 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31639 this.thumbEl.setLeft(x);
31640 this.thumbEl.setTop(y);
31644 baseRotateLevel : function()
31646 this.baseRotate = 1;
31649 typeof(this.exif) != 'undefined' &&
31650 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31651 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31653 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31656 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31660 baseScaleLevel : function()
31664 if(this.isDocument){
31666 if(this.baseRotate == 6 || this.baseRotate == 8){
31668 height = this.thumbEl.getHeight();
31669 this.baseScale = height / this.imageEl.OriginWidth;
31671 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31672 width = this.thumbEl.getWidth();
31673 this.baseScale = width / this.imageEl.OriginHeight;
31679 height = this.thumbEl.getHeight();
31680 this.baseScale = height / this.imageEl.OriginHeight;
31682 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31683 width = this.thumbEl.getWidth();
31684 this.baseScale = width / this.imageEl.OriginWidth;
31690 if(this.baseRotate == 6 || this.baseRotate == 8){
31692 width = this.thumbEl.getHeight();
31693 this.baseScale = width / this.imageEl.OriginHeight;
31695 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31696 height = this.thumbEl.getWidth();
31697 this.baseScale = height / this.imageEl.OriginHeight;
31700 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31701 height = this.thumbEl.getWidth();
31702 this.baseScale = height / this.imageEl.OriginHeight;
31704 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31705 width = this.thumbEl.getHeight();
31706 this.baseScale = width / this.imageEl.OriginWidth;
31713 width = this.thumbEl.getWidth();
31714 this.baseScale = width / this.imageEl.OriginWidth;
31716 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31717 height = this.thumbEl.getHeight();
31718 this.baseScale = height / this.imageEl.OriginHeight;
31721 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31723 height = this.thumbEl.getHeight();
31724 this.baseScale = height / this.imageEl.OriginHeight;
31726 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31727 width = this.thumbEl.getWidth();
31728 this.baseScale = width / this.imageEl.OriginWidth;
31736 getScaleLevel : function()
31738 return this.baseScale * Math.pow(1.1, this.scale);
31741 onTouchStart : function(e)
31743 if(!this.canvasLoaded){
31744 this.beforeSelectFile(e);
31748 var touches = e.browserEvent.touches;
31754 if(touches.length == 1){
31755 this.onMouseDown(e);
31759 if(touches.length != 2){
31765 for(var i = 0, finger; finger = touches[i]; i++){
31766 coords.push(finger.pageX, finger.pageY);
31769 var x = Math.pow(coords[0] - coords[2], 2);
31770 var y = Math.pow(coords[1] - coords[3], 2);
31772 this.startDistance = Math.sqrt(x + y);
31774 this.startScale = this.scale;
31776 this.pinching = true;
31777 this.dragable = false;
31781 onTouchMove : function(e)
31783 if(!this.pinching && !this.dragable){
31787 var touches = e.browserEvent.touches;
31794 this.onMouseMove(e);
31800 for(var i = 0, finger; finger = touches[i]; i++){
31801 coords.push(finger.pageX, finger.pageY);
31804 var x = Math.pow(coords[0] - coords[2], 2);
31805 var y = Math.pow(coords[1] - coords[3], 2);
31807 this.endDistance = Math.sqrt(x + y);
31809 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31811 if(!this.zoomable()){
31812 this.scale = this.startScale;
31820 onTouchEnd : function(e)
31822 this.pinching = false;
31823 this.dragable = false;
31827 process : function(file, crop)
31830 this.maskEl.mask(this.loadingText);
31833 this.xhr = new XMLHttpRequest();
31835 file.xhr = this.xhr;
31837 this.xhr.open(this.method, this.url, true);
31840 "Accept": "application/json",
31841 "Cache-Control": "no-cache",
31842 "X-Requested-With": "XMLHttpRequest"
31845 for (var headerName in headers) {
31846 var headerValue = headers[headerName];
31848 this.xhr.setRequestHeader(headerName, headerValue);
31854 this.xhr.onload = function()
31856 _this.xhrOnLoad(_this.xhr);
31859 this.xhr.onerror = function()
31861 _this.xhrOnError(_this.xhr);
31864 var formData = new FormData();
31866 formData.append('returnHTML', 'NO');
31869 formData.append('crop', crop);
31872 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31873 formData.append(this.paramName, file, file.name);
31876 if(typeof(file.filename) != 'undefined'){
31877 formData.append('filename', file.filename);
31880 if(typeof(file.mimetype) != 'undefined'){
31881 formData.append('mimetype', file.mimetype);
31884 if(this.fireEvent('arrange', this, formData) != false){
31885 this.xhr.send(formData);
31889 xhrOnLoad : function(xhr)
31892 this.maskEl.unmask();
31895 if (xhr.readyState !== 4) {
31896 this.fireEvent('exception', this, xhr);
31900 var response = Roo.decode(xhr.responseText);
31902 if(!response.success){
31903 this.fireEvent('exception', this, xhr);
31907 var response = Roo.decode(xhr.responseText);
31909 this.fireEvent('upload', this, response);
31913 xhrOnError : function()
31916 this.maskEl.unmask();
31919 Roo.log('xhr on error');
31921 var response = Roo.decode(xhr.responseText);
31927 prepare : function(file)
31930 this.maskEl.mask(this.loadingText);
31936 if(typeof(file) === 'string'){
31937 this.loadCanvas(file);
31941 if(!file || !this.urlAPI){
31946 this.cropType = file.type;
31950 if(this.fireEvent('prepare', this, this.file) != false){
31952 var reader = new FileReader();
31954 reader.onload = function (e) {
31955 if (e.target.error) {
31956 Roo.log(e.target.error);
31960 var buffer = e.target.result,
31961 dataView = new DataView(buffer),
31963 maxOffset = dataView.byteLength - 4,
31967 if (dataView.getUint16(0) === 0xffd8) {
31968 while (offset < maxOffset) {
31969 markerBytes = dataView.getUint16(offset);
31971 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31972 markerLength = dataView.getUint16(offset + 2) + 2;
31973 if (offset + markerLength > dataView.byteLength) {
31974 Roo.log('Invalid meta data: Invalid segment size.');
31978 if(markerBytes == 0xffe1){
31979 _this.parseExifData(
31986 offset += markerLength;
31996 var url = _this.urlAPI.createObjectURL(_this.file);
31998 _this.loadCanvas(url);
32003 reader.readAsArrayBuffer(this.file);
32009 parseExifData : function(dataView, offset, length)
32011 var tiffOffset = offset + 10,
32015 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32016 // No Exif data, might be XMP data instead
32020 // Check for the ASCII code for "Exif" (0x45786966):
32021 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32022 // No Exif data, might be XMP data instead
32025 if (tiffOffset + 8 > dataView.byteLength) {
32026 Roo.log('Invalid Exif data: Invalid segment size.');
32029 // Check for the two null bytes:
32030 if (dataView.getUint16(offset + 8) !== 0x0000) {
32031 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32034 // Check the byte alignment:
32035 switch (dataView.getUint16(tiffOffset)) {
32037 littleEndian = true;
32040 littleEndian = false;
32043 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32046 // Check for the TIFF tag marker (0x002A):
32047 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32048 Roo.log('Invalid Exif data: Missing TIFF marker.');
32051 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32052 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32054 this.parseExifTags(
32057 tiffOffset + dirOffset,
32062 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32067 if (dirOffset + 6 > dataView.byteLength) {
32068 Roo.log('Invalid Exif data: Invalid directory offset.');
32071 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32072 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32073 if (dirEndOffset + 4 > dataView.byteLength) {
32074 Roo.log('Invalid Exif data: Invalid directory size.');
32077 for (i = 0; i < tagsNumber; i += 1) {
32081 dirOffset + 2 + 12 * i, // tag offset
32085 // Return the offset to the next directory:
32086 return dataView.getUint32(dirEndOffset, littleEndian);
32089 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32091 var tag = dataView.getUint16(offset, littleEndian);
32093 this.exif[tag] = this.getExifValue(
32097 dataView.getUint16(offset + 2, littleEndian), // tag type
32098 dataView.getUint32(offset + 4, littleEndian), // tag length
32103 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32105 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32114 Roo.log('Invalid Exif data: Invalid tag type.');
32118 tagSize = tagType.size * length;
32119 // Determine if the value is contained in the dataOffset bytes,
32120 // or if the value at the dataOffset is a pointer to the actual data:
32121 dataOffset = tagSize > 4 ?
32122 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32123 if (dataOffset + tagSize > dataView.byteLength) {
32124 Roo.log('Invalid Exif data: Invalid data offset.');
32127 if (length === 1) {
32128 return tagType.getValue(dataView, dataOffset, littleEndian);
32131 for (i = 0; i < length; i += 1) {
32132 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32135 if (tagType.ascii) {
32137 // Concatenate the chars:
32138 for (i = 0; i < values.length; i += 1) {
32140 // Ignore the terminating NULL byte(s):
32141 if (c === '\u0000') {
32153 Roo.apply(Roo.bootstrap.UploadCropbox, {
32155 'Orientation': 0x0112
32159 1: 0, //'top-left',
32161 3: 180, //'bottom-right',
32162 // 4: 'bottom-left',
32164 6: 90, //'right-top',
32165 // 7: 'right-bottom',
32166 8: 270 //'left-bottom'
32170 // byte, 8-bit unsigned int:
32172 getValue: function (dataView, dataOffset) {
32173 return dataView.getUint8(dataOffset);
32177 // ascii, 8-bit byte:
32179 getValue: function (dataView, dataOffset) {
32180 return String.fromCharCode(dataView.getUint8(dataOffset));
32185 // short, 16 bit int:
32187 getValue: function (dataView, dataOffset, littleEndian) {
32188 return dataView.getUint16(dataOffset, littleEndian);
32192 // long, 32 bit int:
32194 getValue: function (dataView, dataOffset, littleEndian) {
32195 return dataView.getUint32(dataOffset, littleEndian);
32199 // rational = two long values, first is numerator, second is denominator:
32201 getValue: function (dataView, dataOffset, littleEndian) {
32202 return dataView.getUint32(dataOffset, littleEndian) /
32203 dataView.getUint32(dataOffset + 4, littleEndian);
32207 // slong, 32 bit signed int:
32209 getValue: function (dataView, dataOffset, littleEndian) {
32210 return dataView.getInt32(dataOffset, littleEndian);
32214 // srational, two slongs, first is numerator, second is denominator:
32216 getValue: function (dataView, dataOffset, littleEndian) {
32217 return dataView.getInt32(dataOffset, littleEndian) /
32218 dataView.getInt32(dataOffset + 4, littleEndian);
32228 cls : 'btn-group roo-upload-cropbox-rotate-left',
32229 action : 'rotate-left',
32233 cls : 'btn btn-default',
32234 html : '<i class="fa fa-undo"></i>'
32240 cls : 'btn-group roo-upload-cropbox-picture',
32241 action : 'picture',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-picture-o"></i>'
32252 cls : 'btn-group roo-upload-cropbox-rotate-right',
32253 action : 'rotate-right',
32257 cls : 'btn btn-default',
32258 html : '<i class="fa fa-repeat"></i>'
32266 cls : 'btn-group roo-upload-cropbox-rotate-left',
32267 action : 'rotate-left',
32271 cls : 'btn btn-default',
32272 html : '<i class="fa fa-undo"></i>'
32278 cls : 'btn-group roo-upload-cropbox-download',
32279 action : 'download',
32283 cls : 'btn btn-default',
32284 html : '<i class="fa fa-download"></i>'
32290 cls : 'btn-group roo-upload-cropbox-crop',
32295 cls : 'btn btn-default',
32296 html : '<i class="fa fa-crop"></i>'
32302 cls : 'btn-group roo-upload-cropbox-trash',
32307 cls : 'btn btn-default',
32308 html : '<i class="fa fa-trash"></i>'
32314 cls : 'btn-group roo-upload-cropbox-rotate-right',
32315 action : 'rotate-right',
32319 cls : 'btn btn-default',
32320 html : '<i class="fa fa-repeat"></i>'
32328 cls : 'btn-group roo-upload-cropbox-rotate-left',
32329 action : 'rotate-left',
32333 cls : 'btn btn-default',
32334 html : '<i class="fa fa-undo"></i>'
32340 cls : 'btn-group roo-upload-cropbox-rotate-right',
32341 action : 'rotate-right',
32345 cls : 'btn btn-default',
32346 html : '<i class="fa fa-repeat"></i>'
32359 * @class Roo.bootstrap.DocumentManager
32360 * @extends Roo.bootstrap.Component
32361 * Bootstrap DocumentManager class
32362 * @cfg {String} paramName default 'imageUpload'
32363 * @cfg {String} toolTipName default 'filename'
32364 * @cfg {String} method default POST
32365 * @cfg {String} url action url
32366 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32367 * @cfg {Boolean} multiple multiple upload default true
32368 * @cfg {Number} thumbSize default 300
32369 * @cfg {String} fieldLabel
32370 * @cfg {Number} labelWidth default 4
32371 * @cfg {String} labelAlign (left|top) default left
32372 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32373 * @cfg {Number} labellg set the width of label (1-12)
32374 * @cfg {Number} labelmd set the width of label (1-12)
32375 * @cfg {Number} labelsm set the width of label (1-12)
32376 * @cfg {Number} labelxs set the width of label (1-12)
32379 * Create a new DocumentManager
32380 * @param {Object} config The config object
32383 Roo.bootstrap.DocumentManager = function(config){
32384 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32387 this.delegates = [];
32392 * Fire when initial the DocumentManager
32393 * @param {Roo.bootstrap.DocumentManager} this
32398 * inspect selected file
32399 * @param {Roo.bootstrap.DocumentManager} this
32400 * @param {File} file
32405 * Fire when xhr load exception
32406 * @param {Roo.bootstrap.DocumentManager} this
32407 * @param {XMLHttpRequest} xhr
32409 "exception" : true,
32411 * @event afterupload
32412 * Fire when xhr load exception
32413 * @param {Roo.bootstrap.DocumentManager} this
32414 * @param {XMLHttpRequest} xhr
32416 "afterupload" : true,
32419 * prepare the form data
32420 * @param {Roo.bootstrap.DocumentManager} this
32421 * @param {Object} formData
32426 * Fire when remove the file
32427 * @param {Roo.bootstrap.DocumentManager} this
32428 * @param {Object} file
32433 * Fire after refresh the file
32434 * @param {Roo.bootstrap.DocumentManager} this
32439 * Fire after click the image
32440 * @param {Roo.bootstrap.DocumentManager} this
32441 * @param {Object} file
32446 * Fire when upload a image and editable set to true
32447 * @param {Roo.bootstrap.DocumentManager} this
32448 * @param {Object} file
32452 * @event beforeselectfile
32453 * Fire before select file
32454 * @param {Roo.bootstrap.DocumentManager} this
32456 "beforeselectfile" : true,
32459 * Fire before process file
32460 * @param {Roo.bootstrap.DocumentManager} this
32461 * @param {Object} file
32465 * @event previewrendered
32466 * Fire when preview rendered
32467 * @param {Roo.bootstrap.DocumentManager} this
32468 * @param {Object} file
32470 "previewrendered" : true,
32473 "previewResize" : true
32478 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32487 paramName : 'imageUpload',
32488 toolTipName : 'filename',
32491 labelAlign : 'left',
32501 getAutoCreate : function()
32503 var managerWidget = {
32505 cls : 'roo-document-manager',
32509 cls : 'roo-document-manager-selector',
32514 cls : 'roo-document-manager-uploader',
32518 cls : 'roo-document-manager-upload-btn',
32519 html : '<i class="fa fa-plus"></i>'
32530 cls : 'column col-md-12',
32535 if(this.fieldLabel.length){
32540 cls : 'column col-md-12',
32541 html : this.fieldLabel
32545 cls : 'column col-md-12',
32550 if(this.labelAlign == 'left'){
32555 html : this.fieldLabel
32564 if(this.labelWidth > 12){
32565 content[0].style = "width: " + this.labelWidth + 'px';
32568 if(this.labelWidth < 13 && this.labelmd == 0){
32569 this.labelmd = this.labelWidth;
32572 if(this.labellg > 0){
32573 content[0].cls += ' col-lg-' + this.labellg;
32574 content[1].cls += ' col-lg-' + (12 - this.labellg);
32577 if(this.labelmd > 0){
32578 content[0].cls += ' col-md-' + this.labelmd;
32579 content[1].cls += ' col-md-' + (12 - this.labelmd);
32582 if(this.labelsm > 0){
32583 content[0].cls += ' col-sm-' + this.labelsm;
32584 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32587 if(this.labelxs > 0){
32588 content[0].cls += ' col-xs-' + this.labelxs;
32589 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32597 cls : 'row clearfix',
32605 initEvents : function()
32607 this.managerEl = this.el.select('.roo-document-manager', true).first();
32608 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32610 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32611 this.selectorEl.hide();
32614 this.selectorEl.attr('multiple', 'multiple');
32617 this.selectorEl.on('change', this.onFileSelected, this);
32619 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32620 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32622 this.uploader.on('click', this.onUploaderClick, this);
32624 this.renderProgressDialog();
32628 window.addEventListener("resize", function() { _this.refresh(); } );
32630 this.fireEvent('initial', this);
32633 renderProgressDialog : function()
32637 this.progressDialog = new Roo.bootstrap.Modal({
32638 cls : 'roo-document-manager-progress-dialog',
32639 allow_close : false,
32650 btnclick : function() {
32651 _this.uploadCancel();
32657 this.progressDialog.render(Roo.get(document.body));
32659 this.progress = new Roo.bootstrap.Progress({
32660 cls : 'roo-document-manager-progress',
32665 this.progress.render(this.progressDialog.getChildContainer());
32667 this.progressBar = new Roo.bootstrap.ProgressBar({
32668 cls : 'roo-document-manager-progress-bar',
32671 aria_valuemax : 12,
32675 this.progressBar.render(this.progress.getChildContainer());
32678 onUploaderClick : function(e)
32680 e.preventDefault();
32682 if(this.fireEvent('beforeselectfile', this) != false){
32683 this.selectorEl.dom.click();
32688 onFileSelected : function(e)
32690 e.preventDefault();
32692 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32696 Roo.each(this.selectorEl.dom.files, function(file){
32697 if(this.fireEvent('inspect', this, file) != false){
32698 this.files.push(file);
32708 this.selectorEl.dom.value = '';
32710 if(!this.files || !this.files.length){
32714 if(this.boxes > 0 && this.files.length > this.boxes){
32715 this.files = this.files.slice(0, this.boxes);
32718 this.uploader.show();
32720 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32721 this.uploader.hide();
32730 Roo.each(this.files, function(file){
32732 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32733 var f = this.renderPreview(file);
32738 if(file.type.indexOf('image') != -1){
32739 this.delegates.push(
32741 _this.process(file);
32742 }).createDelegate(this)
32750 _this.process(file);
32751 }).createDelegate(this)
32756 this.files = files;
32758 this.delegates = this.delegates.concat(docs);
32760 if(!this.delegates.length){
32765 this.progressBar.aria_valuemax = this.delegates.length;
32772 arrange : function()
32774 if(!this.delegates.length){
32775 this.progressDialog.hide();
32780 var delegate = this.delegates.shift();
32782 this.progressDialog.show();
32784 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32786 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32791 refresh : function()
32793 this.uploader.show();
32795 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32796 this.uploader.hide();
32799 Roo.isTouch ? this.closable(false) : this.closable(true);
32801 this.fireEvent('refresh', this);
32804 onRemove : function(e, el, o)
32806 e.preventDefault();
32808 this.fireEvent('remove', this, o);
32812 remove : function(o)
32816 Roo.each(this.files, function(file){
32817 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32826 this.files = files;
32833 Roo.each(this.files, function(file){
32838 file.target.remove();
32847 onClick : function(e, el, o)
32849 e.preventDefault();
32851 this.fireEvent('click', this, o);
32855 closable : function(closable)
32857 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32859 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32871 xhrOnLoad : function(xhr)
32873 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32877 if (xhr.readyState !== 4) {
32879 this.fireEvent('exception', this, xhr);
32883 var response = Roo.decode(xhr.responseText);
32885 if(!response.success){
32887 this.fireEvent('exception', this, xhr);
32891 var file = this.renderPreview(response.data);
32893 this.files.push(file);
32897 this.fireEvent('afterupload', this, xhr);
32901 xhrOnError : function(xhr)
32903 Roo.log('xhr on error');
32905 var response = Roo.decode(xhr.responseText);
32912 process : function(file)
32914 if(this.fireEvent('process', this, file) !== false){
32915 if(this.editable && file.type.indexOf('image') != -1){
32916 this.fireEvent('edit', this, file);
32920 this.uploadStart(file, false);
32927 uploadStart : function(file, crop)
32929 this.xhr = new XMLHttpRequest();
32931 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32936 file.xhr = this.xhr;
32938 this.managerEl.createChild({
32940 cls : 'roo-document-manager-loading',
32944 tooltip : file.name,
32945 cls : 'roo-document-manager-thumb',
32946 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32952 this.xhr.open(this.method, this.url, true);
32955 "Accept": "application/json",
32956 "Cache-Control": "no-cache",
32957 "X-Requested-With": "XMLHttpRequest"
32960 for (var headerName in headers) {
32961 var headerValue = headers[headerName];
32963 this.xhr.setRequestHeader(headerName, headerValue);
32969 this.xhr.onload = function()
32971 _this.xhrOnLoad(_this.xhr);
32974 this.xhr.onerror = function()
32976 _this.xhrOnError(_this.xhr);
32979 var formData = new FormData();
32981 formData.append('returnHTML', 'NO');
32984 formData.append('crop', crop);
32987 formData.append(this.paramName, file, file.name);
32994 if(this.fireEvent('prepare', this, formData, options) != false){
32996 if(options.manually){
33000 this.xhr.send(formData);
33004 this.uploadCancel();
33007 uploadCancel : function()
33013 this.delegates = [];
33015 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33022 renderPreview : function(file)
33024 if(typeof(file.target) != 'undefined' && file.target){
33028 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33030 var previewEl = this.managerEl.createChild({
33032 cls : 'roo-document-manager-preview',
33036 tooltip : file[this.toolTipName],
33037 cls : 'roo-document-manager-thumb',
33038 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33043 html : '<i class="fa fa-times-circle"></i>'
33048 var close = previewEl.select('button.close', true).first();
33050 close.on('click', this.onRemove, this, file);
33052 file.target = previewEl;
33054 var image = previewEl.select('img', true).first();
33058 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33060 image.on('click', this.onClick, this, file);
33062 this.fireEvent('previewrendered', this, file);
33068 onPreviewLoad : function(file, image)
33070 if(typeof(file.target) == 'undefined' || !file.target){
33074 var width = image.dom.naturalWidth || image.dom.width;
33075 var height = image.dom.naturalHeight || image.dom.height;
33077 if(!this.previewResize) {
33081 if(width > height){
33082 file.target.addClass('wide');
33086 file.target.addClass('tall');
33091 uploadFromSource : function(file, crop)
33093 this.xhr = new XMLHttpRequest();
33095 this.managerEl.createChild({
33097 cls : 'roo-document-manager-loading',
33101 tooltip : file.name,
33102 cls : 'roo-document-manager-thumb',
33103 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33109 this.xhr.open(this.method, this.url, true);
33112 "Accept": "application/json",
33113 "Cache-Control": "no-cache",
33114 "X-Requested-With": "XMLHttpRequest"
33117 for (var headerName in headers) {
33118 var headerValue = headers[headerName];
33120 this.xhr.setRequestHeader(headerName, headerValue);
33126 this.xhr.onload = function()
33128 _this.xhrOnLoad(_this.xhr);
33131 this.xhr.onerror = function()
33133 _this.xhrOnError(_this.xhr);
33136 var formData = new FormData();
33138 formData.append('returnHTML', 'NO');
33140 formData.append('crop', crop);
33142 if(typeof(file.filename) != 'undefined'){
33143 formData.append('filename', file.filename);
33146 if(typeof(file.mimetype) != 'undefined'){
33147 formData.append('mimetype', file.mimetype);
33152 if(this.fireEvent('prepare', this, formData) != false){
33153 this.xhr.send(formData);
33163 * @class Roo.bootstrap.DocumentViewer
33164 * @extends Roo.bootstrap.Component
33165 * Bootstrap DocumentViewer class
33166 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33167 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33170 * Create a new DocumentViewer
33171 * @param {Object} config The config object
33174 Roo.bootstrap.DocumentViewer = function(config){
33175 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33180 * Fire after initEvent
33181 * @param {Roo.bootstrap.DocumentViewer} this
33187 * @param {Roo.bootstrap.DocumentViewer} this
33192 * Fire after download button
33193 * @param {Roo.bootstrap.DocumentViewer} this
33198 * Fire after trash button
33199 * @param {Roo.bootstrap.DocumentViewer} this
33206 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33208 showDownload : true,
33212 getAutoCreate : function()
33216 cls : 'roo-document-viewer',
33220 cls : 'roo-document-viewer-body',
33224 cls : 'roo-document-viewer-thumb',
33228 cls : 'roo-document-viewer-image'
33236 cls : 'roo-document-viewer-footer',
33239 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33243 cls : 'btn-group roo-document-viewer-download',
33247 cls : 'btn btn-default',
33248 html : '<i class="fa fa-download"></i>'
33254 cls : 'btn-group roo-document-viewer-trash',
33258 cls : 'btn btn-default',
33259 html : '<i class="fa fa-trash"></i>'
33272 initEvents : function()
33274 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33275 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33277 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33278 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33280 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33281 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33283 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33284 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33286 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33287 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33289 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33290 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33292 this.bodyEl.on('click', this.onClick, this);
33293 this.downloadBtn.on('click', this.onDownload, this);
33294 this.trashBtn.on('click', this.onTrash, this);
33296 this.downloadBtn.hide();
33297 this.trashBtn.hide();
33299 if(this.showDownload){
33300 this.downloadBtn.show();
33303 if(this.showTrash){
33304 this.trashBtn.show();
33307 if(!this.showDownload && !this.showTrash) {
33308 this.footerEl.hide();
33313 initial : function()
33315 this.fireEvent('initial', this);
33319 onClick : function(e)
33321 e.preventDefault();
33323 this.fireEvent('click', this);
33326 onDownload : function(e)
33328 e.preventDefault();
33330 this.fireEvent('download', this);
33333 onTrash : function(e)
33335 e.preventDefault();
33337 this.fireEvent('trash', this);
33349 * @class Roo.bootstrap.form.FieldLabel
33350 * @extends Roo.bootstrap.Component
33351 * Bootstrap FieldLabel class
33352 * @cfg {String} html contents of the element
33353 * @cfg {String} tag tag of the element default label
33354 * @cfg {String} cls class of the element
33355 * @cfg {String} target label target
33356 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33357 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33358 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33359 * @cfg {String} iconTooltip default "This field is required"
33360 * @cfg {String} indicatorpos (left|right) default left
33363 * Create a new FieldLabel
33364 * @param {Object} config The config object
33367 Roo.bootstrap.form.FieldLabel = function(config){
33368 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33373 * Fires after the field has been marked as invalid.
33374 * @param {Roo.form.FieldLabel} this
33375 * @param {String} msg The validation message
33380 * Fires after the field has been validated with no errors.
33381 * @param {Roo.form.FieldLabel} this
33387 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33394 invalidClass : 'has-warning',
33395 validClass : 'has-success',
33396 iconTooltip : 'This field is required',
33397 indicatorpos : 'left',
33399 getAutoCreate : function(){
33402 if (!this.allowBlank) {
33408 cls : 'roo-bootstrap-field-label ' + this.cls,
33413 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33414 tooltip : this.iconTooltip
33423 if(this.indicatorpos == 'right'){
33426 cls : 'roo-bootstrap-field-label ' + this.cls,
33435 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33436 tooltip : this.iconTooltip
33445 initEvents: function()
33447 Roo.bootstrap.Element.superclass.initEvents.call(this);
33449 this.indicator = this.indicatorEl();
33451 if(this.indicator){
33452 this.indicator.removeClass('visible');
33453 this.indicator.addClass('invisible');
33456 Roo.bootstrap.form.FieldLabel.register(this);
33459 indicatorEl : function()
33461 var indicator = this.el.select('i.roo-required-indicator',true).first();
33472 * Mark this field as valid
33474 markValid : function()
33476 if(this.indicator){
33477 this.indicator.removeClass('visible');
33478 this.indicator.addClass('invisible');
33480 if (Roo.bootstrap.version == 3) {
33481 this.el.removeClass(this.invalidClass);
33482 this.el.addClass(this.validClass);
33484 this.el.removeClass('is-invalid');
33485 this.el.addClass('is-valid');
33489 this.fireEvent('valid', this);
33493 * Mark this field as invalid
33494 * @param {String} msg The validation message
33496 markInvalid : function(msg)
33498 if(this.indicator){
33499 this.indicator.removeClass('invisible');
33500 this.indicator.addClass('visible');
33502 if (Roo.bootstrap.version == 3) {
33503 this.el.removeClass(this.validClass);
33504 this.el.addClass(this.invalidClass);
33506 this.el.removeClass('is-valid');
33507 this.el.addClass('is-invalid');
33511 this.fireEvent('invalid', this, msg);
33517 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33522 * register a FieldLabel Group
33523 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33525 register : function(label)
33527 if(this.groups.hasOwnProperty(label.target)){
33531 this.groups[label.target] = label;
33535 * fetch a FieldLabel Group based on the target
33536 * @param {string} target
33537 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33539 get: function(target) {
33540 if (typeof(this.groups[target]) == 'undefined') {
33544 return this.groups[target] ;
33553 * page DateSplitField.
33559 * @class Roo.bootstrap.form.DateSplitField
33560 * @extends Roo.bootstrap.Component
33561 * Bootstrap DateSplitField class
33562 * @cfg {string} fieldLabel - the label associated
33563 * @cfg {Number} labelWidth set the width of label (0-12)
33564 * @cfg {String} labelAlign (top|left)
33565 * @cfg {Boolean} dayAllowBlank (true|false) default false
33566 * @cfg {Boolean} monthAllowBlank (true|false) default false
33567 * @cfg {Boolean} yearAllowBlank (true|false) default false
33568 * @cfg {string} dayPlaceholder
33569 * @cfg {string} monthPlaceholder
33570 * @cfg {string} yearPlaceholder
33571 * @cfg {string} dayFormat default 'd'
33572 * @cfg {string} monthFormat default 'm'
33573 * @cfg {string} yearFormat default 'Y'
33574 * @cfg {Number} labellg set the width of label (1-12)
33575 * @cfg {Number} labelmd set the width of label (1-12)
33576 * @cfg {Number} labelsm set the width of label (1-12)
33577 * @cfg {Number} labelxs set the width of label (1-12)
33581 * Create a new DateSplitField
33582 * @param {Object} config The config object
33585 Roo.bootstrap.form.DateSplitField = function(config){
33586 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33592 * getting the data of years
33593 * @param {Roo.bootstrap.form.DateSplitField} this
33594 * @param {Object} years
33599 * getting the data of days
33600 * @param {Roo.bootstrap.form.DateSplitField} this
33601 * @param {Object} days
33606 * Fires after the field has been marked as invalid.
33607 * @param {Roo.form.Field} this
33608 * @param {String} msg The validation message
33613 * Fires after the field has been validated with no errors.
33614 * @param {Roo.form.Field} this
33620 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33623 labelAlign : 'top',
33625 dayAllowBlank : false,
33626 monthAllowBlank : false,
33627 yearAllowBlank : false,
33628 dayPlaceholder : '',
33629 monthPlaceholder : '',
33630 yearPlaceholder : '',
33634 isFormField : true,
33640 getAutoCreate : function()
33644 cls : 'row roo-date-split-field-group',
33649 cls : 'form-hidden-field roo-date-split-field-group-value',
33655 var labelCls = 'col-md-12';
33656 var contentCls = 'col-md-4';
33658 if(this.fieldLabel){
33662 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33666 html : this.fieldLabel
33671 if(this.labelAlign == 'left'){
33673 if(this.labelWidth > 12){
33674 label.style = "width: " + this.labelWidth + 'px';
33677 if(this.labelWidth < 13 && this.labelmd == 0){
33678 this.labelmd = this.labelWidth;
33681 if(this.labellg > 0){
33682 labelCls = ' col-lg-' + this.labellg;
33683 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33686 if(this.labelmd > 0){
33687 labelCls = ' col-md-' + this.labelmd;
33688 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33691 if(this.labelsm > 0){
33692 labelCls = ' col-sm-' + this.labelsm;
33693 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33696 if(this.labelxs > 0){
33697 labelCls = ' col-xs-' + this.labelxs;
33698 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33702 label.cls += ' ' + labelCls;
33704 cfg.cn.push(label);
33707 Roo.each(['day', 'month', 'year'], function(t){
33710 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33717 inputEl: function ()
33719 return this.el.select('.roo-date-split-field-group-value', true).first();
33722 onRender : function(ct, position)
33726 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33728 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33730 this.dayField = new Roo.bootstrap.form.ComboBox({
33731 allowBlank : this.dayAllowBlank,
33732 alwaysQuery : true,
33733 displayField : 'value',
33736 forceSelection : true,
33738 placeholder : this.dayPlaceholder,
33739 selectOnFocus : true,
33740 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33741 triggerAction : 'all',
33743 valueField : 'value',
33744 store : new Roo.data.SimpleStore({
33745 data : (function() {
33747 _this.fireEvent('days', _this, days);
33750 fields : [ 'value' ]
33753 select : function (_self, record, index)
33755 _this.setValue(_this.getValue());
33760 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33762 this.monthField = new Roo.bootstrap.form.MonthField({
33763 after : '<i class=\"fa fa-calendar\"></i>',
33764 allowBlank : this.monthAllowBlank,
33765 placeholder : this.monthPlaceholder,
33768 render : function (_self)
33770 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33771 e.preventDefault();
33775 select : function (_self, oldvalue, newvalue)
33777 _this.setValue(_this.getValue());
33782 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33784 this.yearField = new Roo.bootstrap.form.ComboBox({
33785 allowBlank : this.yearAllowBlank,
33786 alwaysQuery : true,
33787 displayField : 'value',
33790 forceSelection : true,
33792 placeholder : this.yearPlaceholder,
33793 selectOnFocus : true,
33794 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33795 triggerAction : 'all',
33797 valueField : 'value',
33798 store : new Roo.data.SimpleStore({
33799 data : (function() {
33801 _this.fireEvent('years', _this, years);
33804 fields : [ 'value' ]
33807 select : function (_self, record, index)
33809 _this.setValue(_this.getValue());
33814 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33817 setValue : function(v, format)
33819 this.inputEl.dom.value = v;
33821 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33823 var d = Date.parseDate(v, f);
33830 this.setDay(d.format(this.dayFormat));
33831 this.setMonth(d.format(this.monthFormat));
33832 this.setYear(d.format(this.yearFormat));
33839 setDay : function(v)
33841 this.dayField.setValue(v);
33842 this.inputEl.dom.value = this.getValue();
33847 setMonth : function(v)
33849 this.monthField.setValue(v, true);
33850 this.inputEl.dom.value = this.getValue();
33855 setYear : function(v)
33857 this.yearField.setValue(v);
33858 this.inputEl.dom.value = this.getValue();
33863 getDay : function()
33865 return this.dayField.getValue();
33868 getMonth : function()
33870 return this.monthField.getValue();
33873 getYear : function()
33875 return this.yearField.getValue();
33878 getValue : function()
33880 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33882 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33892 this.inputEl.dom.value = '';
33897 validate : function()
33899 var d = this.dayField.validate();
33900 var m = this.monthField.validate();
33901 var y = this.yearField.validate();
33906 (!this.dayAllowBlank && !d) ||
33907 (!this.monthAllowBlank && !m) ||
33908 (!this.yearAllowBlank && !y)
33913 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33922 this.markInvalid();
33927 markValid : function()
33930 var label = this.el.select('label', true).first();
33931 var icon = this.el.select('i.fa-star', true).first();
33937 this.fireEvent('valid', this);
33941 * Mark this field as invalid
33942 * @param {String} msg The validation message
33944 markInvalid : function(msg)
33947 var label = this.el.select('label', true).first();
33948 var icon = this.el.select('i.fa-star', true).first();
33950 if(label && !icon){
33951 this.el.select('.roo-date-split-field-label', true).createChild({
33953 cls : 'text-danger fa fa-lg fa-star',
33954 tooltip : 'This field is required',
33955 style : 'margin-right:5px;'
33959 this.fireEvent('invalid', this, msg);
33962 clearInvalid : function()
33964 var label = this.el.select('label', true).first();
33965 var icon = this.el.select('i.fa-star', true).first();
33971 this.fireEvent('valid', this);
33974 getName: function()
33984 * @class Roo.bootstrap.LayoutMasonry
33985 * @extends Roo.bootstrap.Component
33986 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
33987 * Bootstrap Layout Masonry class
33990 * http://masonry.desandro.com
33992 * The idea is to render all the bricks based on vertical width...
33994 * The original code extends 'outlayer' - we might need to use that....
33997 * Create a new Element
33998 * @param {Object} config The config object
34001 Roo.bootstrap.LayoutMasonry = function(config){
34003 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34007 Roo.bootstrap.LayoutMasonry.register(this);
34013 * Fire after layout the items
34014 * @param {Roo.bootstrap.LayoutMasonry} this
34015 * @param {Roo.EventObject} e
34022 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34025 * @cfg {Boolean} isLayoutInstant = no animation?
34027 isLayoutInstant : false, // needed?
34030 * @cfg {Number} boxWidth width of the columns
34035 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34040 * @cfg {Number} padWidth padding below box..
34045 * @cfg {Number} gutter gutter width..
34050 * @cfg {Number} maxCols maximum number of columns
34056 * @cfg {Boolean} isAutoInitial defalut true
34058 isAutoInitial : true,
34063 * @cfg {Boolean} isHorizontal defalut false
34065 isHorizontal : false,
34067 currentSize : null,
34073 bricks: null, //CompositeElement
34077 _isLayoutInited : false,
34079 // isAlternative : false, // only use for vertical layout...
34082 * @cfg {Number} alternativePadWidth padding below box..
34084 alternativePadWidth : 50,
34086 selectedBrick : [],
34088 getAutoCreate : function(){
34090 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34094 cls: 'blog-masonary-wrapper ' + this.cls,
34096 cls : 'mas-boxes masonary'
34103 getChildContainer: function( )
34105 if (this.boxesEl) {
34106 return this.boxesEl;
34109 this.boxesEl = this.el.select('.mas-boxes').first();
34111 return this.boxesEl;
34115 initEvents : function()
34119 if(this.isAutoInitial){
34120 Roo.log('hook children rendered');
34121 this.on('childrenrendered', function() {
34122 Roo.log('children rendered');
34128 initial : function()
34130 this.selectedBrick = [];
34132 this.currentSize = this.el.getBox(true);
34134 Roo.EventManager.onWindowResize(this.resize, this);
34136 if(!this.isAutoInitial){
34144 //this.layout.defer(500,this);
34148 resize : function()
34150 var cs = this.el.getBox(true);
34153 this.currentSize.width == cs.width &&
34154 this.currentSize.x == cs.x &&
34155 this.currentSize.height == cs.height &&
34156 this.currentSize.y == cs.y
34158 Roo.log("no change in with or X or Y");
34162 this.currentSize = cs;
34168 layout : function()
34170 this._resetLayout();
34172 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34174 this.layoutItems( isInstant );
34176 this._isLayoutInited = true;
34178 this.fireEvent('layout', this);
34182 _resetLayout : function()
34184 if(this.isHorizontal){
34185 this.horizontalMeasureColumns();
34189 this.verticalMeasureColumns();
34193 verticalMeasureColumns : function()
34195 this.getContainerWidth();
34197 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34198 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34202 var boxWidth = this.boxWidth + this.padWidth;
34204 if(this.containerWidth < this.boxWidth){
34205 boxWidth = this.containerWidth
34208 var containerWidth = this.containerWidth;
34210 var cols = Math.floor(containerWidth / boxWidth);
34212 this.cols = Math.max( cols, 1 );
34214 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34216 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34218 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34220 this.colWidth = boxWidth + avail - this.padWidth;
34222 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34223 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34226 horizontalMeasureColumns : function()
34228 this.getContainerWidth();
34230 var boxWidth = this.boxWidth;
34232 if(this.containerWidth < boxWidth){
34233 boxWidth = this.containerWidth;
34236 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34238 this.el.setHeight(boxWidth);
34242 getContainerWidth : function()
34244 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34247 layoutItems : function( isInstant )
34249 Roo.log(this.bricks);
34251 var items = Roo.apply([], this.bricks);
34253 if(this.isHorizontal){
34254 this._horizontalLayoutItems( items , isInstant );
34258 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34259 // this._verticalAlternativeLayoutItems( items , isInstant );
34263 this._verticalLayoutItems( items , isInstant );
34267 _verticalLayoutItems : function ( items , isInstant)
34269 if ( !items || !items.length ) {
34274 ['xs', 'xs', 'xs', 'tall'],
34275 ['xs', 'xs', 'tall'],
34276 ['xs', 'xs', 'sm'],
34277 ['xs', 'xs', 'xs'],
34283 ['sm', 'xs', 'xs'],
34287 ['tall', 'xs', 'xs', 'xs'],
34288 ['tall', 'xs', 'xs'],
34300 Roo.each(items, function(item, k){
34302 switch (item.size) {
34303 // these layouts take up a full box,
34314 boxes.push([item]);
34337 var filterPattern = function(box, length)
34345 var pattern = box.slice(0, length);
34349 Roo.each(pattern, function(i){
34350 format.push(i.size);
34353 Roo.each(standard, function(s){
34355 if(String(s) != String(format)){
34364 if(!match && length == 1){
34369 filterPattern(box, length - 1);
34373 queue.push(pattern);
34375 box = box.slice(length, box.length);
34377 filterPattern(box, 4);
34383 Roo.each(boxes, function(box, k){
34389 if(box.length == 1){
34394 filterPattern(box, 4);
34398 this._processVerticalLayoutQueue( queue, isInstant );
34402 // _verticalAlternativeLayoutItems : function( items , isInstant )
34404 // if ( !items || !items.length ) {
34408 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34412 _horizontalLayoutItems : function ( items , isInstant)
34414 if ( !items || !items.length || items.length < 3) {
34420 var eItems = items.slice(0, 3);
34422 items = items.slice(3, items.length);
34425 ['xs', 'xs', 'xs', 'wide'],
34426 ['xs', 'xs', 'wide'],
34427 ['xs', 'xs', 'sm'],
34428 ['xs', 'xs', 'xs'],
34434 ['sm', 'xs', 'xs'],
34438 ['wide', 'xs', 'xs', 'xs'],
34439 ['wide', 'xs', 'xs'],
34452 Roo.each(items, function(item, k){
34454 switch (item.size) {
34465 boxes.push([item]);
34489 var filterPattern = function(box, length)
34497 var pattern = box.slice(0, length);
34501 Roo.each(pattern, function(i){
34502 format.push(i.size);
34505 Roo.each(standard, function(s){
34507 if(String(s) != String(format)){
34516 if(!match && length == 1){
34521 filterPattern(box, length - 1);
34525 queue.push(pattern);
34527 box = box.slice(length, box.length);
34529 filterPattern(box, 4);
34535 Roo.each(boxes, function(box, k){
34541 if(box.length == 1){
34546 filterPattern(box, 4);
34553 var pos = this.el.getBox(true);
34557 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34559 var hit_end = false;
34561 Roo.each(queue, function(box){
34565 Roo.each(box, function(b){
34567 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34577 Roo.each(box, function(b){
34579 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34582 mx = Math.max(mx, b.x);
34586 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34590 Roo.each(box, function(b){
34592 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34606 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34609 /** Sets position of item in DOM
34610 * @param {Element} item
34611 * @param {Number} x - horizontal position
34612 * @param {Number} y - vertical position
34613 * @param {Boolean} isInstant - disables transitions
34615 _processVerticalLayoutQueue : function( queue, isInstant )
34617 var pos = this.el.getBox(true);
34622 for (var i = 0; i < this.cols; i++){
34626 Roo.each(queue, function(box, k){
34628 var col = k % this.cols;
34630 Roo.each(box, function(b,kk){
34632 b.el.position('absolute');
34634 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34635 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34637 if(b.size == 'md-left' || b.size == 'md-right'){
34638 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34639 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34642 b.el.setWidth(width);
34643 b.el.setHeight(height);
34645 b.el.select('iframe',true).setSize(width,height);
34649 for (var i = 0; i < this.cols; i++){
34651 if(maxY[i] < maxY[col]){
34656 col = Math.min(col, i);
34660 x = pos.x + col * (this.colWidth + this.padWidth);
34664 var positions = [];
34666 switch (box.length){
34668 positions = this.getVerticalOneBoxColPositions(x, y, box);
34671 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34674 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34677 positions = this.getVerticalFourBoxColPositions(x, y, box);
34683 Roo.each(box, function(b,kk){
34685 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34687 var sz = b.el.getSize();
34689 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34697 for (var i = 0; i < this.cols; i++){
34698 mY = Math.max(mY, maxY[i]);
34701 this.el.setHeight(mY - pos.y);
34705 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34707 // var pos = this.el.getBox(true);
34710 // var maxX = pos.right;
34712 // var maxHeight = 0;
34714 // Roo.each(items, function(item, k){
34718 // item.el.position('absolute');
34720 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34722 // item.el.setWidth(width);
34724 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34726 // item.el.setHeight(height);
34729 // item.el.setXY([x, y], isInstant ? false : true);
34731 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34734 // y = y + height + this.alternativePadWidth;
34736 // maxHeight = maxHeight + height + this.alternativePadWidth;
34740 // this.el.setHeight(maxHeight);
34744 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34746 var pos = this.el.getBox(true);
34751 var maxX = pos.right;
34753 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34755 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34757 Roo.each(queue, function(box, k){
34759 Roo.each(box, function(b, kk){
34761 b.el.position('absolute');
34763 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34764 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34766 if(b.size == 'md-left' || b.size == 'md-right'){
34767 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34768 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34771 b.el.setWidth(width);
34772 b.el.setHeight(height);
34780 var positions = [];
34782 switch (box.length){
34784 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34787 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34790 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34793 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34799 Roo.each(box, function(b,kk){
34801 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34803 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34811 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34813 Roo.each(eItems, function(b,k){
34815 b.size = (k == 0) ? 'sm' : 'xs';
34816 b.x = (k == 0) ? 2 : 1;
34817 b.y = (k == 0) ? 2 : 1;
34819 b.el.position('absolute');
34821 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34823 b.el.setWidth(width);
34825 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34827 b.el.setHeight(height);
34831 var positions = [];
34834 x : maxX - this.unitWidth * 2 - this.gutter,
34839 x : maxX - this.unitWidth,
34840 y : minY + (this.unitWidth + this.gutter) * 2
34844 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34848 Roo.each(eItems, function(b,k){
34850 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34856 getVerticalOneBoxColPositions : function(x, y, box)
34860 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34862 if(box[0].size == 'md-left'){
34866 if(box[0].size == 'md-right'){
34871 x : x + (this.unitWidth + this.gutter) * rand,
34878 getVerticalTwoBoxColPositions : function(x, y, box)
34882 if(box[0].size == 'xs'){
34886 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34890 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34904 x : x + (this.unitWidth + this.gutter) * 2,
34905 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34912 getVerticalThreeBoxColPositions : function(x, y, box)
34916 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34924 x : x + (this.unitWidth + this.gutter) * 1,
34929 x : x + (this.unitWidth + this.gutter) * 2,
34937 if(box[0].size == 'xs' && box[1].size == 'xs'){
34946 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34950 x : x + (this.unitWidth + this.gutter) * 1,
34964 x : x + (this.unitWidth + this.gutter) * 2,
34969 x : x + (this.unitWidth + this.gutter) * 2,
34970 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34977 getVerticalFourBoxColPositions : function(x, y, box)
34981 if(box[0].size == 'xs'){
34990 y : y + (this.unitHeight + this.gutter) * 1
34995 y : y + (this.unitHeight + this.gutter) * 2
34999 x : x + (this.unitWidth + this.gutter) * 1,
35013 x : x + (this.unitWidth + this.gutter) * 2,
35018 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35019 y : y + (this.unitHeight + this.gutter) * 1
35023 x : x + (this.unitWidth + this.gutter) * 2,
35024 y : y + (this.unitWidth + this.gutter) * 2
35031 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35035 if(box[0].size == 'md-left'){
35037 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35044 if(box[0].size == 'md-right'){
35046 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35047 y : minY + (this.unitWidth + this.gutter) * 1
35053 var rand = Math.floor(Math.random() * (4 - box[0].y));
35056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35057 y : minY + (this.unitWidth + this.gutter) * rand
35064 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35068 if(box[0].size == 'xs'){
35071 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35076 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35077 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35085 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35090 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35091 y : minY + (this.unitWidth + this.gutter) * 2
35098 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35102 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35105 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35110 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35111 y : minY + (this.unitWidth + this.gutter) * 1
35115 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35116 y : minY + (this.unitWidth + this.gutter) * 2
35123 if(box[0].size == 'xs' && box[1].size == 'xs'){
35126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35131 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35136 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35137 y : minY + (this.unitWidth + this.gutter) * 1
35145 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35150 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35151 y : minY + (this.unitWidth + this.gutter) * 2
35155 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35156 y : minY + (this.unitWidth + this.gutter) * 2
35163 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35167 if(box[0].size == 'xs'){
35170 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35175 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35180 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),
35185 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35186 y : minY + (this.unitWidth + this.gutter) * 1
35194 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35199 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35200 y : minY + (this.unitWidth + this.gutter) * 2
35204 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35205 y : minY + (this.unitWidth + this.gutter) * 2
35209 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35210 y : minY + (this.unitWidth + this.gutter) * 2
35218 * remove a Masonry Brick
35219 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35221 removeBrick : function(brick_id)
35227 for (var i = 0; i<this.bricks.length; i++) {
35228 if (this.bricks[i].id == brick_id) {
35229 this.bricks.splice(i,1);
35230 this.el.dom.removeChild(Roo.get(brick_id).dom);
35237 * adds a Masonry Brick
35238 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35240 addBrick : function(cfg)
35242 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35243 //this.register(cn);
35244 cn.parentId = this.id;
35245 cn.render(this.el);
35250 * register a Masonry Brick
35251 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35254 register : function(brick)
35256 this.bricks.push(brick);
35257 brick.masonryId = this.id;
35261 * clear all the Masonry Brick
35263 clearAll : function()
35266 //this.getChildContainer().dom.innerHTML = "";
35267 this.el.dom.innerHTML = '';
35270 getSelected : function()
35272 if (!this.selectedBrick) {
35276 return this.selectedBrick;
35280 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35284 * register a Masonry Layout
35285 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35288 register : function(layout)
35290 this.groups[layout.id] = layout;
35293 * fetch a Masonry Layout based on the masonry layout ID
35294 * @param {string} the masonry layout to add
35295 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35298 get: function(layout_id) {
35299 if (typeof(this.groups[layout_id]) == 'undefined') {
35302 return this.groups[layout_id] ;
35314 * http://masonry.desandro.com
35316 * The idea is to render all the bricks based on vertical width...
35318 * The original code extends 'outlayer' - we might need to use that....
35324 * @class Roo.bootstrap.LayoutMasonryAuto
35325 * @extends Roo.bootstrap.Component
35326 * Bootstrap Layout Masonry class
35329 * Create a new Element
35330 * @param {Object} config The config object
35333 Roo.bootstrap.LayoutMasonryAuto = function(config){
35334 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35337 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35340 * @cfg {Boolean} isFitWidth - resize the width..
35342 isFitWidth : false, // options..
35344 * @cfg {Boolean} isOriginLeft = left align?
35346 isOriginLeft : true,
35348 * @cfg {Boolean} isOriginTop = top align?
35350 isOriginTop : false,
35352 * @cfg {Boolean} isLayoutInstant = no animation?
35354 isLayoutInstant : false, // needed?
35356 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35358 isResizingContainer : true,
35360 * @cfg {Number} columnWidth width of the columns
35366 * @cfg {Number} maxCols maximum number of columns
35371 * @cfg {Number} padHeight padding below box..
35377 * @cfg {Boolean} isAutoInitial defalut true
35380 isAutoInitial : true,
35386 initialColumnWidth : 0,
35387 currentSize : null,
35389 colYs : null, // array.
35396 bricks: null, //CompositeElement
35397 cols : 0, // array?
35398 // element : null, // wrapped now this.el
35399 _isLayoutInited : null,
35402 getAutoCreate : function(){
35406 cls: 'blog-masonary-wrapper ' + this.cls,
35408 cls : 'mas-boxes masonary'
35415 getChildContainer: function( )
35417 if (this.boxesEl) {
35418 return this.boxesEl;
35421 this.boxesEl = this.el.select('.mas-boxes').first();
35423 return this.boxesEl;
35427 initEvents : function()
35431 if(this.isAutoInitial){
35432 Roo.log('hook children rendered');
35433 this.on('childrenrendered', function() {
35434 Roo.log('children rendered');
35441 initial : function()
35443 this.reloadItems();
35445 this.currentSize = this.el.getBox(true);
35447 /// was window resize... - let's see if this works..
35448 Roo.EventManager.onWindowResize(this.resize, this);
35450 if(!this.isAutoInitial){
35455 this.layout.defer(500,this);
35458 reloadItems: function()
35460 this.bricks = this.el.select('.masonry-brick', true);
35462 this.bricks.each(function(b) {
35463 //Roo.log(b.getSize());
35464 if (!b.attr('originalwidth')) {
35465 b.attr('originalwidth', b.getSize().width);
35470 Roo.log(this.bricks.elements.length);
35473 resize : function()
35476 var cs = this.el.getBox(true);
35478 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35479 Roo.log("no change in with or X");
35482 this.currentSize = cs;
35486 layout : function()
35489 this._resetLayout();
35490 //this._manageStamps();
35492 // don't animate first layout
35493 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35494 this.layoutItems( isInstant );
35496 // flag for initalized
35497 this._isLayoutInited = true;
35500 layoutItems : function( isInstant )
35502 //var items = this._getItemsForLayout( this.items );
35503 // original code supports filtering layout items.. we just ignore it..
35505 this._layoutItems( this.bricks , isInstant );
35507 this._postLayout();
35509 _layoutItems : function ( items , isInstant)
35511 //this.fireEvent( 'layout', this, items );
35514 if ( !items || !items.elements.length ) {
35515 // no items, emit event with empty array
35520 items.each(function(item) {
35521 Roo.log("layout item");
35523 // get x/y object from method
35524 var position = this._getItemLayoutPosition( item );
35526 position.item = item;
35527 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35528 queue.push( position );
35531 this._processLayoutQueue( queue );
35533 /** Sets position of item in DOM
35534 * @param {Element} item
35535 * @param {Number} x - horizontal position
35536 * @param {Number} y - vertical position
35537 * @param {Boolean} isInstant - disables transitions
35539 _processLayoutQueue : function( queue )
35541 for ( var i=0, len = queue.length; i < len; i++ ) {
35542 var obj = queue[i];
35543 obj.item.position('absolute');
35544 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35550 * Any logic you want to do after each layout,
35551 * i.e. size the container
35553 _postLayout : function()
35555 this.resizeContainer();
35558 resizeContainer : function()
35560 if ( !this.isResizingContainer ) {
35563 var size = this._getContainerSize();
35565 this.el.setSize(size.width,size.height);
35566 this.boxesEl.setSize(size.width,size.height);
35572 _resetLayout : function()
35574 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35575 this.colWidth = this.el.getWidth();
35576 //this.gutter = this.el.getWidth();
35578 this.measureColumns();
35584 this.colYs.push( 0 );
35590 measureColumns : function()
35592 this.getContainerWidth();
35593 // if columnWidth is 0, default to outerWidth of first item
35594 if ( !this.columnWidth ) {
35595 var firstItem = this.bricks.first();
35596 Roo.log(firstItem);
35597 this.columnWidth = this.containerWidth;
35598 if (firstItem && firstItem.attr('originalwidth') ) {
35599 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35601 // columnWidth fall back to item of first element
35602 Roo.log("set column width?");
35603 this.initialColumnWidth = this.columnWidth ;
35605 // if first elem has no width, default to size of container
35610 if (this.initialColumnWidth) {
35611 this.columnWidth = this.initialColumnWidth;
35616 // column width is fixed at the top - however if container width get's smaller we should
35619 // this bit calcs how man columns..
35621 var columnWidth = this.columnWidth += this.gutter;
35623 // calculate columns
35624 var containerWidth = this.containerWidth + this.gutter;
35626 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35627 // fix rounding errors, typically with gutters
35628 var excess = columnWidth - containerWidth % columnWidth;
35631 // if overshoot is less than a pixel, round up, otherwise floor it
35632 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35633 cols = Math[ mathMethod ]( cols );
35634 this.cols = Math.max( cols, 1 );
35635 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35637 // padding positioning..
35638 var totalColWidth = this.cols * this.columnWidth;
35639 var padavail = this.containerWidth - totalColWidth;
35640 // so for 2 columns - we need 3 'pads'
35642 var padNeeded = (1+this.cols) * this.padWidth;
35644 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35646 this.columnWidth += padExtra
35647 //this.padWidth = Math.floor(padavail / ( this.cols));
35649 // adjust colum width so that padding is fixed??
35651 // we have 3 columns ... total = width * 3
35652 // we have X left over... that should be used by
35654 //if (this.expandC) {
35662 getContainerWidth : function()
35664 /* // container is parent if fit width
35665 var container = this.isFitWidth ? this.element.parentNode : this.element;
35666 // check that this.size and size are there
35667 // IE8 triggers resize on body size change, so they might not be
35669 var size = getSize( container ); //FIXME
35670 this.containerWidth = size && size.innerWidth; //FIXME
35673 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35677 _getItemLayoutPosition : function( item ) // what is item?
35679 // we resize the item to our columnWidth..
35681 item.setWidth(this.columnWidth);
35682 item.autoBoxAdjust = false;
35684 var sz = item.getSize();
35686 // how many columns does this brick span
35687 var remainder = this.containerWidth % this.columnWidth;
35689 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35690 // round if off by 1 pixel, otherwise use ceil
35691 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35692 colSpan = Math.min( colSpan, this.cols );
35694 // normally this should be '1' as we dont' currently allow multi width columns..
35696 var colGroup = this._getColGroup( colSpan );
35697 // get the minimum Y value from the columns
35698 var minimumY = Math.min.apply( Math, colGroup );
35699 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35701 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35703 // position the brick
35705 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35706 y: this.currentSize.y + minimumY + this.padHeight
35710 // apply setHeight to necessary columns
35711 var setHeight = minimumY + sz.height + this.padHeight;
35712 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35714 var setSpan = this.cols + 1 - colGroup.length;
35715 for ( var i = 0; i < setSpan; i++ ) {
35716 this.colYs[ shortColIndex + i ] = setHeight ;
35723 * @param {Number} colSpan - number of columns the element spans
35724 * @returns {Array} colGroup
35726 _getColGroup : function( colSpan )
35728 if ( colSpan < 2 ) {
35729 // if brick spans only one column, use all the column Ys
35734 // how many different places could this brick fit horizontally
35735 var groupCount = this.cols + 1 - colSpan;
35736 // for each group potential horizontal position
35737 for ( var i = 0; i < groupCount; i++ ) {
35738 // make an array of colY values for that one group
35739 var groupColYs = this.colYs.slice( i, i + colSpan );
35740 // and get the max value of the array
35741 colGroup[i] = Math.max.apply( Math, groupColYs );
35746 _manageStamp : function( stamp )
35748 var stampSize = stamp.getSize();
35749 var offset = stamp.getBox();
35750 // get the columns that this stamp affects
35751 var firstX = this.isOriginLeft ? offset.x : offset.right;
35752 var lastX = firstX + stampSize.width;
35753 var firstCol = Math.floor( firstX / this.columnWidth );
35754 firstCol = Math.max( 0, firstCol );
35756 var lastCol = Math.floor( lastX / this.columnWidth );
35757 // lastCol should not go over if multiple of columnWidth #425
35758 lastCol -= lastX % this.columnWidth ? 0 : 1;
35759 lastCol = Math.min( this.cols - 1, lastCol );
35761 // set colYs to bottom of the stamp
35762 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35765 for ( var i = firstCol; i <= lastCol; i++ ) {
35766 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35771 _getContainerSize : function()
35773 this.maxY = Math.max.apply( Math, this.colYs );
35778 if ( this.isFitWidth ) {
35779 size.width = this._getContainerFitWidth();
35785 _getContainerFitWidth : function()
35787 var unusedCols = 0;
35788 // count unused columns
35791 if ( this.colYs[i] !== 0 ) {
35796 // fit container to columns that have been used
35797 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35800 needsResizeLayout : function()
35802 var previousWidth = this.containerWidth;
35803 this.getContainerWidth();
35804 return previousWidth !== this.containerWidth;
35819 * @class Roo.bootstrap.MasonryBrick
35820 * @extends Roo.bootstrap.Component
35821 * Bootstrap MasonryBrick class
35824 * Create a new MasonryBrick
35825 * @param {Object} config The config object
35828 Roo.bootstrap.MasonryBrick = function(config){
35830 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35832 Roo.bootstrap.MasonryBrick.register(this);
35838 * When a MasonryBrick is clcik
35839 * @param {Roo.bootstrap.MasonryBrick} this
35840 * @param {Roo.EventObject} e
35846 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35849 * @cfg {String} title
35853 * @cfg {String} html
35857 * @cfg {String} bgimage
35861 * @cfg {String} videourl
35865 * @cfg {String} cls
35869 * @cfg {String} href
35873 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35878 * @cfg {String} placetitle (center|bottom)
35883 * @cfg {Boolean} isFitContainer defalut true
35885 isFitContainer : true,
35888 * @cfg {Boolean} preventDefault defalut false
35890 preventDefault : false,
35893 * @cfg {Boolean} inverse defalut false
35895 maskInverse : false,
35897 getAutoCreate : function()
35899 if(!this.isFitContainer){
35900 return this.getSplitAutoCreate();
35903 var cls = 'masonry-brick masonry-brick-full';
35905 if(this.href.length){
35906 cls += ' masonry-brick-link';
35909 if(this.bgimage.length){
35910 cls += ' masonry-brick-image';
35913 if(this.maskInverse){
35914 cls += ' mask-inverse';
35917 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35918 cls += ' enable-mask';
35922 cls += ' masonry-' + this.size + '-brick';
35925 if(this.placetitle.length){
35927 switch (this.placetitle) {
35929 cls += ' masonry-center-title';
35932 cls += ' masonry-bottom-title';
35939 if(!this.html.length && !this.bgimage.length){
35940 cls += ' masonry-center-title';
35943 if(!this.html.length && this.bgimage.length){
35944 cls += ' masonry-bottom-title';
35949 cls += ' ' + this.cls;
35953 tag: (this.href.length) ? 'a' : 'div',
35958 cls: 'masonry-brick-mask'
35962 cls: 'masonry-brick-paragraph',
35968 if(this.href.length){
35969 cfg.href = this.href;
35972 var cn = cfg.cn[1].cn;
35974 if(this.title.length){
35977 cls: 'masonry-brick-title',
35982 if(this.html.length){
35985 cls: 'masonry-brick-text',
35990 if (!this.title.length && !this.html.length) {
35991 cfg.cn[1].cls += ' hide';
35994 if(this.bgimage.length){
35997 cls: 'masonry-brick-image-view',
36002 if(this.videourl.length){
36003 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36004 // youtube support only?
36007 cls: 'masonry-brick-image-view',
36010 allowfullscreen : true
36018 getSplitAutoCreate : function()
36020 var cls = 'masonry-brick masonry-brick-split';
36022 if(this.href.length){
36023 cls += ' masonry-brick-link';
36026 if(this.bgimage.length){
36027 cls += ' masonry-brick-image';
36031 cls += ' masonry-' + this.size + '-brick';
36034 switch (this.placetitle) {
36036 cls += ' masonry-center-title';
36039 cls += ' masonry-bottom-title';
36042 if(!this.bgimage.length){
36043 cls += ' masonry-center-title';
36046 if(this.bgimage.length){
36047 cls += ' masonry-bottom-title';
36053 cls += ' ' + this.cls;
36057 tag: (this.href.length) ? 'a' : 'div',
36062 cls: 'masonry-brick-split-head',
36066 cls: 'masonry-brick-paragraph',
36073 cls: 'masonry-brick-split-body',
36079 if(this.href.length){
36080 cfg.href = this.href;
36083 if(this.title.length){
36084 cfg.cn[0].cn[0].cn.push({
36086 cls: 'masonry-brick-title',
36091 if(this.html.length){
36092 cfg.cn[1].cn.push({
36094 cls: 'masonry-brick-text',
36099 if(this.bgimage.length){
36100 cfg.cn[0].cn.push({
36102 cls: 'masonry-brick-image-view',
36107 if(this.videourl.length){
36108 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36109 // youtube support only?
36110 cfg.cn[0].cn.cn.push({
36112 cls: 'masonry-brick-image-view',
36115 allowfullscreen : true
36122 initEvents: function()
36124 switch (this.size) {
36157 this.el.on('touchstart', this.onTouchStart, this);
36158 this.el.on('touchmove', this.onTouchMove, this);
36159 this.el.on('touchend', this.onTouchEnd, this);
36160 this.el.on('contextmenu', this.onContextMenu, this);
36162 this.el.on('mouseenter' ,this.enter, this);
36163 this.el.on('mouseleave', this.leave, this);
36164 this.el.on('click', this.onClick, this);
36167 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36168 this.parent().bricks.push(this);
36173 onClick: function(e, el)
36175 var time = this.endTimer - this.startTimer;
36176 // Roo.log(e.preventDefault());
36179 e.preventDefault();
36184 if(!this.preventDefault){
36188 e.preventDefault();
36190 if (this.activeClass != '') {
36191 this.selectBrick();
36194 this.fireEvent('click', this, e);
36197 enter: function(e, el)
36199 e.preventDefault();
36201 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36205 if(this.bgimage.length && this.html.length){
36206 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36210 leave: function(e, el)
36212 e.preventDefault();
36214 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36218 if(this.bgimage.length && this.html.length){
36219 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36223 onTouchStart: function(e, el)
36225 // e.preventDefault();
36227 this.touchmoved = false;
36229 if(!this.isFitContainer){
36233 if(!this.bgimage.length || !this.html.length){
36237 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36239 this.timer = new Date().getTime();
36243 onTouchMove: function(e, el)
36245 this.touchmoved = true;
36248 onContextMenu : function(e,el)
36250 e.preventDefault();
36251 e.stopPropagation();
36255 onTouchEnd: function(e, el)
36257 // e.preventDefault();
36259 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36266 if(!this.bgimage.length || !this.html.length){
36268 if(this.href.length){
36269 window.location.href = this.href;
36275 if(!this.isFitContainer){
36279 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36281 window.location.href = this.href;
36284 //selection on single brick only
36285 selectBrick : function() {
36287 if (!this.parentId) {
36291 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36292 var index = m.selectedBrick.indexOf(this.id);
36295 m.selectedBrick.splice(index,1);
36296 this.el.removeClass(this.activeClass);
36300 for(var i = 0; i < m.selectedBrick.length; i++) {
36301 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36302 b.el.removeClass(b.activeClass);
36305 m.selectedBrick = [];
36307 m.selectedBrick.push(this.id);
36308 this.el.addClass(this.activeClass);
36312 isSelected : function(){
36313 return this.el.hasClass(this.activeClass);
36318 Roo.apply(Roo.bootstrap.MasonryBrick, {
36321 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36323 * register a Masonry Brick
36324 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36327 register : function(brick)
36329 //this.groups[brick.id] = brick;
36330 this.groups.add(brick.id, brick);
36333 * fetch a masonry brick based on the masonry brick ID
36334 * @param {string} the masonry brick to add
36335 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36338 get: function(brick_id)
36340 // if (typeof(this.groups[brick_id]) == 'undefined') {
36343 // return this.groups[brick_id] ;
36345 if(this.groups.key(brick_id)) {
36346 return this.groups.key(brick_id);
36364 * @class Roo.bootstrap.Brick
36365 * @extends Roo.bootstrap.Component
36366 * Bootstrap Brick class
36369 * Create a new Brick
36370 * @param {Object} config The config object
36373 Roo.bootstrap.Brick = function(config){
36374 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36380 * When a Brick is click
36381 * @param {Roo.bootstrap.Brick} this
36382 * @param {Roo.EventObject} e
36388 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36391 * @cfg {String} title
36395 * @cfg {String} html
36399 * @cfg {String} bgimage
36403 * @cfg {String} cls
36407 * @cfg {String} href
36411 * @cfg {String} video
36415 * @cfg {Boolean} square
36419 getAutoCreate : function()
36421 var cls = 'roo-brick';
36423 if(this.href.length){
36424 cls += ' roo-brick-link';
36427 if(this.bgimage.length){
36428 cls += ' roo-brick-image';
36431 if(!this.html.length && !this.bgimage.length){
36432 cls += ' roo-brick-center-title';
36435 if(!this.html.length && this.bgimage.length){
36436 cls += ' roo-brick-bottom-title';
36440 cls += ' ' + this.cls;
36444 tag: (this.href.length) ? 'a' : 'div',
36449 cls: 'roo-brick-paragraph',
36455 if(this.href.length){
36456 cfg.href = this.href;
36459 var cn = cfg.cn[0].cn;
36461 if(this.title.length){
36464 cls: 'roo-brick-title',
36469 if(this.html.length){
36472 cls: 'roo-brick-text',
36479 if(this.bgimage.length){
36482 cls: 'roo-brick-image-view',
36490 initEvents: function()
36492 if(this.title.length || this.html.length){
36493 this.el.on('mouseenter' ,this.enter, this);
36494 this.el.on('mouseleave', this.leave, this);
36497 Roo.EventManager.onWindowResize(this.resize, this);
36499 if(this.bgimage.length){
36500 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36501 this.imageEl.on('load', this.onImageLoad, this);
36508 onImageLoad : function()
36513 resize : function()
36515 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36517 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36519 if(this.bgimage.length){
36520 var image = this.el.select('.roo-brick-image-view', true).first();
36522 image.setWidth(paragraph.getWidth());
36525 image.setHeight(paragraph.getWidth());
36528 this.el.setHeight(image.getHeight());
36529 paragraph.setHeight(image.getHeight());
36535 enter: function(e, el)
36537 e.preventDefault();
36539 if(this.bgimage.length){
36540 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36541 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36545 leave: function(e, el)
36547 e.preventDefault();
36549 if(this.bgimage.length){
36550 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36551 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36566 * @class Roo.bootstrap.form.NumberField
36567 * @extends Roo.bootstrap.form.Input
36568 * Bootstrap NumberField class
36574 * Create a new NumberField
36575 * @param {Object} config The config object
36578 Roo.bootstrap.form.NumberField = function(config){
36579 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36582 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36585 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36587 allowDecimals : true,
36589 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36591 decimalSeparator : ".",
36593 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36595 decimalPrecision : 2,
36597 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36599 allowNegative : true,
36602 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36606 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36608 minValue : Number.NEGATIVE_INFINITY,
36610 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36612 maxValue : Number.MAX_VALUE,
36614 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36616 minText : "The minimum value for this field is {0}",
36618 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36620 maxText : "The maximum value for this field is {0}",
36622 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36623 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36625 nanText : "{0} is not a valid number",
36627 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36629 thousandsDelimiter : false,
36631 * @cfg {String} valueAlign alignment of value
36633 valueAlign : "left",
36635 getAutoCreate : function()
36637 var hiddenInput = {
36641 cls: 'hidden-number-input'
36645 hiddenInput.name = this.name;
36650 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36652 this.name = hiddenInput.name;
36654 if(cfg.cn.length > 0) {
36655 cfg.cn.push(hiddenInput);
36662 initEvents : function()
36664 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36666 var allowed = "0123456789";
36668 if(this.allowDecimals){
36669 allowed += this.decimalSeparator;
36672 if(this.allowNegative){
36676 if(this.thousandsDelimiter) {
36680 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36682 var keyPress = function(e){
36684 var k = e.getKey();
36686 var c = e.getCharCode();
36689 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36690 allowed.indexOf(String.fromCharCode(c)) === -1
36696 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36700 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36705 this.el.on("keypress", keyPress, this);
36708 validateValue : function(value)
36711 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36715 var num = this.parseValue(value);
36718 this.markInvalid(String.format(this.nanText, value));
36722 if(num < this.minValue){
36723 this.markInvalid(String.format(this.minText, this.minValue));
36727 if(num > this.maxValue){
36728 this.markInvalid(String.format(this.maxText, this.maxValue));
36735 getValue : function()
36737 var v = this.hiddenEl().getValue();
36739 return this.fixPrecision(this.parseValue(v));
36742 parseValue : function(value)
36744 if(this.thousandsDelimiter) {
36746 r = new RegExp(",", "g");
36747 value = value.replace(r, "");
36750 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36751 return isNaN(value) ? '' : value;
36754 fixPrecision : function(value)
36756 if(this.thousandsDelimiter) {
36758 r = new RegExp(",", "g");
36759 value = value.replace(r, "");
36762 var nan = isNaN(value);
36764 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36765 return nan ? '' : value;
36767 return parseFloat(value).toFixed(this.decimalPrecision);
36770 setValue : function(v)
36772 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36778 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36780 this.inputEl().dom.value = (v == '') ? '' :
36781 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36783 if(!this.allowZero && v === '0') {
36784 this.hiddenEl().dom.value = '';
36785 this.inputEl().dom.value = '';
36792 decimalPrecisionFcn : function(v)
36794 return Math.floor(v);
36797 beforeBlur : function()
36799 var v = this.parseValue(this.getRawValue());
36801 if(v || v === 0 || v === ''){
36806 hiddenEl : function()
36808 return this.el.select('input.hidden-number-input',true).first();
36820 * @class Roo.bootstrap.DocumentSlider
36821 * @extends Roo.bootstrap.Component
36822 * Bootstrap DocumentSlider class
36825 * Create a new DocumentViewer
36826 * @param {Object} config The config object
36829 Roo.bootstrap.DocumentSlider = function(config){
36830 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36837 * Fire after initEvent
36838 * @param {Roo.bootstrap.DocumentSlider} this
36843 * Fire after update
36844 * @param {Roo.bootstrap.DocumentSlider} this
36850 * @param {Roo.bootstrap.DocumentSlider} this
36856 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36862 getAutoCreate : function()
36866 cls : 'roo-document-slider',
36870 cls : 'roo-document-slider-header',
36874 cls : 'roo-document-slider-header-title'
36880 cls : 'roo-document-slider-body',
36884 cls : 'roo-document-slider-prev',
36888 cls : 'fa fa-chevron-left'
36894 cls : 'roo-document-slider-thumb',
36898 cls : 'roo-document-slider-image'
36904 cls : 'roo-document-slider-next',
36908 cls : 'fa fa-chevron-right'
36920 initEvents : function()
36922 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36923 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36925 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36926 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36928 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36929 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36931 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36932 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36934 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36935 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36937 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36938 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36940 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36941 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36943 this.thumbEl.on('click', this.onClick, this);
36945 this.prevIndicator.on('click', this.prev, this);
36947 this.nextIndicator.on('click', this.next, this);
36951 initial : function()
36953 if(this.files.length){
36954 this.indicator = 1;
36958 this.fireEvent('initial', this);
36961 update : function()
36963 this.imageEl.attr('src', this.files[this.indicator - 1]);
36965 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36967 this.prevIndicator.show();
36969 if(this.indicator == 1){
36970 this.prevIndicator.hide();
36973 this.nextIndicator.show();
36975 if(this.indicator == this.files.length){
36976 this.nextIndicator.hide();
36979 this.thumbEl.scrollTo('top');
36981 this.fireEvent('update', this);
36984 onClick : function(e)
36986 e.preventDefault();
36988 this.fireEvent('click', this);
36993 e.preventDefault();
36995 this.indicator = Math.max(1, this.indicator - 1);
37002 e.preventDefault();
37004 this.indicator = Math.min(this.files.length, this.indicator + 1);
37018 * @class Roo.bootstrap.form.RadioSet
37019 * @extends Roo.bootstrap.form.Input
37020 * @children Roo.bootstrap.form.Radio
37021 * Bootstrap RadioSet class
37022 * @cfg {String} indicatorpos (left|right) default left
37023 * @cfg {Boolean} inline (true|false) inline the element (default true)
37024 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37026 * Create a new RadioSet
37027 * @param {Object} config The config object
37030 Roo.bootstrap.form.RadioSet = function(config){
37032 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37036 Roo.bootstrap.form.RadioSet.register(this);
37041 * Fires when the element is checked or unchecked.
37042 * @param {Roo.bootstrap.form.RadioSet} this This radio
37043 * @param {Roo.bootstrap.form.Radio} item The checked item
37048 * Fires when the element is click.
37049 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37050 * @param {Roo.bootstrap.form.Radio} item The checked item
37051 * @param {Roo.EventObject} e The event object
37058 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37066 indicatorpos : 'left',
37068 getAutoCreate : function()
37072 cls : 'roo-radio-set-label',
37076 html : this.fieldLabel
37080 if (Roo.bootstrap.version == 3) {
37083 if(this.indicatorpos == 'left'){
37086 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37087 tooltip : 'This field is required'
37092 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37093 tooltip : 'This field is required'
37099 cls : 'roo-radio-set-items'
37102 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37104 if (align === 'left' && this.fieldLabel.length) {
37107 cls : "roo-radio-set-right",
37113 if(this.labelWidth > 12){
37114 label.style = "width: " + this.labelWidth + 'px';
37117 if(this.labelWidth < 13 && this.labelmd == 0){
37118 this.labelmd = this.labelWidth;
37121 if(this.labellg > 0){
37122 label.cls += ' col-lg-' + this.labellg;
37123 items.cls += ' col-lg-' + (12 - this.labellg);
37126 if(this.labelmd > 0){
37127 label.cls += ' col-md-' + this.labelmd;
37128 items.cls += ' col-md-' + (12 - this.labelmd);
37131 if(this.labelsm > 0){
37132 label.cls += ' col-sm-' + this.labelsm;
37133 items.cls += ' col-sm-' + (12 - this.labelsm);
37136 if(this.labelxs > 0){
37137 label.cls += ' col-xs-' + this.labelxs;
37138 items.cls += ' col-xs-' + (12 - this.labelxs);
37144 cls : 'roo-radio-set',
37148 cls : 'roo-radio-set-input',
37151 value : this.value ? this.value : ''
37158 if(this.weight.length){
37159 cfg.cls += ' roo-radio-' + this.weight;
37163 cfg.cls += ' roo-radio-set-inline';
37167 ['xs','sm','md','lg'].map(function(size){
37168 if (settings[size]) {
37169 cfg.cls += ' col-' + size + '-' + settings[size];
37177 initEvents : function()
37179 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37180 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37182 if(!this.fieldLabel.length){
37183 this.labelEl.hide();
37186 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37187 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37189 this.indicator = this.indicatorEl();
37191 if(this.indicator){
37192 this.indicator.addClass('invisible');
37195 this.originalValue = this.getValue();
37199 inputEl: function ()
37201 return this.el.select('.roo-radio-set-input', true).first();
37204 getChildContainer : function()
37206 return this.itemsEl;
37209 register : function(item)
37211 this.radioes.push(item);
37215 validate : function()
37217 if(this.getVisibilityEl().hasClass('hidden')){
37223 Roo.each(this.radioes, function(i){
37232 if(this.allowBlank) {
37236 if(this.disabled || valid){
37241 this.markInvalid();
37246 markValid : function()
37248 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37249 this.indicatorEl().removeClass('visible');
37250 this.indicatorEl().addClass('invisible');
37254 if (Roo.bootstrap.version == 3) {
37255 this.el.removeClass([this.invalidClass, this.validClass]);
37256 this.el.addClass(this.validClass);
37258 this.el.removeClass(['is-invalid','is-valid']);
37259 this.el.addClass(['is-valid']);
37261 this.fireEvent('valid', this);
37264 markInvalid : function(msg)
37266 if(this.allowBlank || this.disabled){
37270 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37271 this.indicatorEl().removeClass('invisible');
37272 this.indicatorEl().addClass('visible');
37274 if (Roo.bootstrap.version == 3) {
37275 this.el.removeClass([this.invalidClass, this.validClass]);
37276 this.el.addClass(this.invalidClass);
37278 this.el.removeClass(['is-invalid','is-valid']);
37279 this.el.addClass(['is-invalid']);
37282 this.fireEvent('invalid', this, msg);
37286 setValue : function(v, suppressEvent)
37288 if(this.value === v){
37295 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37298 Roo.each(this.radioes, function(i){
37300 i.el.removeClass('checked');
37303 Roo.each(this.radioes, function(i){
37305 if(i.value === v || i.value.toString() === v.toString()){
37307 i.el.addClass('checked');
37309 if(suppressEvent !== true){
37310 this.fireEvent('check', this, i);
37321 clearInvalid : function(){
37323 if(!this.el || this.preventMark){
37327 this.el.removeClass([this.invalidClass]);
37329 this.fireEvent('valid', this);
37334 Roo.apply(Roo.bootstrap.form.RadioSet, {
37338 register : function(set)
37340 this.groups[set.name] = set;
37343 get: function(name)
37345 if (typeof(this.groups[name]) == 'undefined') {
37349 return this.groups[name] ;
37355 * Ext JS Library 1.1.1
37356 * Copyright(c) 2006-2007, Ext JS, LLC.
37358 * Originally Released Under LGPL - original licence link has changed is not relivant.
37361 * <script type="text/javascript">
37366 * @class Roo.bootstrap.SplitBar
37367 * @extends Roo.util.Observable
37368 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37372 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37373 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37374 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37375 split.minSize = 100;
37376 split.maxSize = 600;
37377 split.animate = true;
37378 split.on('moved', splitterMoved);
37381 * Create a new SplitBar
37382 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37383 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37384 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37385 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37386 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37387 position of the SplitBar).
37389 Roo.bootstrap.SplitBar = function(cfg){
37394 // dragElement : elm
37395 // resizingElement: el,
37397 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37398 // placement : Roo.bootstrap.SplitBar.LEFT ,
37399 // existingProxy ???
37402 this.el = Roo.get(cfg.dragElement, true);
37403 this.el.dom.unselectable = "on";
37405 this.resizingEl = Roo.get(cfg.resizingElement, true);
37409 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37410 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37413 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37416 * The minimum size of the resizing element. (Defaults to 0)
37422 * The maximum size of the resizing element. (Defaults to 2000)
37425 this.maxSize = 2000;
37428 * Whether to animate the transition to the new size
37431 this.animate = false;
37434 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37437 this.useShim = false;
37442 if(!cfg.existingProxy){
37444 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37446 this.proxy = Roo.get(cfg.existingProxy).dom;
37449 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37452 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37455 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37458 this.dragSpecs = {};
37461 * @private The adapter to use to positon and resize elements
37463 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37464 this.adapter.init(this);
37466 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37468 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37469 this.el.addClass("roo-splitbar-h");
37472 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37473 this.el.addClass("roo-splitbar-v");
37479 * Fires when the splitter is moved (alias for {@link #event-moved})
37480 * @param {Roo.bootstrap.SplitBar} this
37481 * @param {Number} newSize the new width or height
37486 * Fires when the splitter is moved
37487 * @param {Roo.bootstrap.SplitBar} this
37488 * @param {Number} newSize the new width or height
37492 * @event beforeresize
37493 * Fires before the splitter is dragged
37494 * @param {Roo.bootstrap.SplitBar} this
37496 "beforeresize" : true,
37498 "beforeapply" : true
37501 Roo.util.Observable.call(this);
37504 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37505 onStartProxyDrag : function(x, y){
37506 this.fireEvent("beforeresize", this);
37508 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37510 o.enableDisplayMode("block");
37511 // all splitbars share the same overlay
37512 Roo.bootstrap.SplitBar.prototype.overlay = o;
37514 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37515 this.overlay.show();
37516 Roo.get(this.proxy).setDisplayed("block");
37517 var size = this.adapter.getElementSize(this);
37518 this.activeMinSize = this.getMinimumSize();;
37519 this.activeMaxSize = this.getMaximumSize();;
37520 var c1 = size - this.activeMinSize;
37521 var c2 = Math.max(this.activeMaxSize - size, 0);
37522 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37523 this.dd.resetConstraints();
37524 this.dd.setXConstraint(
37525 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37526 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37528 this.dd.setYConstraint(0, 0);
37530 this.dd.resetConstraints();
37531 this.dd.setXConstraint(0, 0);
37532 this.dd.setYConstraint(
37533 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37534 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37537 this.dragSpecs.startSize = size;
37538 this.dragSpecs.startPoint = [x, y];
37539 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37543 * @private Called after the drag operation by the DDProxy
37545 onEndProxyDrag : function(e){
37546 Roo.get(this.proxy).setDisplayed(false);
37547 var endPoint = Roo.lib.Event.getXY(e);
37549 this.overlay.hide();
37552 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37553 newSize = this.dragSpecs.startSize +
37554 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37555 endPoint[0] - this.dragSpecs.startPoint[0] :
37556 this.dragSpecs.startPoint[0] - endPoint[0]
37559 newSize = this.dragSpecs.startSize +
37560 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37561 endPoint[1] - this.dragSpecs.startPoint[1] :
37562 this.dragSpecs.startPoint[1] - endPoint[1]
37565 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37566 if(newSize != this.dragSpecs.startSize){
37567 if(this.fireEvent('beforeapply', this, newSize) !== false){
37568 this.adapter.setElementSize(this, newSize);
37569 this.fireEvent("moved", this, newSize);
37570 this.fireEvent("resize", this, newSize);
37576 * Get the adapter this SplitBar uses
37577 * @return The adapter object
37579 getAdapter : function(){
37580 return this.adapter;
37584 * Set the adapter this SplitBar uses
37585 * @param {Object} adapter A SplitBar adapter object
37587 setAdapter : function(adapter){
37588 this.adapter = adapter;
37589 this.adapter.init(this);
37593 * Gets the minimum size for the resizing element
37594 * @return {Number} The minimum size
37596 getMinimumSize : function(){
37597 return this.minSize;
37601 * Sets the minimum size for the resizing element
37602 * @param {Number} minSize The minimum size
37604 setMinimumSize : function(minSize){
37605 this.minSize = minSize;
37609 * Gets the maximum size for the resizing element
37610 * @return {Number} The maximum size
37612 getMaximumSize : function(){
37613 return this.maxSize;
37617 * Sets the maximum size for the resizing element
37618 * @param {Number} maxSize The maximum size
37620 setMaximumSize : function(maxSize){
37621 this.maxSize = maxSize;
37625 * Sets the initialize size for the resizing element
37626 * @param {Number} size The initial size
37628 setCurrentSize : function(size){
37629 var oldAnimate = this.animate;
37630 this.animate = false;
37631 this.adapter.setElementSize(this, size);
37632 this.animate = oldAnimate;
37636 * Destroy this splitbar.
37637 * @param {Boolean} removeEl True to remove the element
37639 destroy : function(removeEl){
37641 this.shim.remove();
37644 this.proxy.parentNode.removeChild(this.proxy);
37652 * @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.
37654 Roo.bootstrap.SplitBar.createProxy = function(dir){
37655 var proxy = new Roo.Element(document.createElement("div"));
37656 proxy.unselectable();
37657 var cls = 'roo-splitbar-proxy';
37658 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37659 document.body.appendChild(proxy.dom);
37664 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37665 * Default Adapter. It assumes the splitter and resizing element are not positioned
37666 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37668 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37671 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37672 // do nothing for now
37673 init : function(s){
37677 * Called before drag operations to get the current size of the resizing element.
37678 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37680 getElementSize : function(s){
37681 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37682 return s.resizingEl.getWidth();
37684 return s.resizingEl.getHeight();
37689 * Called after drag operations to set the size of the resizing element.
37690 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37691 * @param {Number} newSize The new size to set
37692 * @param {Function} onComplete A function to be invoked when resizing is complete
37694 setElementSize : function(s, newSize, onComplete){
37695 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37697 s.resizingEl.setWidth(newSize);
37699 onComplete(s, newSize);
37702 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37707 s.resizingEl.setHeight(newSize);
37709 onComplete(s, newSize);
37712 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37719 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37720 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37721 * Adapter that moves the splitter element to align with the resized sizing element.
37722 * Used with an absolute positioned SplitBar.
37723 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37724 * document.body, make sure you assign an id to the body element.
37726 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37727 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37728 this.container = Roo.get(container);
37731 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37732 init : function(s){
37733 this.basic.init(s);
37736 getElementSize : function(s){
37737 return this.basic.getElementSize(s);
37740 setElementSize : function(s, newSize, onComplete){
37741 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37744 moveSplitter : function(s){
37745 var yes = Roo.bootstrap.SplitBar;
37746 switch(s.placement){
37748 s.el.setX(s.resizingEl.getRight());
37751 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37754 s.el.setY(s.resizingEl.getBottom());
37757 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37764 * Orientation constant - Create a vertical SplitBar
37768 Roo.bootstrap.SplitBar.VERTICAL = 1;
37771 * Orientation constant - Create a horizontal SplitBar
37775 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37778 * Placement constant - The resizing element is to the left of the splitter element
37782 Roo.bootstrap.SplitBar.LEFT = 1;
37785 * Placement constant - The resizing element is to the right of the splitter element
37789 Roo.bootstrap.SplitBar.RIGHT = 2;
37792 * Placement constant - The resizing element is positioned above the splitter element
37796 Roo.bootstrap.SplitBar.TOP = 3;
37799 * Placement constant - The resizing element is positioned under splitter element
37803 Roo.bootstrap.SplitBar.BOTTOM = 4;
37806 * Ext JS Library 1.1.1
37807 * Copyright(c) 2006-2007, Ext JS, LLC.
37809 * Originally Released Under LGPL - original licence link has changed is not relivant.
37812 * <script type="text/javascript">
37816 * @class Roo.bootstrap.layout.Manager
37817 * @extends Roo.bootstrap.Component
37818 * Base class for layout managers.
37820 Roo.bootstrap.layout.Manager = function(config)
37822 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37828 /** false to disable window resize monitoring @type Boolean */
37829 this.monitorWindowResize = true;
37834 * Fires when a layout is performed.
37835 * @param {Roo.LayoutManager} this
37839 * @event regionresized
37840 * Fires when the user resizes a region.
37841 * @param {Roo.LayoutRegion} region The resized region
37842 * @param {Number} newSize The new size (width for east/west, height for north/south)
37844 "regionresized" : true,
37846 * @event regioncollapsed
37847 * Fires when a region is collapsed.
37848 * @param {Roo.LayoutRegion} region The collapsed region
37850 "regioncollapsed" : true,
37852 * @event regionexpanded
37853 * Fires when a region is expanded.
37854 * @param {Roo.LayoutRegion} region The expanded region
37856 "regionexpanded" : true
37858 this.updating = false;
37861 this.el = Roo.get(config.el);
37867 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37872 monitorWindowResize : true,
37878 onRender : function(ct, position)
37881 this.el = Roo.get(ct);
37884 //this.fireEvent('render',this);
37888 initEvents: function()
37892 // ie scrollbar fix
37893 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37894 document.body.scroll = "no";
37895 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37896 this.el.position('relative');
37898 this.id = this.el.id;
37899 this.el.addClass("roo-layout-container");
37900 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37901 if(this.el.dom != document.body ) {
37902 this.el.on('resize', this.layout,this);
37903 this.el.on('show', this.layout,this);
37909 * Returns true if this layout is currently being updated
37910 * @return {Boolean}
37912 isUpdating : function(){
37913 return this.updating;
37917 * Suspend the LayoutManager from doing auto-layouts while
37918 * making multiple add or remove calls
37920 beginUpdate : function(){
37921 this.updating = true;
37925 * Restore auto-layouts and optionally disable the manager from performing a layout
37926 * @param {Boolean} noLayout true to disable a layout update
37928 endUpdate : function(noLayout){
37929 this.updating = false;
37935 layout: function(){
37939 onRegionResized : function(region, newSize){
37940 this.fireEvent("regionresized", region, newSize);
37944 onRegionCollapsed : function(region){
37945 this.fireEvent("regioncollapsed", region);
37948 onRegionExpanded : function(region){
37949 this.fireEvent("regionexpanded", region);
37953 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37954 * performs box-model adjustments.
37955 * @return {Object} The size as an object {width: (the width), height: (the height)}
37957 getViewSize : function()
37960 if(this.el.dom != document.body){
37961 size = this.el.getSize();
37963 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37965 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37966 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37971 * Returns the Element this layout is bound to.
37972 * @return {Roo.Element}
37974 getEl : function(){
37979 * Returns the specified region.
37980 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37981 * @return {Roo.LayoutRegion}
37983 getRegion : function(target){
37984 return this.regions[target.toLowerCase()];
37987 onWindowResize : function(){
37988 if(this.monitorWindowResize){
37995 * Ext JS Library 1.1.1
37996 * Copyright(c) 2006-2007, Ext JS, LLC.
37998 * Originally Released Under LGPL - original licence link has changed is not relivant.
38001 * <script type="text/javascript">
38004 * @class Roo.bootstrap.layout.Border
38005 * @extends Roo.bootstrap.layout.Manager
38007 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38008 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38009 * please see: examples/bootstrap/nested.html<br><br>
38011 <b>The container the layout is rendered into can be either the body element or any other element.
38012 If it is not the body element, the container needs to either be an absolute positioned element,
38013 or you will need to add "position:relative" to the css of the container. You will also need to specify
38014 the container size if it is not the body element.</b>
38017 * Create a new Border
38018 * @param {Object} config Configuration options
38020 Roo.bootstrap.layout.Border = function(config){
38021 config = config || {};
38022 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38026 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38027 if(config[region]){
38028 config[region].region = region;
38029 this.addRegion(config[region]);
38035 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38037 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38040 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38043 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38046 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38049 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38052 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38058 parent : false, // this might point to a 'nest' or a ???
38061 * Creates and adds a new region if it doesn't already exist.
38062 * @param {String} target The target region key (north, south, east, west or center).
38063 * @param {Object} config The regions config object
38064 * @return {BorderLayoutRegion} The new region
38066 addRegion : function(config)
38068 if(!this.regions[config.region]){
38069 var r = this.factory(config);
38070 this.bindRegion(r);
38072 return this.regions[config.region];
38076 bindRegion : function(r){
38077 this.regions[r.config.region] = r;
38079 r.on("visibilitychange", this.layout, this);
38080 r.on("paneladded", this.layout, this);
38081 r.on("panelremoved", this.layout, this);
38082 r.on("invalidated", this.layout, this);
38083 r.on("resized", this.onRegionResized, this);
38084 r.on("collapsed", this.onRegionCollapsed, this);
38085 r.on("expanded", this.onRegionExpanded, this);
38089 * Performs a layout update.
38091 layout : function()
38093 if(this.updating) {
38097 // render all the rebions if they have not been done alreayd?
38098 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38099 if(this.regions[region] && !this.regions[region].bodyEl){
38100 this.regions[region].onRender(this.el)
38104 var size = this.getViewSize();
38105 var w = size.width;
38106 var h = size.height;
38111 //var x = 0, y = 0;
38113 var rs = this.regions;
38114 var north = rs["north"];
38115 var south = rs["south"];
38116 var west = rs["west"];
38117 var east = rs["east"];
38118 var center = rs["center"];
38119 //if(this.hideOnLayout){ // not supported anymore
38120 //c.el.setStyle("display", "none");
38122 if(north && north.isVisible()){
38123 var b = north.getBox();
38124 var m = north.getMargins();
38125 b.width = w - (m.left+m.right);
38128 centerY = b.height + b.y + m.bottom;
38129 centerH -= centerY;
38130 north.updateBox(this.safeBox(b));
38132 if(south && south.isVisible()){
38133 var b = south.getBox();
38134 var m = south.getMargins();
38135 b.width = w - (m.left+m.right);
38137 var totalHeight = (b.height + m.top + m.bottom);
38138 b.y = h - totalHeight + m.top;
38139 centerH -= totalHeight;
38140 south.updateBox(this.safeBox(b));
38142 if(west && west.isVisible()){
38143 var b = west.getBox();
38144 var m = west.getMargins();
38145 b.height = centerH - (m.top+m.bottom);
38147 b.y = centerY + m.top;
38148 var totalWidth = (b.width + m.left + m.right);
38149 centerX += totalWidth;
38150 centerW -= totalWidth;
38151 west.updateBox(this.safeBox(b));
38153 if(east && east.isVisible()){
38154 var b = east.getBox();
38155 var m = east.getMargins();
38156 b.height = centerH - (m.top+m.bottom);
38157 var totalWidth = (b.width + m.left + m.right);
38158 b.x = w - totalWidth + m.left;
38159 b.y = centerY + m.top;
38160 centerW -= totalWidth;
38161 east.updateBox(this.safeBox(b));
38164 var m = center.getMargins();
38166 x: centerX + m.left,
38167 y: centerY + m.top,
38168 width: centerW - (m.left+m.right),
38169 height: centerH - (m.top+m.bottom)
38171 //if(this.hideOnLayout){
38172 //center.el.setStyle("display", "block");
38174 center.updateBox(this.safeBox(centerBox));
38177 this.fireEvent("layout", this);
38181 safeBox : function(box){
38182 box.width = Math.max(0, box.width);
38183 box.height = Math.max(0, box.height);
38188 * Adds a ContentPanel (or subclass) to this layout.
38189 * @param {String} target The target region key (north, south, east, west or center).
38190 * @param {Roo.ContentPanel} panel The panel to add
38191 * @return {Roo.ContentPanel} The added panel
38193 add : function(target, panel){
38195 target = target.toLowerCase();
38196 return this.regions[target].add(panel);
38200 * Remove a ContentPanel (or subclass) to this layout.
38201 * @param {String} target The target region key (north, south, east, west or center).
38202 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38203 * @return {Roo.ContentPanel} The removed panel
38205 remove : function(target, panel){
38206 target = target.toLowerCase();
38207 return this.regions[target].remove(panel);
38211 * Searches all regions for a panel with the specified id
38212 * @param {String} panelId
38213 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38215 findPanel : function(panelId){
38216 var rs = this.regions;
38217 for(var target in rs){
38218 if(typeof rs[target] != "function"){
38219 var p = rs[target].getPanel(panelId);
38229 * Searches all regions for a panel with the specified id and activates (shows) it.
38230 * @param {String/ContentPanel} panelId The panels id or the panel itself
38231 * @return {Roo.ContentPanel} The shown panel or null
38233 showPanel : function(panelId) {
38234 var rs = this.regions;
38235 for(var target in rs){
38236 var r = rs[target];
38237 if(typeof r != "function"){
38238 if(r.hasPanel(panelId)){
38239 return r.showPanel(panelId);
38247 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38248 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38251 restoreState : function(provider){
38253 provider = Roo.state.Manager;
38255 var sm = new Roo.LayoutStateManager();
38256 sm.init(this, provider);
38262 * Adds a xtype elements to the layout.
38266 xtype : 'ContentPanel',
38273 xtype : 'NestedLayoutPanel',
38279 items : [ ... list of content panels or nested layout panels.. ]
38283 * @param {Object} cfg Xtype definition of item to add.
38285 addxtype : function(cfg)
38287 // basically accepts a pannel...
38288 // can accept a layout region..!?!?
38289 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38292 // theory? children can only be panels??
38294 //if (!cfg.xtype.match(/Panel$/)) {
38299 if (typeof(cfg.region) == 'undefined') {
38300 Roo.log("Failed to add Panel, region was not set");
38304 var region = cfg.region;
38310 xitems = cfg.items;
38315 if ( region == 'center') {
38316 Roo.log("Center: " + cfg.title);
38322 case 'Content': // ContentPanel (el, cfg)
38323 case 'Scroll': // ContentPanel (el, cfg)
38325 cfg.autoCreate = cfg.autoCreate || true;
38326 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38328 // var el = this.el.createChild();
38329 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38332 this.add(region, ret);
38336 case 'TreePanel': // our new panel!
38337 cfg.el = this.el.createChild();
38338 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38339 this.add(region, ret);
38344 // create a new Layout (which is a Border Layout...
38346 var clayout = cfg.layout;
38347 clayout.el = this.el.createChild();
38348 clayout.items = clayout.items || [];
38352 // replace this exitems with the clayout ones..
38353 xitems = clayout.items;
38355 // force background off if it's in center...
38356 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38357 cfg.background = false;
38359 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38362 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38363 //console.log('adding nested layout panel ' + cfg.toSource());
38364 this.add(region, ret);
38365 nb = {}; /// find first...
38370 // needs grid and region
38372 //var el = this.getRegion(region).el.createChild();
38374 *var el = this.el.createChild();
38375 // create the grid first...
38376 cfg.grid.container = el;
38377 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38380 if (region == 'center' && this.active ) {
38381 cfg.background = false;
38384 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38386 this.add(region, ret);
38388 if (cfg.background) {
38389 // render grid on panel activation (if panel background)
38390 ret.on('activate', function(gp) {
38391 if (!gp.grid.rendered) {
38392 // gp.grid.render(el);
38396 // cfg.grid.render(el);
38402 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38403 // it was the old xcomponent building that caused this before.
38404 // espeically if border is the top element in the tree.
38414 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38416 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38417 this.add(region, ret);
38421 throw "Can not add '" + cfg.xtype + "' to Border";
38427 this.beginUpdate();
38431 Roo.each(xitems, function(i) {
38432 region = nb && i.region ? i.region : false;
38434 var add = ret.addxtype(i);
38437 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38438 if (!i.background) {
38439 abn[region] = nb[region] ;
38446 // make the last non-background panel active..
38447 //if (nb) { Roo.log(abn); }
38450 for(var r in abn) {
38451 region = this.getRegion(r);
38453 // tried using nb[r], but it does not work..
38455 region.showPanel(abn[r]);
38466 factory : function(cfg)
38469 var validRegions = Roo.bootstrap.layout.Border.regions;
38471 var target = cfg.region;
38474 var r = Roo.bootstrap.layout;
38478 return new r.North(cfg);
38480 return new r.South(cfg);
38482 return new r.East(cfg);
38484 return new r.West(cfg);
38486 return new r.Center(cfg);
38488 throw 'Layout region "'+target+'" not supported.';
38495 * Ext JS Library 1.1.1
38496 * Copyright(c) 2006-2007, Ext JS, LLC.
38498 * Originally Released Under LGPL - original licence link has changed is not relivant.
38501 * <script type="text/javascript">
38505 * @class Roo.bootstrap.layout.Basic
38506 * @extends Roo.util.Observable
38507 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38508 * and does not have a titlebar, tabs or any other features. All it does is size and position
38509 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38510 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38511 * @cfg {string} region the region that it inhabits..
38512 * @cfg {bool} skipConfig skip config?
38516 Roo.bootstrap.layout.Basic = function(config){
38518 this.mgr = config.mgr;
38520 this.position = config.region;
38522 var skipConfig = config.skipConfig;
38526 * @scope Roo.BasicLayoutRegion
38530 * @event beforeremove
38531 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38532 * @param {Roo.LayoutRegion} this
38533 * @param {Roo.ContentPanel} panel The panel
38534 * @param {Object} e The cancel event object
38536 "beforeremove" : true,
38538 * @event invalidated
38539 * Fires when the layout for this region is changed.
38540 * @param {Roo.LayoutRegion} this
38542 "invalidated" : true,
38544 * @event visibilitychange
38545 * Fires when this region is shown or hidden
38546 * @param {Roo.LayoutRegion} this
38547 * @param {Boolean} visibility true or false
38549 "visibilitychange" : true,
38551 * @event paneladded
38552 * Fires when a panel is added.
38553 * @param {Roo.LayoutRegion} this
38554 * @param {Roo.ContentPanel} panel The panel
38556 "paneladded" : true,
38558 * @event panelremoved
38559 * Fires when a panel is removed.
38560 * @param {Roo.LayoutRegion} this
38561 * @param {Roo.ContentPanel} panel The panel
38563 "panelremoved" : true,
38565 * @event beforecollapse
38566 * Fires when this region before collapse.
38567 * @param {Roo.LayoutRegion} this
38569 "beforecollapse" : true,
38572 * Fires when this region is collapsed.
38573 * @param {Roo.LayoutRegion} this
38575 "collapsed" : true,
38578 * Fires when this region is expanded.
38579 * @param {Roo.LayoutRegion} this
38584 * Fires when this region is slid into view.
38585 * @param {Roo.LayoutRegion} this
38587 "slideshow" : true,
38590 * Fires when this region slides out of view.
38591 * @param {Roo.LayoutRegion} this
38593 "slidehide" : true,
38595 * @event panelactivated
38596 * Fires when a panel is activated.
38597 * @param {Roo.LayoutRegion} this
38598 * @param {Roo.ContentPanel} panel The activated panel
38600 "panelactivated" : true,
38603 * Fires when the user resizes this region.
38604 * @param {Roo.LayoutRegion} this
38605 * @param {Number} newSize The new size (width for east/west, height for north/south)
38609 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38610 this.panels = new Roo.util.MixedCollection();
38611 this.panels.getKey = this.getPanelId.createDelegate(this);
38613 this.activePanel = null;
38614 // ensure listeners are added...
38616 if (config.listeners || config.events) {
38617 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38618 listeners : config.listeners || {},
38619 events : config.events || {}
38623 if(skipConfig !== true){
38624 this.applyConfig(config);
38628 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38630 getPanelId : function(p){
38634 applyConfig : function(config){
38635 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38636 this.config = config;
38641 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38642 * the width, for horizontal (north, south) the height.
38643 * @param {Number} newSize The new width or height
38645 resizeTo : function(newSize){
38646 var el = this.el ? this.el :
38647 (this.activePanel ? this.activePanel.getEl() : null);
38649 switch(this.position){
38652 el.setWidth(newSize);
38653 this.fireEvent("resized", this, newSize);
38657 el.setHeight(newSize);
38658 this.fireEvent("resized", this, newSize);
38664 getBox : function(){
38665 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38668 getMargins : function(){
38669 return this.margins;
38672 updateBox : function(box){
38674 var el = this.activePanel.getEl();
38675 el.dom.style.left = box.x + "px";
38676 el.dom.style.top = box.y + "px";
38677 this.activePanel.setSize(box.width, box.height);
38681 * Returns the container element for this region.
38682 * @return {Roo.Element}
38684 getEl : function(){
38685 return this.activePanel;
38689 * Returns true if this region is currently visible.
38690 * @return {Boolean}
38692 isVisible : function(){
38693 return this.activePanel ? true : false;
38696 setActivePanel : function(panel){
38697 panel = this.getPanel(panel);
38698 if(this.activePanel && this.activePanel != panel){
38699 this.activePanel.setActiveState(false);
38700 this.activePanel.getEl().setLeftTop(-10000,-10000);
38702 this.activePanel = panel;
38703 panel.setActiveState(true);
38705 panel.setSize(this.box.width, this.box.height);
38707 this.fireEvent("panelactivated", this, panel);
38708 this.fireEvent("invalidated");
38712 * Show the specified panel.
38713 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38714 * @return {Roo.ContentPanel} The shown panel or null
38716 showPanel : function(panel){
38717 panel = this.getPanel(panel);
38719 this.setActivePanel(panel);
38725 * Get the active panel for this region.
38726 * @return {Roo.ContentPanel} The active panel or null
38728 getActivePanel : function(){
38729 return this.activePanel;
38733 * Add the passed ContentPanel(s)
38734 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38735 * @return {Roo.ContentPanel} The panel added (if only one was added)
38737 add : function(panel){
38738 if(arguments.length > 1){
38739 for(var i = 0, len = arguments.length; i < len; i++) {
38740 this.add(arguments[i]);
38744 if(this.hasPanel(panel)){
38745 this.showPanel(panel);
38748 var el = panel.getEl();
38749 if(el.dom.parentNode != this.mgr.el.dom){
38750 this.mgr.el.dom.appendChild(el.dom);
38752 if(panel.setRegion){
38753 panel.setRegion(this);
38755 this.panels.add(panel);
38756 el.setStyle("position", "absolute");
38757 if(!panel.background){
38758 this.setActivePanel(panel);
38759 if(this.config.initialSize && this.panels.getCount()==1){
38760 this.resizeTo(this.config.initialSize);
38763 this.fireEvent("paneladded", this, panel);
38768 * Returns true if the panel is in this region.
38769 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38770 * @return {Boolean}
38772 hasPanel : function(panel){
38773 if(typeof panel == "object"){ // must be panel obj
38774 panel = panel.getId();
38776 return this.getPanel(panel) ? true : false;
38780 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38781 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38782 * @param {Boolean} preservePanel Overrides the config preservePanel option
38783 * @return {Roo.ContentPanel} The panel that was removed
38785 remove : function(panel, preservePanel){
38786 panel = this.getPanel(panel);
38791 this.fireEvent("beforeremove", this, panel, e);
38792 if(e.cancel === true){
38795 var panelId = panel.getId();
38796 this.panels.removeKey(panelId);
38801 * Returns the panel specified or null if it's not in this region.
38802 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38803 * @return {Roo.ContentPanel}
38805 getPanel : function(id){
38806 if(typeof id == "object"){ // must be panel obj
38809 return this.panels.get(id);
38813 * Returns this regions position (north/south/east/west/center).
38816 getPosition: function(){
38817 return this.position;
38821 * Ext JS Library 1.1.1
38822 * Copyright(c) 2006-2007, Ext JS, LLC.
38824 * Originally Released Under LGPL - original licence link has changed is not relivant.
38827 * <script type="text/javascript">
38831 * @class Roo.bootstrap.layout.Region
38832 * @extends Roo.bootstrap.layout.Basic
38833 * This class represents a region in a layout manager.
38835 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38836 * @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})
38837 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38838 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38839 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38840 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38841 * @cfg {String} title The title for the region (overrides panel titles)
38842 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38843 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38844 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38845 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38846 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38847 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38848 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38849 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38850 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38851 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38853 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38854 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38855 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38856 * @cfg {Number} width For East/West panels
38857 * @cfg {Number} height For North/South panels
38858 * @cfg {Boolean} split To show the splitter
38859 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38861 * @cfg {string} cls Extra CSS classes to add to region
38863 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38864 * @cfg {string} region the region that it inhabits..
38867 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38868 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38870 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38871 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38872 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38874 Roo.bootstrap.layout.Region = function(config)
38876 this.applyConfig(config);
38878 var mgr = config.mgr;
38879 var pos = config.region;
38880 config.skipConfig = true;
38881 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38884 this.onRender(mgr.el);
38887 this.visible = true;
38888 this.collapsed = false;
38889 this.unrendered_panels = [];
38892 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38894 position: '', // set by wrapper (eg. north/south etc..)
38895 unrendered_panels : null, // unrendered panels.
38897 tabPosition : false,
38899 mgr: false, // points to 'Border'
38902 createBody : function(){
38903 /** This region's body element
38904 * @type Roo.Element */
38905 this.bodyEl = this.el.createChild({
38907 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38911 onRender: function(ctr, pos)
38913 var dh = Roo.DomHelper;
38914 /** This region's container element
38915 * @type Roo.Element */
38916 this.el = dh.append(ctr.dom, {
38918 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38920 /** This region's title element
38921 * @type Roo.Element */
38923 this.titleEl = dh.append(this.el.dom, {
38925 unselectable: "on",
38926 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38928 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38929 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38933 this.titleEl.enableDisplayMode();
38934 /** This region's title text element
38935 * @type HTMLElement */
38936 this.titleTextEl = this.titleEl.dom.firstChild;
38937 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38939 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38940 this.closeBtn.enableDisplayMode();
38941 this.closeBtn.on("click", this.closeClicked, this);
38942 this.closeBtn.hide();
38944 this.createBody(this.config);
38945 if(this.config.hideWhenEmpty){
38947 this.on("paneladded", this.validateVisibility, this);
38948 this.on("panelremoved", this.validateVisibility, this);
38950 if(this.autoScroll){
38951 this.bodyEl.setStyle("overflow", "auto");
38953 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38955 //if(c.titlebar !== false){
38956 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38957 this.titleEl.hide();
38959 this.titleEl.show();
38960 if(this.config.title){
38961 this.titleTextEl.innerHTML = this.config.title;
38965 if(this.config.collapsed){
38966 this.collapse(true);
38968 if(this.config.hidden){
38972 if (this.unrendered_panels && this.unrendered_panels.length) {
38973 for (var i =0;i< this.unrendered_panels.length; i++) {
38974 this.add(this.unrendered_panels[i]);
38976 this.unrendered_panels = null;
38982 applyConfig : function(c)
38985 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38986 var dh = Roo.DomHelper;
38987 if(c.titlebar !== false){
38988 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38989 this.collapseBtn.on("click", this.collapse, this);
38990 this.collapseBtn.enableDisplayMode();
38992 if(c.showPin === true || this.showPin){
38993 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38994 this.stickBtn.enableDisplayMode();
38995 this.stickBtn.on("click", this.expand, this);
38996 this.stickBtn.hide();
39001 /** This region's collapsed element
39002 * @type Roo.Element */
39005 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39006 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39009 if(c.floatable !== false){
39010 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39011 this.collapsedEl.on("click", this.collapseClick, this);
39014 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39015 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39016 id: "message", unselectable: "on", style:{"float":"left"}});
39017 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39019 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39020 this.expandBtn.on("click", this.expand, this);
39024 if(this.collapseBtn){
39025 this.collapseBtn.setVisible(c.collapsible == true);
39028 this.cmargins = c.cmargins || this.cmargins ||
39029 (this.position == "west" || this.position == "east" ?
39030 {top: 0, left: 2, right:2, bottom: 0} :
39031 {top: 2, left: 0, right:0, bottom: 2});
39033 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39036 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39038 this.autoScroll = c.autoScroll || false;
39043 this.duration = c.duration || .30;
39044 this.slideDuration = c.slideDuration || .45;
39049 * Returns true if this region is currently visible.
39050 * @return {Boolean}
39052 isVisible : function(){
39053 return this.visible;
39057 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39058 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39060 //setCollapsedTitle : function(title){
39061 // title = title || " ";
39062 // if(this.collapsedTitleTextEl){
39063 // this.collapsedTitleTextEl.innerHTML = title;
39067 getBox : function(){
39069 // if(!this.collapsed){
39070 b = this.el.getBox(false, true);
39072 // b = this.collapsedEl.getBox(false, true);
39077 getMargins : function(){
39078 return this.margins;
39079 //return this.collapsed ? this.cmargins : this.margins;
39082 highlight : function(){
39083 this.el.addClass("x-layout-panel-dragover");
39086 unhighlight : function(){
39087 this.el.removeClass("x-layout-panel-dragover");
39090 updateBox : function(box)
39092 if (!this.bodyEl) {
39093 return; // not rendered yet..
39097 if(!this.collapsed){
39098 this.el.dom.style.left = box.x + "px";
39099 this.el.dom.style.top = box.y + "px";
39100 this.updateBody(box.width, box.height);
39102 this.collapsedEl.dom.style.left = box.x + "px";
39103 this.collapsedEl.dom.style.top = box.y + "px";
39104 this.collapsedEl.setSize(box.width, box.height);
39107 this.tabs.autoSizeTabs();
39111 updateBody : function(w, h)
39114 this.el.setWidth(w);
39115 w -= this.el.getBorderWidth("rl");
39116 if(this.config.adjustments){
39117 w += this.config.adjustments[0];
39120 if(h !== null && h > 0){
39121 this.el.setHeight(h);
39122 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39123 h -= this.el.getBorderWidth("tb");
39124 if(this.config.adjustments){
39125 h += this.config.adjustments[1];
39127 this.bodyEl.setHeight(h);
39129 h = this.tabs.syncHeight(h);
39132 if(this.panelSize){
39133 w = w !== null ? w : this.panelSize.width;
39134 h = h !== null ? h : this.panelSize.height;
39136 if(this.activePanel){
39137 var el = this.activePanel.getEl();
39138 w = w !== null ? w : el.getWidth();
39139 h = h !== null ? h : el.getHeight();
39140 this.panelSize = {width: w, height: h};
39141 this.activePanel.setSize(w, h);
39143 if(Roo.isIE && this.tabs){
39144 this.tabs.el.repaint();
39149 * Returns the container element for this region.
39150 * @return {Roo.Element}
39152 getEl : function(){
39157 * Hides this region.
39160 //if(!this.collapsed){
39161 this.el.dom.style.left = "-2000px";
39164 // this.collapsedEl.dom.style.left = "-2000px";
39165 // this.collapsedEl.hide();
39167 this.visible = false;
39168 this.fireEvent("visibilitychange", this, false);
39172 * Shows this region if it was previously hidden.
39175 //if(!this.collapsed){
39178 // this.collapsedEl.show();
39180 this.visible = true;
39181 this.fireEvent("visibilitychange", this, true);
39184 closeClicked : function(){
39185 if(this.activePanel){
39186 this.remove(this.activePanel);
39190 collapseClick : function(e){
39192 e.stopPropagation();
39195 e.stopPropagation();
39201 * Collapses this region.
39202 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39205 collapse : function(skipAnim, skipCheck = false){
39206 if(this.collapsed) {
39210 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39212 this.collapsed = true;
39214 this.split.el.hide();
39216 if(this.config.animate && skipAnim !== true){
39217 this.fireEvent("invalidated", this);
39218 this.animateCollapse();
39220 this.el.setLocation(-20000,-20000);
39222 this.collapsedEl.show();
39223 this.fireEvent("collapsed", this);
39224 this.fireEvent("invalidated", this);
39230 animateCollapse : function(){
39235 * Expands this region if it was previously collapsed.
39236 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39237 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39240 expand : function(e, skipAnim){
39242 e.stopPropagation();
39244 if(!this.collapsed || this.el.hasActiveFx()) {
39248 this.afterSlideIn();
39251 this.collapsed = false;
39252 if(this.config.animate && skipAnim !== true){
39253 this.animateExpand();
39257 this.split.el.show();
39259 this.collapsedEl.setLocation(-2000,-2000);
39260 this.collapsedEl.hide();
39261 this.fireEvent("invalidated", this);
39262 this.fireEvent("expanded", this);
39266 animateExpand : function(){
39270 initTabs : function()
39272 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39274 var ts = new Roo.bootstrap.panel.Tabs({
39275 el: this.bodyEl.dom,
39277 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39278 disableTooltips: this.config.disableTabTips,
39279 toolbar : this.config.toolbar
39282 if(this.config.hideTabs){
39283 ts.stripWrap.setDisplayed(false);
39286 ts.resizeTabs = this.config.resizeTabs === true;
39287 ts.minTabWidth = this.config.minTabWidth || 40;
39288 ts.maxTabWidth = this.config.maxTabWidth || 250;
39289 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39290 ts.monitorResize = false;
39291 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39292 ts.bodyEl.addClass('roo-layout-tabs-body');
39293 this.panels.each(this.initPanelAsTab, this);
39296 initPanelAsTab : function(panel){
39297 var ti = this.tabs.addTab(
39301 this.config.closeOnTab && panel.isClosable(),
39304 if(panel.tabTip !== undefined){
39305 ti.setTooltip(panel.tabTip);
39307 ti.on("activate", function(){
39308 this.setActivePanel(panel);
39311 if(this.config.closeOnTab){
39312 ti.on("beforeclose", function(t, e){
39314 this.remove(panel);
39318 panel.tabItem = ti;
39323 updatePanelTitle : function(panel, title)
39325 if(this.activePanel == panel){
39326 this.updateTitle(title);
39329 var ti = this.tabs.getTab(panel.getEl().id);
39331 if(panel.tabTip !== undefined){
39332 ti.setTooltip(panel.tabTip);
39337 updateTitle : function(title){
39338 if(this.titleTextEl && !this.config.title){
39339 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39343 setActivePanel : function(panel)
39345 panel = this.getPanel(panel);
39346 if(this.activePanel && this.activePanel != panel){
39347 if(this.activePanel.setActiveState(false) === false){
39351 this.activePanel = panel;
39352 panel.setActiveState(true);
39353 if(this.panelSize){
39354 panel.setSize(this.panelSize.width, this.panelSize.height);
39357 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39359 this.updateTitle(panel.getTitle());
39361 this.fireEvent("invalidated", this);
39363 this.fireEvent("panelactivated", this, panel);
39367 * Shows the specified panel.
39368 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39369 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39371 showPanel : function(panel)
39373 panel = this.getPanel(panel);
39376 var tab = this.tabs.getTab(panel.getEl().id);
39377 if(tab.isHidden()){
39378 this.tabs.unhideTab(tab.id);
39382 this.setActivePanel(panel);
39389 * Get the active panel for this region.
39390 * @return {Roo.ContentPanel} The active panel or null
39392 getActivePanel : function(){
39393 return this.activePanel;
39396 validateVisibility : function(){
39397 if(this.panels.getCount() < 1){
39398 this.updateTitle(" ");
39399 this.closeBtn.hide();
39402 if(!this.isVisible()){
39409 * Adds the passed ContentPanel(s) to this region.
39410 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39411 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39413 add : function(panel)
39415 if(arguments.length > 1){
39416 for(var i = 0, len = arguments.length; i < len; i++) {
39417 this.add(arguments[i]);
39422 // if we have not been rendered yet, then we can not really do much of this..
39423 if (!this.bodyEl) {
39424 this.unrendered_panels.push(panel);
39431 if(this.hasPanel(panel)){
39432 this.showPanel(panel);
39435 panel.setRegion(this);
39436 this.panels.add(panel);
39437 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39438 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39439 // and hide them... ???
39440 this.bodyEl.dom.appendChild(panel.getEl().dom);
39441 if(panel.background !== true){
39442 this.setActivePanel(panel);
39444 this.fireEvent("paneladded", this, panel);
39451 this.initPanelAsTab(panel);
39455 if(panel.background !== true){
39456 this.tabs.activate(panel.getEl().id);
39458 this.fireEvent("paneladded", this, panel);
39463 * Hides the tab for the specified panel.
39464 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39466 hidePanel : function(panel){
39467 if(this.tabs && (panel = this.getPanel(panel))){
39468 this.tabs.hideTab(panel.getEl().id);
39473 * Unhides the tab for a previously hidden panel.
39474 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39476 unhidePanel : function(panel){
39477 if(this.tabs && (panel = this.getPanel(panel))){
39478 this.tabs.unhideTab(panel.getEl().id);
39482 clearPanels : function(){
39483 while(this.panels.getCount() > 0){
39484 this.remove(this.panels.first());
39489 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39490 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39491 * @param {Boolean} preservePanel Overrides the config preservePanel option
39492 * @return {Roo.ContentPanel} The panel that was removed
39494 remove : function(panel, preservePanel)
39496 panel = this.getPanel(panel);
39501 this.fireEvent("beforeremove", this, panel, e);
39502 if(e.cancel === true){
39505 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39506 var panelId = panel.getId();
39507 this.panels.removeKey(panelId);
39509 document.body.appendChild(panel.getEl().dom);
39512 this.tabs.removeTab(panel.getEl().id);
39513 }else if (!preservePanel){
39514 this.bodyEl.dom.removeChild(panel.getEl().dom);
39516 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39517 var p = this.panels.first();
39518 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39519 tempEl.appendChild(p.getEl().dom);
39520 this.bodyEl.update("");
39521 this.bodyEl.dom.appendChild(p.getEl().dom);
39523 this.updateTitle(p.getTitle());
39525 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39526 this.setActivePanel(p);
39528 panel.setRegion(null);
39529 if(this.activePanel == panel){
39530 this.activePanel = null;
39532 if(this.config.autoDestroy !== false && preservePanel !== true){
39533 try{panel.destroy();}catch(e){}
39535 this.fireEvent("panelremoved", this, panel);
39540 * Returns the TabPanel component used by this region
39541 * @return {Roo.TabPanel}
39543 getTabs : function(){
39547 createTool : function(parentEl, className){
39548 var btn = Roo.DomHelper.append(parentEl, {
39550 cls: "x-layout-tools-button",
39553 cls: "roo-layout-tools-button-inner " + className,
39557 btn.addClassOnOver("roo-layout-tools-button-over");
39562 * Ext JS Library 1.1.1
39563 * Copyright(c) 2006-2007, Ext JS, LLC.
39565 * Originally Released Under LGPL - original licence link has changed is not relivant.
39568 * <script type="text/javascript">
39574 * @class Roo.SplitLayoutRegion
39575 * @extends Roo.LayoutRegion
39576 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39578 Roo.bootstrap.layout.Split = function(config){
39579 this.cursor = config.cursor;
39580 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39583 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39585 splitTip : "Drag to resize.",
39586 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39587 useSplitTips : false,
39589 applyConfig : function(config){
39590 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39593 onRender : function(ctr,pos) {
39595 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39596 if(!this.config.split){
39601 var splitEl = Roo.DomHelper.append(ctr.dom, {
39603 id: this.el.id + "-split",
39604 cls: "roo-layout-split roo-layout-split-"+this.position,
39607 /** The SplitBar for this region
39608 * @type Roo.SplitBar */
39609 // does not exist yet...
39610 Roo.log([this.position, this.orientation]);
39612 this.split = new Roo.bootstrap.SplitBar({
39613 dragElement : splitEl,
39614 resizingElement: this.el,
39615 orientation : this.orientation
39618 this.split.on("moved", this.onSplitMove, this);
39619 this.split.useShim = this.config.useShim === true;
39620 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39621 if(this.useSplitTips){
39622 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39624 //if(config.collapsible){
39625 // this.split.el.on("dblclick", this.collapse, this);
39628 if(typeof this.config.minSize != "undefined"){
39629 this.split.minSize = this.config.minSize;
39631 if(typeof this.config.maxSize != "undefined"){
39632 this.split.maxSize = this.config.maxSize;
39634 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39635 this.hideSplitter();
39640 getHMaxSize : function(){
39641 var cmax = this.config.maxSize || 10000;
39642 var center = this.mgr.getRegion("center");
39643 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39646 getVMaxSize : function(){
39647 var cmax = this.config.maxSize || 10000;
39648 var center = this.mgr.getRegion("center");
39649 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39652 onSplitMove : function(split, newSize){
39653 this.fireEvent("resized", this, newSize);
39657 * Returns the {@link Roo.SplitBar} for this region.
39658 * @return {Roo.SplitBar}
39660 getSplitBar : function(){
39665 this.hideSplitter();
39666 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39669 hideSplitter : function(){
39671 this.split.el.setLocation(-2000,-2000);
39672 this.split.el.hide();
39678 this.split.el.show();
39680 Roo.bootstrap.layout.Split.superclass.show.call(this);
39683 beforeSlide: function(){
39684 if(Roo.isGecko){// firefox overflow auto bug workaround
39685 this.bodyEl.clip();
39687 this.tabs.bodyEl.clip();
39689 if(this.activePanel){
39690 this.activePanel.getEl().clip();
39692 if(this.activePanel.beforeSlide){
39693 this.activePanel.beforeSlide();
39699 afterSlide : function(){
39700 if(Roo.isGecko){// firefox overflow auto bug workaround
39701 this.bodyEl.unclip();
39703 this.tabs.bodyEl.unclip();
39705 if(this.activePanel){
39706 this.activePanel.getEl().unclip();
39707 if(this.activePanel.afterSlide){
39708 this.activePanel.afterSlide();
39714 initAutoHide : function(){
39715 if(this.autoHide !== false){
39716 if(!this.autoHideHd){
39717 var st = new Roo.util.DelayedTask(this.slideIn, this);
39718 this.autoHideHd = {
39719 "mouseout": function(e){
39720 if(!e.within(this.el, true)){
39724 "mouseover" : function(e){
39730 this.el.on(this.autoHideHd);
39734 clearAutoHide : function(){
39735 if(this.autoHide !== false){
39736 this.el.un("mouseout", this.autoHideHd.mouseout);
39737 this.el.un("mouseover", this.autoHideHd.mouseover);
39741 clearMonitor : function(){
39742 Roo.get(document).un("click", this.slideInIf, this);
39745 // these names are backwards but not changed for compat
39746 slideOut : function(){
39747 if(this.isSlid || this.el.hasActiveFx()){
39750 this.isSlid = true;
39751 if(this.collapseBtn){
39752 this.collapseBtn.hide();
39754 this.closeBtnState = this.closeBtn.getStyle('display');
39755 this.closeBtn.hide();
39757 this.stickBtn.show();
39760 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39761 this.beforeSlide();
39762 this.el.setStyle("z-index", 10001);
39763 this.el.slideIn(this.getSlideAnchor(), {
39764 callback: function(){
39766 this.initAutoHide();
39767 Roo.get(document).on("click", this.slideInIf, this);
39768 this.fireEvent("slideshow", this);
39775 afterSlideIn : function(){
39776 this.clearAutoHide();
39777 this.isSlid = false;
39778 this.clearMonitor();
39779 this.el.setStyle("z-index", "");
39780 if(this.collapseBtn){
39781 this.collapseBtn.show();
39783 this.closeBtn.setStyle('display', this.closeBtnState);
39785 this.stickBtn.hide();
39787 this.fireEvent("slidehide", this);
39790 slideIn : function(cb){
39791 if(!this.isSlid || this.el.hasActiveFx()){
39795 this.isSlid = false;
39796 this.beforeSlide();
39797 this.el.slideOut(this.getSlideAnchor(), {
39798 callback: function(){
39799 this.el.setLeftTop(-10000, -10000);
39801 this.afterSlideIn();
39809 slideInIf : function(e){
39810 if(!e.within(this.el)){
39815 animateCollapse : function(){
39816 this.beforeSlide();
39817 this.el.setStyle("z-index", 20000);
39818 var anchor = this.getSlideAnchor();
39819 this.el.slideOut(anchor, {
39820 callback : function(){
39821 this.el.setStyle("z-index", "");
39822 this.collapsedEl.slideIn(anchor, {duration:.3});
39824 this.el.setLocation(-10000,-10000);
39826 this.fireEvent("collapsed", this);
39833 animateExpand : function(){
39834 this.beforeSlide();
39835 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39836 this.el.setStyle("z-index", 20000);
39837 this.collapsedEl.hide({
39840 this.el.slideIn(this.getSlideAnchor(), {
39841 callback : function(){
39842 this.el.setStyle("z-index", "");
39845 this.split.el.show();
39847 this.fireEvent("invalidated", this);
39848 this.fireEvent("expanded", this);
39876 getAnchor : function(){
39877 return this.anchors[this.position];
39880 getCollapseAnchor : function(){
39881 return this.canchors[this.position];
39884 getSlideAnchor : function(){
39885 return this.sanchors[this.position];
39888 getAlignAdj : function(){
39889 var cm = this.cmargins;
39890 switch(this.position){
39906 getExpandAdj : function(){
39907 var c = this.collapsedEl, cm = this.cmargins;
39908 switch(this.position){
39910 return [-(cm.right+c.getWidth()+cm.left), 0];
39913 return [cm.right+c.getWidth()+cm.left, 0];
39916 return [0, -(cm.top+cm.bottom+c.getHeight())];
39919 return [0, cm.top+cm.bottom+c.getHeight()];
39925 * Ext JS Library 1.1.1
39926 * Copyright(c) 2006-2007, Ext JS, LLC.
39928 * Originally Released Under LGPL - original licence link has changed is not relivant.
39931 * <script type="text/javascript">
39934 * These classes are private internal classes
39936 Roo.bootstrap.layout.Center = function(config){
39937 config.region = "center";
39938 Roo.bootstrap.layout.Region.call(this, config);
39939 this.visible = true;
39940 this.minWidth = config.minWidth || 20;
39941 this.minHeight = config.minHeight || 20;
39944 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39946 // center panel can't be hidden
39950 // center panel can't be hidden
39953 getMinWidth: function(){
39954 return this.minWidth;
39957 getMinHeight: function(){
39958 return this.minHeight;
39972 Roo.bootstrap.layout.North = function(config)
39974 config.region = 'north';
39975 config.cursor = 'n-resize';
39977 Roo.bootstrap.layout.Split.call(this, config);
39981 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39982 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39983 this.split.el.addClass("roo-layout-split-v");
39985 //var size = config.initialSize || config.height;
39986 //if(this.el && typeof size != "undefined"){
39987 // this.el.setHeight(size);
39990 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39992 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39995 onRender : function(ctr, pos)
39997 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39998 var size = this.config.initialSize || this.config.height;
39999 if(this.el && typeof size != "undefined"){
40000 this.el.setHeight(size);
40005 getBox : function(){
40006 if(this.collapsed){
40007 return this.collapsedEl.getBox();
40009 var box = this.el.getBox();
40011 box.height += this.split.el.getHeight();
40016 updateBox : function(box){
40017 if(this.split && !this.collapsed){
40018 box.height -= this.split.el.getHeight();
40019 this.split.el.setLeft(box.x);
40020 this.split.el.setTop(box.y+box.height);
40021 this.split.el.setWidth(box.width);
40023 if(this.collapsed){
40024 this.updateBody(box.width, null);
40026 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40034 Roo.bootstrap.layout.South = function(config){
40035 config.region = 'south';
40036 config.cursor = 's-resize';
40037 Roo.bootstrap.layout.Split.call(this, config);
40039 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40040 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40041 this.split.el.addClass("roo-layout-split-v");
40046 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40047 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40049 onRender : function(ctr, pos)
40051 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40052 var size = this.config.initialSize || this.config.height;
40053 if(this.el && typeof size != "undefined"){
40054 this.el.setHeight(size);
40059 getBox : function(){
40060 if(this.collapsed){
40061 return this.collapsedEl.getBox();
40063 var box = this.el.getBox();
40065 var sh = this.split.el.getHeight();
40072 updateBox : function(box){
40073 if(this.split && !this.collapsed){
40074 var sh = this.split.el.getHeight();
40077 this.split.el.setLeft(box.x);
40078 this.split.el.setTop(box.y-sh);
40079 this.split.el.setWidth(box.width);
40081 if(this.collapsed){
40082 this.updateBody(box.width, null);
40084 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40088 Roo.bootstrap.layout.East = function(config){
40089 config.region = "east";
40090 config.cursor = "e-resize";
40091 Roo.bootstrap.layout.Split.call(this, config);
40093 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40094 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40095 this.split.el.addClass("roo-layout-split-h");
40099 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40100 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40102 onRender : function(ctr, pos)
40104 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40105 var size = this.config.initialSize || this.config.width;
40106 if(this.el && typeof size != "undefined"){
40107 this.el.setWidth(size);
40112 getBox : function(){
40113 if(this.collapsed){
40114 return this.collapsedEl.getBox();
40116 var box = this.el.getBox();
40118 var sw = this.split.el.getWidth();
40125 updateBox : function(box){
40126 if(this.split && !this.collapsed){
40127 var sw = this.split.el.getWidth();
40129 this.split.el.setLeft(box.x);
40130 this.split.el.setTop(box.y);
40131 this.split.el.setHeight(box.height);
40134 if(this.collapsed){
40135 this.updateBody(null, box.height);
40137 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40141 Roo.bootstrap.layout.West = function(config){
40142 config.region = "west";
40143 config.cursor = "w-resize";
40145 Roo.bootstrap.layout.Split.call(this, config);
40147 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40148 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40149 this.split.el.addClass("roo-layout-split-h");
40153 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40154 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40156 onRender: function(ctr, pos)
40158 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40159 var size = this.config.initialSize || this.config.width;
40160 if(typeof size != "undefined"){
40161 this.el.setWidth(size);
40165 getBox : function(){
40166 if(this.collapsed){
40167 return this.collapsedEl.getBox();
40169 var box = this.el.getBox();
40170 if (box.width == 0) {
40171 box.width = this.config.width; // kludge?
40174 box.width += this.split.el.getWidth();
40179 updateBox : function(box){
40180 if(this.split && !this.collapsed){
40181 var sw = this.split.el.getWidth();
40183 this.split.el.setLeft(box.x+box.width);
40184 this.split.el.setTop(box.y);
40185 this.split.el.setHeight(box.height);
40187 if(this.collapsed){
40188 this.updateBody(null, box.height);
40190 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40194 * Ext JS Library 1.1.1
40195 * Copyright(c) 2006-2007, Ext JS, LLC.
40197 * Originally Released Under LGPL - original licence link has changed is not relivant.
40200 * <script type="text/javascript">
40203 * @class Roo.bootstrap.paenl.Content
40204 * @extends Roo.util.Observable
40206 * @children Roo.bootstrap.Component
40207 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40208 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40209 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40210 * @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
40211 * @cfg {Boolean} closable True if the panel can be closed/removed
40212 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40213 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40214 * @cfg {Toolbar} toolbar A toolbar for this panel
40215 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40216 * @cfg {String} title The title for this panel
40217 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40218 * @cfg {String} url Calls {@link #setUrl} with this value
40219 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40220 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40221 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40222 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40223 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40224 * @cfg {Boolean} badges render the badges
40225 * @cfg {String} cls extra classes to use
40226 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40229 * Create a new ContentPanel.
40230 * @param {String/Object} config A string to set only the title or a config object
40233 Roo.bootstrap.panel.Content = function( config){
40235 this.tpl = config.tpl || false;
40237 var el = config.el;
40238 var content = config.content;
40240 if(config.autoCreate){ // xtype is available if this is called from factory
40243 this.el = Roo.get(el);
40244 if(!this.el && config && config.autoCreate){
40245 if(typeof config.autoCreate == "object"){
40246 if(!config.autoCreate.id){
40247 config.autoCreate.id = config.id||el;
40249 this.el = Roo.DomHelper.append(document.body,
40250 config.autoCreate, true);
40254 cls: (config.cls || '') +
40255 (config.background ? ' bg-' + config.background : '') +
40256 " roo-layout-inactive-content",
40259 if (config.iframe) {
40263 style : 'border: 0px',
40264 src : 'about:blank'
40270 elcfg.html = config.html;
40274 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40275 if (config.iframe) {
40276 this.iframeEl = this.el.select('iframe',true).first();
40281 this.closable = false;
40282 this.loaded = false;
40283 this.active = false;
40286 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40288 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40290 this.wrapEl = this.el; //this.el.wrap();
40292 if (config.toolbar.items) {
40293 ti = config.toolbar.items ;
40294 delete config.toolbar.items ;
40298 this.toolbar.render(this.wrapEl, 'before');
40299 for(var i =0;i < ti.length;i++) {
40300 // Roo.log(['add child', items[i]]);
40301 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40303 this.toolbar.items = nitems;
40304 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40305 delete config.toolbar;
40309 // xtype created footer. - not sure if will work as we normally have to render first..
40310 if (this.footer && !this.footer.el && this.footer.xtype) {
40311 if (!this.wrapEl) {
40312 this.wrapEl = this.el.wrap();
40315 this.footer.container = this.wrapEl.createChild();
40317 this.footer = Roo.factory(this.footer, Roo);
40322 if(typeof config == "string"){
40323 this.title = config;
40325 Roo.apply(this, config);
40329 this.resizeEl = Roo.get(this.resizeEl, true);
40331 this.resizeEl = this.el;
40333 // handle view.xtype
40341 * Fires when this panel is activated.
40342 * @param {Roo.ContentPanel} this
40346 * @event deactivate
40347 * Fires when this panel is activated.
40348 * @param {Roo.ContentPanel} this
40350 "deactivate" : true,
40354 * Fires when this panel is resized if fitToFrame is true.
40355 * @param {Roo.ContentPanel} this
40356 * @param {Number} width The width after any component adjustments
40357 * @param {Number} height The height after any component adjustments
40363 * Fires when this tab is created
40364 * @param {Roo.ContentPanel} this
40370 * Fires when this content is scrolled
40371 * @param {Roo.ContentPanel} this
40372 * @param {Event} scrollEvent
40383 if(this.autoScroll && !this.iframe){
40384 this.resizeEl.setStyle("overflow", "auto");
40385 this.resizeEl.on('scroll', this.onScroll, this);
40387 // fix randome scrolling
40388 //this.el.on('scroll', function() {
40389 // Roo.log('fix random scolling');
40390 // this.scrollTo('top',0);
40393 content = content || this.content;
40395 this.setContent(content);
40397 if(config && config.url){
40398 this.setUrl(this.url, this.params, this.loadOnce);
40403 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40405 if (this.view && typeof(this.view.xtype) != 'undefined') {
40406 this.view.el = this.el.appendChild(document.createElement("div"));
40407 this.view = Roo.factory(this.view);
40408 this.view.render && this.view.render(false, '');
40412 this.fireEvent('render', this);
40415 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40425 /* Resize Element - use this to work out scroll etc. */
40428 setRegion : function(region){
40429 this.region = region;
40430 this.setActiveClass(region && !this.background);
40434 setActiveClass: function(state)
40437 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40438 this.el.setStyle('position','relative');
40440 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40441 this.el.setStyle('position', 'absolute');
40446 * Returns the toolbar for this Panel if one was configured.
40447 * @return {Roo.Toolbar}
40449 getToolbar : function(){
40450 return this.toolbar;
40453 setActiveState : function(active)
40455 this.active = active;
40456 this.setActiveClass(active);
40458 if(this.fireEvent("deactivate", this) === false){
40463 this.fireEvent("activate", this);
40467 * Updates this panel's element (not for iframe)
40468 * @param {String} content The new content
40469 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40471 setContent : function(content, loadScripts){
40476 this.el.update(content, loadScripts);
40479 ignoreResize : function(w, h){
40480 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40483 this.lastSize = {width: w, height: h};
40488 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40489 * @return {Roo.UpdateManager} The UpdateManager
40491 getUpdateManager : function(){
40495 return this.el.getUpdateManager();
40498 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40499 * Does not work with IFRAME contents
40500 * @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:
40503 url: "your-url.php",
40504 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40505 callback: yourFunction,
40506 scope: yourObject, //(optional scope)
40509 text: "Loading...",
40515 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40516 * 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.
40517 * @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}
40518 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40519 * @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.
40520 * @return {Roo.ContentPanel} this
40528 var um = this.el.getUpdateManager();
40529 um.update.apply(um, arguments);
40535 * 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.
40536 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40537 * @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)
40538 * @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)
40539 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40541 setUrl : function(url, params, loadOnce){
40543 this.iframeEl.dom.src = url;
40547 if(this.refreshDelegate){
40548 this.removeListener("activate", this.refreshDelegate);
40550 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40551 this.on("activate", this.refreshDelegate);
40552 return this.el.getUpdateManager();
40555 _handleRefresh : function(url, params, loadOnce){
40556 if(!loadOnce || !this.loaded){
40557 var updater = this.el.getUpdateManager();
40558 updater.update(url, params, this._setLoaded.createDelegate(this));
40562 _setLoaded : function(){
40563 this.loaded = true;
40567 * Returns this panel's id
40570 getId : function(){
40575 * Returns this panel's element - used by regiosn to add.
40576 * @return {Roo.Element}
40578 getEl : function(){
40579 return this.wrapEl || this.el;
40584 adjustForComponents : function(width, height)
40586 //Roo.log('adjustForComponents ');
40587 if(this.resizeEl != this.el){
40588 width -= this.el.getFrameWidth('lr');
40589 height -= this.el.getFrameWidth('tb');
40592 var te = this.toolbar.getEl();
40593 te.setWidth(width);
40594 height -= te.getHeight();
40597 var te = this.footer.getEl();
40598 te.setWidth(width);
40599 height -= te.getHeight();
40603 if(this.adjustments){
40604 width += this.adjustments[0];
40605 height += this.adjustments[1];
40607 return {"width": width, "height": height};
40610 setSize : function(width, height){
40611 if(this.fitToFrame && !this.ignoreResize(width, height)){
40612 if(this.fitContainer && this.resizeEl != this.el){
40613 this.el.setSize(width, height);
40615 var size = this.adjustForComponents(width, height);
40617 this.iframeEl.setSize(width,height);
40620 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40621 this.fireEvent('resize', this, size.width, size.height);
40628 * Returns this panel's title
40631 getTitle : function(){
40633 if (typeof(this.title) != 'object') {
40638 for (var k in this.title) {
40639 if (!this.title.hasOwnProperty(k)) {
40643 if (k.indexOf('-') >= 0) {
40644 var s = k.split('-');
40645 for (var i = 0; i<s.length; i++) {
40646 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40649 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40656 * Set this panel's title
40657 * @param {String} title
40659 setTitle : function(title){
40660 this.title = title;
40662 this.region.updatePanelTitle(this, title);
40667 * Returns true is this panel was configured to be closable
40668 * @return {Boolean}
40670 isClosable : function(){
40671 return this.closable;
40674 beforeSlide : function(){
40676 this.resizeEl.clip();
40679 afterSlide : function(){
40681 this.resizeEl.unclip();
40685 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40686 * Will fail silently if the {@link #setUrl} method has not been called.
40687 * This does not activate the panel, just updates its content.
40689 refresh : function(){
40690 if(this.refreshDelegate){
40691 this.loaded = false;
40692 this.refreshDelegate();
40697 * Destroys this panel
40699 destroy : function(){
40700 this.el.removeAllListeners();
40701 var tempEl = document.createElement("span");
40702 tempEl.appendChild(this.el.dom);
40703 tempEl.innerHTML = "";
40709 * form - if the content panel contains a form - this is a reference to it.
40710 * @type {Roo.form.Form}
40714 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40715 * This contains a reference to it.
40721 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40731 * @param {Object} cfg Xtype definition of item to add.
40735 getChildContainer: function () {
40736 return this.getEl();
40740 onScroll : function(e)
40742 this.fireEvent('scroll', this, e);
40747 var ret = new Roo.factory(cfg);
40752 if (cfg.xtype.match(/^Form$/)) {
40755 //if (this.footer) {
40756 // el = this.footer.container.insertSibling(false, 'before');
40758 el = this.el.createChild();
40761 this.form = new Roo.form.Form(cfg);
40764 if ( this.form.allItems.length) {
40765 this.form.render(el.dom);
40769 // should only have one of theses..
40770 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40771 // views.. should not be just added - used named prop 'view''
40773 cfg.el = this.el.appendChild(document.createElement("div"));
40776 var ret = new Roo.factory(cfg);
40778 ret.render && ret.render(false, ''); // render blank..
40788 * @class Roo.bootstrap.panel.Grid
40789 * @extends Roo.bootstrap.panel.Content
40791 * Create a new GridPanel.
40792 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40793 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40794 * @param {Object} config A the config object
40800 Roo.bootstrap.panel.Grid = function(config)
40804 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40805 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40807 config.el = this.wrapper;
40808 //this.el = this.wrapper;
40810 if (config.container) {
40811 // ctor'ed from a Border/panel.grid
40814 this.wrapper.setStyle("overflow", "hidden");
40815 this.wrapper.addClass('roo-grid-container');
40820 if(config.toolbar){
40821 var tool_el = this.wrapper.createChild();
40822 this.toolbar = Roo.factory(config.toolbar);
40824 if (config.toolbar.items) {
40825 ti = config.toolbar.items ;
40826 delete config.toolbar.items ;
40830 this.toolbar.render(tool_el);
40831 for(var i =0;i < ti.length;i++) {
40832 // Roo.log(['add child', items[i]]);
40833 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40835 this.toolbar.items = nitems;
40837 delete config.toolbar;
40840 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40841 config.grid.scrollBody = true;;
40842 config.grid.monitorWindowResize = false; // turn off autosizing
40843 config.grid.autoHeight = false;
40844 config.grid.autoWidth = false;
40846 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40848 if (config.background) {
40849 // render grid on panel activation (if panel background)
40850 this.on('activate', function(gp) {
40851 if (!gp.grid.rendered) {
40852 gp.grid.render(this.wrapper);
40853 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40858 this.grid.render(this.wrapper);
40859 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40862 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40863 // ??? needed ??? config.el = this.wrapper;
40868 // xtype created footer. - not sure if will work as we normally have to render first..
40869 if (this.footer && !this.footer.el && this.footer.xtype) {
40871 var ctr = this.grid.getView().getFooterPanel(true);
40872 this.footer.dataSource = this.grid.dataSource;
40873 this.footer = Roo.factory(this.footer, Roo);
40874 this.footer.render(ctr);
40884 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40885 getId : function(){
40886 return this.grid.id;
40890 * Returns the grid for this panel
40891 * @return {Roo.bootstrap.Table}
40893 getGrid : function(){
40897 setSize : function(width, height){
40898 if(!this.ignoreResize(width, height)){
40899 var grid = this.grid;
40900 var size = this.adjustForComponents(width, height);
40901 // tfoot is not a footer?
40904 var gridel = grid.getGridEl();
40905 gridel.setSize(size.width, size.height);
40907 var tbd = grid.getGridEl().select('tbody', true).first();
40908 var thd = grid.getGridEl().select('thead',true).first();
40909 var tbf= grid.getGridEl().select('tfoot', true).first();
40912 size.height -= tbf.getHeight();
40915 size.height -= thd.getHeight();
40918 tbd.setSize(size.width, size.height );
40919 // this is for the account management tab -seems to work there.
40920 var thd = grid.getGridEl().select('thead',true).first();
40922 // tbd.setSize(size.width, size.height - thd.getHeight());
40931 beforeSlide : function(){
40932 this.grid.getView().scroller.clip();
40935 afterSlide : function(){
40936 this.grid.getView().scroller.unclip();
40939 destroy : function(){
40940 this.grid.destroy();
40942 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40947 * @class Roo.bootstrap.panel.Nest
40948 * @extends Roo.bootstrap.panel.Content
40950 * Create a new Panel, that can contain a layout.Border.
40953 * @param {String/Object} config A string to set only the title or a config object
40955 Roo.bootstrap.panel.Nest = function(config)
40957 // construct with only one argument..
40958 /* FIXME - implement nicer consturctors
40959 if (layout.layout) {
40961 layout = config.layout;
40962 delete config.layout;
40964 if (layout.xtype && !layout.getEl) {
40965 // then layout needs constructing..
40966 layout = Roo.factory(layout, Roo);
40970 config.el = config.layout.getEl();
40972 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40974 config.layout.monitorWindowResize = false; // turn off autosizing
40975 this.layout = config.layout;
40976 this.layout.getEl().addClass("roo-layout-nested-layout");
40977 this.layout.parent = this;
40984 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40986 * @cfg {Roo.BorderLayout} layout The layout for this panel
40990 setSize : function(width, height){
40991 if(!this.ignoreResize(width, height)){
40992 var size = this.adjustForComponents(width, height);
40993 var el = this.layout.getEl();
40994 if (size.height < 1) {
40995 el.setWidth(size.width);
40997 el.setSize(size.width, size.height);
40999 var touch = el.dom.offsetWidth;
41000 this.layout.layout();
41001 // ie requires a double layout on the first pass
41002 if(Roo.isIE && !this.initialized){
41003 this.initialized = true;
41004 this.layout.layout();
41009 // activate all subpanels if not currently active..
41011 setActiveState : function(active){
41012 this.active = active;
41013 this.setActiveClass(active);
41016 this.fireEvent("deactivate", this);
41020 this.fireEvent("activate", this);
41021 // not sure if this should happen before or after..
41022 if (!this.layout) {
41023 return; // should not happen..
41026 for (var r in this.layout.regions) {
41027 reg = this.layout.getRegion(r);
41028 if (reg.getActivePanel()) {
41029 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41030 reg.setActivePanel(reg.getActivePanel());
41033 if (!reg.panels.length) {
41036 reg.showPanel(reg.getPanel(0));
41045 * Returns the nested BorderLayout for this panel
41046 * @return {Roo.BorderLayout}
41048 getLayout : function(){
41049 return this.layout;
41053 * Adds a xtype elements to the layout of the nested panel
41057 xtype : 'ContentPanel',
41064 xtype : 'NestedLayoutPanel',
41070 items : [ ... list of content panels or nested layout panels.. ]
41074 * @param {Object} cfg Xtype definition of item to add.
41076 addxtype : function(cfg) {
41077 return this.layout.addxtype(cfg);
41082 * Ext JS Library 1.1.1
41083 * Copyright(c) 2006-2007, Ext JS, LLC.
41085 * Originally Released Under LGPL - original licence link has changed is not relivant.
41088 * <script type="text/javascript">
41091 * @class Roo.TabPanel
41092 * @extends Roo.util.Observable
41093 * A lightweight tab container.
41097 // basic tabs 1, built from existing content
41098 var tabs = new Roo.TabPanel("tabs1");
41099 tabs.addTab("script", "View Script");
41100 tabs.addTab("markup", "View Markup");
41101 tabs.activate("script");
41103 // more advanced tabs, built from javascript
41104 var jtabs = new Roo.TabPanel("jtabs");
41105 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41107 // set up the UpdateManager
41108 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41109 var updater = tab2.getUpdateManager();
41110 updater.setDefaultUrl("ajax1.htm");
41111 tab2.on('activate', updater.refresh, updater, true);
41113 // Use setUrl for Ajax loading
41114 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41115 tab3.setUrl("ajax2.htm", null, true);
41118 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41121 jtabs.activate("jtabs-1");
41124 * Create a new TabPanel.
41125 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41126 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41128 Roo.bootstrap.panel.Tabs = function(config){
41130 * The container element for this TabPanel.
41131 * @type Roo.Element
41133 this.el = Roo.get(config.el);
41136 if(typeof config == "boolean"){
41137 this.tabPosition = config ? "bottom" : "top";
41139 Roo.apply(this, config);
41143 if(this.tabPosition == "bottom"){
41144 // if tabs are at the bottom = create the body first.
41145 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41146 this.el.addClass("roo-tabs-bottom");
41148 // next create the tabs holders
41150 if (this.tabPosition == "west"){
41152 var reg = this.region; // fake it..
41154 if (!reg.mgr.parent) {
41157 reg = reg.mgr.parent.region;
41159 Roo.log("got nest?");
41161 if (reg.mgr.getRegion('west')) {
41162 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41163 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41164 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41165 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41166 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41174 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41175 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41176 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41177 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41182 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41185 // finally - if tabs are at the top, then create the body last..
41186 if(this.tabPosition != "bottom"){
41187 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41188 * @type Roo.Element
41190 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41191 this.el.addClass("roo-tabs-top");
41195 this.bodyEl.setStyle("position", "relative");
41197 this.active = null;
41198 this.activateDelegate = this.activate.createDelegate(this);
41203 * Fires when the active tab changes
41204 * @param {Roo.TabPanel} this
41205 * @param {Roo.TabPanelItem} activePanel The new active tab
41209 * @event beforetabchange
41210 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41211 * @param {Roo.TabPanel} this
41212 * @param {Object} e Set cancel to true on this object to cancel the tab change
41213 * @param {Roo.TabPanelItem} tab The tab being changed to
41215 "beforetabchange" : true
41218 Roo.EventManager.onWindowResize(this.onResize, this);
41219 this.cpad = this.el.getPadding("lr");
41220 this.hiddenCount = 0;
41223 // toolbar on the tabbar support...
41224 if (this.toolbar) {
41225 alert("no toolbar support yet");
41226 this.toolbar = false;
41228 var tcfg = this.toolbar;
41229 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41230 this.toolbar = new Roo.Toolbar(tcfg);
41231 if (Roo.isSafari) {
41232 var tbl = tcfg.container.child('table', true);
41233 tbl.setAttribute('width', '100%');
41241 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41244 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41246 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41248 tabPosition : "top",
41250 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41252 currentTabWidth : 0,
41254 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41258 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41262 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41264 preferredTabWidth : 175,
41266 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41268 resizeTabs : false,
41270 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41272 monitorResize : true,
41274 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41276 toolbar : false, // set by caller..
41278 region : false, /// set by caller
41280 disableTooltips : true, // not used yet...
41283 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41284 * @param {String} id The id of the div to use <b>or create</b>
41285 * @param {String} text The text for the tab
41286 * @param {String} content (optional) Content to put in the TabPanelItem body
41287 * @param {Boolean} closable (optional) True to create a close icon on the tab
41288 * @return {Roo.TabPanelItem} The created TabPanelItem
41290 addTab : function(id, text, content, closable, tpl)
41292 var item = new Roo.bootstrap.panel.TabItem({
41296 closable : closable,
41299 this.addTabItem(item);
41301 item.setContent(content);
41307 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41308 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41309 * @return {Roo.TabPanelItem}
41311 getTab : function(id){
41312 return this.items[id];
41316 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41317 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41319 hideTab : function(id){
41320 var t = this.items[id];
41323 this.hiddenCount++;
41324 this.autoSizeTabs();
41329 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41330 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41332 unhideTab : function(id){
41333 var t = this.items[id];
41335 t.setHidden(false);
41336 this.hiddenCount--;
41337 this.autoSizeTabs();
41342 * Adds an existing {@link Roo.TabPanelItem}.
41343 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41345 addTabItem : function(item)
41347 this.items[item.id] = item;
41348 this.items.push(item);
41349 this.autoSizeTabs();
41350 // if(this.resizeTabs){
41351 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41352 // this.autoSizeTabs();
41354 // item.autoSize();
41359 * Removes a {@link Roo.TabPanelItem}.
41360 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41362 removeTab : function(id){
41363 var items = this.items;
41364 var tab = items[id];
41365 if(!tab) { return; }
41366 var index = items.indexOf(tab);
41367 if(this.active == tab && items.length > 1){
41368 var newTab = this.getNextAvailable(index);
41373 this.stripEl.dom.removeChild(tab.pnode.dom);
41374 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41375 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41377 items.splice(index, 1);
41378 delete this.items[tab.id];
41379 tab.fireEvent("close", tab);
41380 tab.purgeListeners();
41381 this.autoSizeTabs();
41384 getNextAvailable : function(start){
41385 var items = this.items;
41387 // look for a next tab that will slide over to
41388 // replace the one being removed
41389 while(index < items.length){
41390 var item = items[++index];
41391 if(item && !item.isHidden()){
41395 // if one isn't found select the previous tab (on the left)
41398 var item = items[--index];
41399 if(item && !item.isHidden()){
41407 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41408 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41410 disableTab : function(id){
41411 var tab = this.items[id];
41412 if(tab && this.active != tab){
41418 * Enables a {@link Roo.TabPanelItem} that is disabled.
41419 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41421 enableTab : function(id){
41422 var tab = this.items[id];
41427 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41428 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41429 * @return {Roo.TabPanelItem} The TabPanelItem.
41431 activate : function(id)
41433 //Roo.log('activite:' + id);
41435 var tab = this.items[id];
41439 if(tab == this.active || tab.disabled){
41443 this.fireEvent("beforetabchange", this, e, tab);
41444 if(e.cancel !== true && !tab.disabled){
41446 this.active.hide();
41448 this.active = this.items[id];
41449 this.active.show();
41450 this.fireEvent("tabchange", this, this.active);
41456 * Gets the active {@link Roo.TabPanelItem}.
41457 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41459 getActiveTab : function(){
41460 return this.active;
41464 * Updates the tab body element to fit the height of the container element
41465 * for overflow scrolling
41466 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41468 syncHeight : function(targetHeight){
41469 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41470 var bm = this.bodyEl.getMargins();
41471 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41472 this.bodyEl.setHeight(newHeight);
41476 onResize : function(){
41477 if(this.monitorResize){
41478 this.autoSizeTabs();
41483 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41485 beginUpdate : function(){
41486 this.updating = true;
41490 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41492 endUpdate : function(){
41493 this.updating = false;
41494 this.autoSizeTabs();
41498 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41500 autoSizeTabs : function()
41502 var count = this.items.length;
41503 var vcount = count - this.hiddenCount;
41506 this.stripEl.hide();
41508 this.stripEl.show();
41511 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41516 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41517 var availWidth = Math.floor(w / vcount);
41518 var b = this.stripBody;
41519 if(b.getWidth() > w){
41520 var tabs = this.items;
41521 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41522 if(availWidth < this.minTabWidth){
41523 /*if(!this.sleft){ // incomplete scrolling code
41524 this.createScrollButtons();
41527 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41530 if(this.currentTabWidth < this.preferredTabWidth){
41531 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41537 * Returns the number of tabs in this TabPanel.
41540 getCount : function(){
41541 return this.items.length;
41545 * Resizes all the tabs to the passed width
41546 * @param {Number} The new width
41548 setTabWidth : function(width){
41549 this.currentTabWidth = width;
41550 for(var i = 0, len = this.items.length; i < len; i++) {
41551 if(!this.items[i].isHidden()) {
41552 this.items[i].setWidth(width);
41558 * Destroys this TabPanel
41559 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41561 destroy : function(removeEl){
41562 Roo.EventManager.removeResizeListener(this.onResize, this);
41563 for(var i = 0, len = this.items.length; i < len; i++){
41564 this.items[i].purgeListeners();
41566 if(removeEl === true){
41567 this.el.update("");
41572 createStrip : function(container)
41574 var strip = document.createElement("nav");
41575 strip.className = Roo.bootstrap.version == 4 ?
41576 "navbar-light bg-light" :
41577 "navbar navbar-default"; //"x-tabs-wrap";
41578 container.appendChild(strip);
41582 createStripList : function(strip)
41584 // div wrapper for retard IE
41585 // returns the "tr" element.
41586 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41587 //'<div class="x-tabs-strip-wrap">'+
41588 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41589 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41590 return strip.firstChild; //.firstChild.firstChild.firstChild;
41592 createBody : function(container)
41594 var body = document.createElement("div");
41595 Roo.id(body, "tab-body");
41596 //Roo.fly(body).addClass("x-tabs-body");
41597 Roo.fly(body).addClass("tab-content");
41598 container.appendChild(body);
41601 createItemBody :function(bodyEl, id){
41602 var body = Roo.getDom(id);
41604 body = document.createElement("div");
41607 //Roo.fly(body).addClass("x-tabs-item-body");
41608 Roo.fly(body).addClass("tab-pane");
41609 bodyEl.insertBefore(body, bodyEl.firstChild);
41613 createStripElements : function(stripEl, text, closable, tpl)
41615 var td = document.createElement("li"); // was td..
41616 td.className = 'nav-item';
41618 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41621 stripEl.appendChild(td);
41623 td.className = "x-tabs-closable";
41624 if(!this.closeTpl){
41625 this.closeTpl = new Roo.Template(
41626 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41627 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41628 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41631 var el = this.closeTpl.overwrite(td, {"text": text});
41632 var close = el.getElementsByTagName("div")[0];
41633 var inner = el.getElementsByTagName("em")[0];
41634 return {"el": el, "close": close, "inner": inner};
41637 // not sure what this is..
41638 // if(!this.tabTpl){
41639 //this.tabTpl = new Roo.Template(
41640 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41641 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41643 // this.tabTpl = new Roo.Template(
41644 // '<a href="#">' +
41645 // '<span unselectable="on"' +
41646 // (this.disableTooltips ? '' : ' title="{text}"') +
41647 // ' >{text}</span></a>'
41653 var template = tpl || this.tabTpl || false;
41656 template = new Roo.Template(
41657 Roo.bootstrap.version == 4 ?
41659 '<a class="nav-link" href="#" unselectable="on"' +
41660 (this.disableTooltips ? '' : ' title="{text}"') +
41663 '<a class="nav-link" href="#">' +
41664 '<span unselectable="on"' +
41665 (this.disableTooltips ? '' : ' title="{text}"') +
41666 ' >{text}</span></a>'
41671 switch (typeof(template)) {
41675 template = new Roo.Template(template);
41681 var el = template.overwrite(td, {"text": text});
41683 var inner = el.getElementsByTagName("span")[0];
41685 return {"el": el, "inner": inner};
41693 * @class Roo.TabPanelItem
41694 * @extends Roo.util.Observable
41695 * Represents an individual item (tab plus body) in a TabPanel.
41696 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41697 * @param {String} id The id of this TabPanelItem
41698 * @param {String} text The text for the tab of this TabPanelItem
41699 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41701 Roo.bootstrap.panel.TabItem = function(config){
41703 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41704 * @type Roo.TabPanel
41706 this.tabPanel = config.panel;
41708 * The id for this TabPanelItem
41711 this.id = config.id;
41713 this.disabled = false;
41715 this.text = config.text;
41717 this.loaded = false;
41718 this.closable = config.closable;
41721 * The body element for this TabPanelItem.
41722 * @type Roo.Element
41724 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41725 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41726 this.bodyEl.setStyle("display", "block");
41727 this.bodyEl.setStyle("zoom", "1");
41728 //this.hideAction();
41730 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41732 this.el = Roo.get(els.el);
41733 this.inner = Roo.get(els.inner, true);
41734 this.textEl = Roo.bootstrap.version == 4 ?
41735 this.el : Roo.get(this.el.dom.firstChild, true);
41737 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41738 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41741 // this.el.on("mousedown", this.onTabMouseDown, this);
41742 this.el.on("click", this.onTabClick, this);
41744 if(config.closable){
41745 var c = Roo.get(els.close, true);
41746 c.dom.title = this.closeText;
41747 c.addClassOnOver("close-over");
41748 c.on("click", this.closeClick, this);
41754 * Fires when this tab becomes the active tab.
41755 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41756 * @param {Roo.TabPanelItem} this
41760 * @event beforeclose
41761 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41762 * @param {Roo.TabPanelItem} this
41763 * @param {Object} e Set cancel to true on this object to cancel the close.
41765 "beforeclose": true,
41768 * Fires when this tab is closed.
41769 * @param {Roo.TabPanelItem} this
41773 * @event deactivate
41774 * Fires when this tab is no longer the active tab.
41775 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41776 * @param {Roo.TabPanelItem} this
41778 "deactivate" : true
41780 this.hidden = false;
41782 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41785 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41787 purgeListeners : function(){
41788 Roo.util.Observable.prototype.purgeListeners.call(this);
41789 this.el.removeAllListeners();
41792 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41795 this.status_node.addClass("active");
41798 this.tabPanel.stripWrap.repaint();
41800 this.fireEvent("activate", this.tabPanel, this);
41804 * Returns true if this tab is the active tab.
41805 * @return {Boolean}
41807 isActive : function(){
41808 return this.tabPanel.getActiveTab() == this;
41812 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41815 this.status_node.removeClass("active");
41817 this.fireEvent("deactivate", this.tabPanel, this);
41820 hideAction : function(){
41821 this.bodyEl.hide();
41822 this.bodyEl.setStyle("position", "absolute");
41823 this.bodyEl.setLeft("-20000px");
41824 this.bodyEl.setTop("-20000px");
41827 showAction : function(){
41828 this.bodyEl.setStyle("position", "relative");
41829 this.bodyEl.setTop("");
41830 this.bodyEl.setLeft("");
41831 this.bodyEl.show();
41835 * Set the tooltip for the tab.
41836 * @param {String} tooltip The tab's tooltip
41838 setTooltip : function(text){
41839 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41840 this.textEl.dom.qtip = text;
41841 this.textEl.dom.removeAttribute('title');
41843 this.textEl.dom.title = text;
41847 onTabClick : function(e){
41848 e.preventDefault();
41849 this.tabPanel.activate(this.id);
41852 onTabMouseDown : function(e){
41853 e.preventDefault();
41854 this.tabPanel.activate(this.id);
41857 getWidth : function(){
41858 return this.inner.getWidth();
41861 setWidth : function(width){
41862 var iwidth = width - this.linode.getPadding("lr");
41863 this.inner.setWidth(iwidth);
41864 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41865 this.linode.setWidth(width);
41869 * Show or hide the tab
41870 * @param {Boolean} hidden True to hide or false to show.
41872 setHidden : function(hidden){
41873 this.hidden = hidden;
41874 this.linode.setStyle("display", hidden ? "none" : "");
41878 * Returns true if this tab is "hidden"
41879 * @return {Boolean}
41881 isHidden : function(){
41882 return this.hidden;
41886 * Returns the text for this tab
41889 getText : function(){
41893 autoSize : function(){
41894 //this.el.beginMeasure();
41895 this.textEl.setWidth(1);
41897 * #2804 [new] Tabs in Roojs
41898 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41900 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41901 //this.el.endMeasure();
41905 * Sets the text for the tab (Note: this also sets the tooltip text)
41906 * @param {String} text The tab's text and tooltip
41908 setText : function(text){
41910 this.textEl.update(text);
41911 this.setTooltip(text);
41912 //if(!this.tabPanel.resizeTabs){
41913 // this.autoSize();
41917 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41919 activate : function(){
41920 this.tabPanel.activate(this.id);
41924 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41926 disable : function(){
41927 if(this.tabPanel.active != this){
41928 this.disabled = true;
41929 this.status_node.addClass("disabled");
41934 * Enables this TabPanelItem if it was previously disabled.
41936 enable : function(){
41937 this.disabled = false;
41938 this.status_node.removeClass("disabled");
41942 * Sets the content for this TabPanelItem.
41943 * @param {String} content The content
41944 * @param {Boolean} loadScripts true to look for and load scripts
41946 setContent : function(content, loadScripts){
41947 this.bodyEl.update(content, loadScripts);
41951 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41952 * @return {Roo.UpdateManager} The UpdateManager
41954 getUpdateManager : function(){
41955 return this.bodyEl.getUpdateManager();
41959 * Set a URL to be used to load the content for this TabPanelItem.
41960 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41961 * @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)
41962 * @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)
41963 * @return {Roo.UpdateManager} The UpdateManager
41965 setUrl : function(url, params, loadOnce){
41966 if(this.refreshDelegate){
41967 this.un('activate', this.refreshDelegate);
41969 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41970 this.on("activate", this.refreshDelegate);
41971 return this.bodyEl.getUpdateManager();
41975 _handleRefresh : function(url, params, loadOnce){
41976 if(!loadOnce || !this.loaded){
41977 var updater = this.bodyEl.getUpdateManager();
41978 updater.update(url, params, this._setLoaded.createDelegate(this));
41983 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41984 * Will fail silently if the setUrl method has not been called.
41985 * This does not activate the panel, just updates its content.
41987 refresh : function(){
41988 if(this.refreshDelegate){
41989 this.loaded = false;
41990 this.refreshDelegate();
41995 _setLoaded : function(){
41996 this.loaded = true;
42000 closeClick : function(e){
42003 this.fireEvent("beforeclose", this, o);
42004 if(o.cancel !== true){
42005 this.tabPanel.removeTab(this.id);
42009 * The text displayed in the tooltip for the close icon.
42012 closeText : "Close this tab"
42015 * This script refer to:
42016 * Title: International Telephone Input
42017 * Author: Jack O'Connor
42018 * Code version: v12.1.12
42019 * Availability: https://github.com/jackocnr/intl-tel-input.git
42022 Roo.bootstrap.form.PhoneInputData = function() {
42025 "Afghanistan (افغانستان)",
42030 "Albania (Shqipëri)",
42035 "Algeria (الجزائر)",
42060 "Antigua and Barbuda",
42070 "Armenia (Հայաստան)",
42086 "Austria (Österreich)",
42091 "Azerbaijan (Azərbaycan)",
42101 "Bahrain (البحرين)",
42106 "Bangladesh (বাংলাদেশ)",
42116 "Belarus (Беларусь)",
42121 "Belgium (België)",
42151 "Bosnia and Herzegovina (Босна и Херцеговина)",
42166 "British Indian Ocean Territory",
42171 "British Virgin Islands",
42181 "Bulgaria (България)",
42191 "Burundi (Uburundi)",
42196 "Cambodia (កម្ពុជា)",
42201 "Cameroon (Cameroun)",
42210 ["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"]
42213 "Cape Verde (Kabu Verdi)",
42218 "Caribbean Netherlands",
42229 "Central African Republic (République centrafricaine)",
42249 "Christmas Island",
42255 "Cocos (Keeling) Islands",
42266 "Comoros (جزر القمر)",
42271 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42276 "Congo (Republic) (Congo-Brazzaville)",
42296 "Croatia (Hrvatska)",
42317 "Czech Republic (Česká republika)",
42322 "Denmark (Danmark)",
42337 "Dominican Republic (República Dominicana)",
42341 ["809", "829", "849"]
42359 "Equatorial Guinea (Guinea Ecuatorial)",
42379 "Falkland Islands (Islas Malvinas)",
42384 "Faroe Islands (Føroyar)",
42405 "French Guiana (Guyane française)",
42410 "French Polynesia (Polynésie française)",
42425 "Georgia (საქართველო)",
42430 "Germany (Deutschland)",
42450 "Greenland (Kalaallit Nunaat)",
42487 "Guinea-Bissau (Guiné Bissau)",
42512 "Hungary (Magyarország)",
42517 "Iceland (Ísland)",
42537 "Iraq (العراق)",
42553 "Israel (ישראל)",
42580 "Jordan (الأردن)",
42585 "Kazakhstan (Казахстан)",
42606 "Kuwait (الكويت)",
42611 "Kyrgyzstan (Кыргызстан)",
42621 "Latvia (Latvija)",
42626 "Lebanon (لبنان)",
42641 "Libya (ليبيا)",
42651 "Lithuania (Lietuva)",
42666 "Macedonia (FYROM) (Македонија)",
42671 "Madagascar (Madagasikara)",
42701 "Marshall Islands",
42711 "Mauritania (موريتانيا)",
42716 "Mauritius (Moris)",
42737 "Moldova (Republica Moldova)",
42747 "Mongolia (Монгол)",
42752 "Montenegro (Crna Gora)",
42762 "Morocco (المغرب)",
42768 "Mozambique (Moçambique)",
42773 "Myanmar (Burma) (မြန်မာ)",
42778 "Namibia (Namibië)",
42793 "Netherlands (Nederland)",
42798 "New Caledonia (Nouvelle-Calédonie)",
42833 "North Korea (조선 민주주의 인민 공화국)",
42838 "Northern Mariana Islands",
42854 "Pakistan (پاکستان)",
42864 "Palestine (فلسطين)",
42874 "Papua New Guinea",
42916 "Réunion (La Réunion)",
42922 "Romania (România)",
42938 "Saint Barthélemy",
42949 "Saint Kitts and Nevis",
42959 "Saint Martin (Saint-Martin (partie française))",
42965 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42970 "Saint Vincent and the Grenadines",
42985 "São Tomé and Príncipe (São Tomé e Príncipe)",
42990 "Saudi Arabia (المملكة العربية السعودية)",
42995 "Senegal (Sénégal)",
43025 "Slovakia (Slovensko)",
43030 "Slovenia (Slovenija)",
43040 "Somalia (Soomaaliya)",
43050 "South Korea (대한민국)",
43055 "South Sudan (جنوب السودان)",
43065 "Sri Lanka (ශ්රී ලංකාව)",
43070 "Sudan (السودان)",
43080 "Svalbard and Jan Mayen",
43091 "Sweden (Sverige)",
43096 "Switzerland (Schweiz)",
43101 "Syria (سوريا)",
43146 "Trinidad and Tobago",
43151 "Tunisia (تونس)",
43156 "Turkey (Türkiye)",
43166 "Turks and Caicos Islands",
43176 "U.S. Virgin Islands",
43186 "Ukraine (Україна)",
43191 "United Arab Emirates (الإمارات العربية المتحدة)",
43213 "Uzbekistan (Oʻzbekiston)",
43223 "Vatican City (Città del Vaticano)",
43234 "Vietnam (Việt Nam)",
43239 "Wallis and Futuna (Wallis-et-Futuna)",
43244 "Western Sahara (الصحراء الغربية)",
43250 "Yemen (اليمن)",
43274 * This script refer to:
43275 * Title: International Telephone Input
43276 * Author: Jack O'Connor
43277 * Code version: v12.1.12
43278 * Availability: https://github.com/jackocnr/intl-tel-input.git
43282 * @class Roo.bootstrap.form.PhoneInput
43283 * @extends Roo.bootstrap.form.TriggerField
43284 * An input with International dial-code selection
43286 * @cfg {String} defaultDialCode default '+852'
43287 * @cfg {Array} preferedCountries default []
43290 * Create a new PhoneInput.
43291 * @param {Object} config Configuration options
43294 Roo.bootstrap.form.PhoneInput = function(config) {
43295 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43298 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43300 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43302 listWidth: undefined,
43304 selectedClass: 'active',
43306 invalidClass : "has-warning",
43308 validClass: 'has-success',
43310 allowed: '0123456789',
43315 * @cfg {String} defaultDialCode The default dial code when initializing the input
43317 defaultDialCode: '+852',
43320 * @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
43322 preferedCountries: false,
43324 getAutoCreate : function()
43326 var data = Roo.bootstrap.form.PhoneInputData();
43327 var align = this.labelAlign || this.parentLabelAlign();
43330 this.allCountries = [];
43331 this.dialCodeMapping = [];
43333 for (var i = 0; i < data.length; i++) {
43335 this.allCountries[i] = {
43339 priority: c[3] || 0,
43340 areaCodes: c[4] || null
43342 this.dialCodeMapping[c[2]] = {
43345 priority: c[3] || 0,
43346 areaCodes: c[4] || null
43358 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43359 maxlength: this.max_length,
43360 cls : 'form-control tel-input',
43361 autocomplete: 'new-password'
43364 var hiddenInput = {
43367 cls: 'hidden-tel-input'
43371 hiddenInput.name = this.name;
43374 if (this.disabled) {
43375 input.disabled = true;
43378 var flag_container = {
43395 cls: this.hasFeedback ? 'has-feedback' : '',
43401 cls: 'dial-code-holder',
43408 cls: 'roo-select2-container input-group',
43415 if (this.fieldLabel.length) {
43418 tooltip: 'This field is required'
43424 cls: 'control-label',
43430 html: this.fieldLabel
43433 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43439 if(this.indicatorpos == 'right') {
43440 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43447 if(align == 'left') {
43455 if(this.labelWidth > 12){
43456 label.style = "width: " + this.labelWidth + 'px';
43458 if(this.labelWidth < 13 && this.labelmd == 0){
43459 this.labelmd = this.labelWidth;
43461 if(this.labellg > 0){
43462 label.cls += ' col-lg-' + this.labellg;
43463 input.cls += ' col-lg-' + (12 - this.labellg);
43465 if(this.labelmd > 0){
43466 label.cls += ' col-md-' + this.labelmd;
43467 container.cls += ' col-md-' + (12 - this.labelmd);
43469 if(this.labelsm > 0){
43470 label.cls += ' col-sm-' + this.labelsm;
43471 container.cls += ' col-sm-' + (12 - this.labelsm);
43473 if(this.labelxs > 0){
43474 label.cls += ' col-xs-' + this.labelxs;
43475 container.cls += ' col-xs-' + (12 - this.labelxs);
43485 var settings = this;
43487 ['xs','sm','md','lg'].map(function(size){
43488 if (settings[size]) {
43489 cfg.cls += ' col-' + size + '-' + settings[size];
43493 this.store = new Roo.data.Store({
43494 proxy : new Roo.data.MemoryProxy({}),
43495 reader : new Roo.data.JsonReader({
43506 'name' : 'dialCode',
43510 'name' : 'priority',
43514 'name' : 'areaCodes',
43521 if(!this.preferedCountries) {
43522 this.preferedCountries = [
43529 var p = this.preferedCountries.reverse();
43532 for (var i = 0; i < p.length; i++) {
43533 for (var j = 0; j < this.allCountries.length; j++) {
43534 if(this.allCountries[j].iso2 == p[i]) {
43535 var t = this.allCountries[j];
43536 this.allCountries.splice(j,1);
43537 this.allCountries.unshift(t);
43543 this.store.proxy.data = {
43545 data: this.allCountries
43551 initEvents : function()
43554 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43556 this.indicator = this.indicatorEl();
43557 this.flag = this.flagEl();
43558 this.dialCodeHolder = this.dialCodeHolderEl();
43560 this.trigger = this.el.select('div.flag-box',true).first();
43561 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43566 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43567 _this.list.setWidth(lw);
43570 this.list.on('mouseover', this.onViewOver, this);
43571 this.list.on('mousemove', this.onViewMove, this);
43572 this.inputEl().on("keyup", this.onKeyUp, this);
43573 this.inputEl().on("keypress", this.onKeyPress, this);
43575 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43577 this.view = new Roo.View(this.list, this.tpl, {
43578 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43581 this.view.on('click', this.onViewClick, this);
43582 this.setValue(this.defaultDialCode);
43585 onTriggerClick : function(e)
43587 Roo.log('trigger click');
43592 if(this.isExpanded()){
43594 this.hasFocus = false;
43596 this.store.load({});
43597 this.hasFocus = true;
43602 isExpanded : function()
43604 return this.list.isVisible();
43607 collapse : function()
43609 if(!this.isExpanded()){
43613 Roo.get(document).un('mousedown', this.collapseIf, this);
43614 Roo.get(document).un('mousewheel', this.collapseIf, this);
43615 this.fireEvent('collapse', this);
43619 expand : function()
43623 if(this.isExpanded() || !this.hasFocus){
43627 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43628 this.list.setWidth(lw);
43631 this.restrictHeight();
43633 Roo.get(document).on('mousedown', this.collapseIf, this);
43634 Roo.get(document).on('mousewheel', this.collapseIf, this);
43636 this.fireEvent('expand', this);
43639 restrictHeight : function()
43641 this.list.alignTo(this.inputEl(), this.listAlign);
43642 this.list.alignTo(this.inputEl(), this.listAlign);
43645 onViewOver : function(e, t)
43647 if(this.inKeyMode){
43650 var item = this.view.findItemFromChild(t);
43653 var index = this.view.indexOf(item);
43654 this.select(index, false);
43659 onViewClick : function(view, doFocus, el, e)
43661 var index = this.view.getSelectedIndexes()[0];
43663 var r = this.store.getAt(index);
43666 this.onSelect(r, index);
43668 if(doFocus !== false && !this.blockFocus){
43669 this.inputEl().focus();
43673 onViewMove : function(e, t)
43675 this.inKeyMode = false;
43678 select : function(index, scrollIntoView)
43680 this.selectedIndex = index;
43681 this.view.select(index);
43682 if(scrollIntoView !== false){
43683 var el = this.view.getNode(index);
43685 this.list.scrollChildIntoView(el, false);
43690 createList : function()
43692 this.list = Roo.get(document.body).createChild({
43694 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43695 style: 'display:none'
43698 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43701 collapseIf : function(e)
43703 var in_combo = e.within(this.el);
43704 var in_list = e.within(this.list);
43705 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43707 if (in_combo || in_list || is_list) {
43713 onSelect : function(record, index)
43715 if(this.fireEvent('beforeselect', this, record, index) !== false){
43717 this.setFlagClass(record.data.iso2);
43718 this.setDialCode(record.data.dialCode);
43719 this.hasFocus = false;
43721 this.fireEvent('select', this, record, index);
43725 flagEl : function()
43727 var flag = this.el.select('div.flag',true).first();
43734 dialCodeHolderEl : function()
43736 var d = this.el.select('input.dial-code-holder',true).first();
43743 setDialCode : function(v)
43745 this.dialCodeHolder.dom.value = '+'+v;
43748 setFlagClass : function(n)
43750 this.flag.dom.className = 'flag '+n;
43753 getValue : function()
43755 var v = this.inputEl().getValue();
43756 if(this.dialCodeHolder) {
43757 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43762 setValue : function(v)
43764 var d = this.getDialCode(v);
43766 //invalid dial code
43767 if(v.length == 0 || !d || d.length == 0) {
43769 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43770 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43776 this.setFlagClass(this.dialCodeMapping[d].iso2);
43777 this.setDialCode(d);
43778 this.inputEl().dom.value = v.replace('+'+d,'');
43779 this.hiddenEl().dom.value = this.getValue();
43784 getDialCode : function(v)
43788 if (v.length == 0) {
43789 return this.dialCodeHolder.dom.value;
43793 if (v.charAt(0) != "+") {
43796 var numericChars = "";
43797 for (var i = 1; i < v.length; i++) {
43798 var c = v.charAt(i);
43801 if (this.dialCodeMapping[numericChars]) {
43802 dialCode = v.substr(1, i);
43804 if (numericChars.length == 4) {
43814 this.setValue(this.defaultDialCode);
43818 hiddenEl : function()
43820 return this.el.select('input.hidden-tel-input',true).first();
43823 // after setting val
43824 onKeyUp : function(e){
43825 this.setValue(this.getValue());
43828 onKeyPress : function(e){
43829 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43836 * @class Roo.bootstrap.form.MoneyField
43837 * @extends Roo.bootstrap.form.ComboBox
43838 * Bootstrap MoneyField class
43841 * Create a new MoneyField.
43842 * @param {Object} config Configuration options
43845 Roo.bootstrap.form.MoneyField = function(config) {
43847 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43851 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43854 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43856 allowDecimals : true,
43858 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43860 decimalSeparator : ".",
43862 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43864 decimalPrecision : 0,
43866 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43868 allowNegative : true,
43870 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43874 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43876 minValue : Number.NEGATIVE_INFINITY,
43878 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43880 maxValue : Number.MAX_VALUE,
43882 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43884 minText : "The minimum value for this field is {0}",
43886 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43888 maxText : "The maximum value for this field is {0}",
43890 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43891 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43893 nanText : "{0} is not a valid number",
43895 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43899 * @cfg {String} defaults currency of the MoneyField
43900 * value should be in lkey
43902 defaultCurrency : false,
43904 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43906 thousandsDelimiter : false,
43908 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43917 * @cfg {Roo.data.Store} store Store to lookup currency??
43921 getAutoCreate : function()
43923 var align = this.labelAlign || this.parentLabelAlign();
43935 cls : 'form-control roo-money-amount-input',
43936 autocomplete: 'new-password'
43939 var hiddenInput = {
43943 cls: 'hidden-number-input'
43946 if(this.max_length) {
43947 input.maxlength = this.max_length;
43951 hiddenInput.name = this.name;
43954 if (this.disabled) {
43955 input.disabled = true;
43958 var clg = 12 - this.inputlg;
43959 var cmd = 12 - this.inputmd;
43960 var csm = 12 - this.inputsm;
43961 var cxs = 12 - this.inputxs;
43965 cls : 'row roo-money-field',
43969 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43973 cls: 'roo-select2-container input-group',
43977 cls : 'form-control roo-money-currency-input',
43978 autocomplete: 'new-password',
43980 name : this.currencyName
43984 cls : 'input-group-addon',
43998 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44002 cls: this.hasFeedback ? 'has-feedback' : '',
44013 if (this.fieldLabel.length) {
44016 tooltip: 'This field is required'
44022 cls: 'control-label',
44028 html: this.fieldLabel
44031 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44037 if(this.indicatorpos == 'right') {
44038 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44045 if(align == 'left') {
44053 if(this.labelWidth > 12){
44054 label.style = "width: " + this.labelWidth + 'px';
44056 if(this.labelWidth < 13 && this.labelmd == 0){
44057 this.labelmd = this.labelWidth;
44059 if(this.labellg > 0){
44060 label.cls += ' col-lg-' + this.labellg;
44061 input.cls += ' col-lg-' + (12 - this.labellg);
44063 if(this.labelmd > 0){
44064 label.cls += ' col-md-' + this.labelmd;
44065 container.cls += ' col-md-' + (12 - this.labelmd);
44067 if(this.labelsm > 0){
44068 label.cls += ' col-sm-' + this.labelsm;
44069 container.cls += ' col-sm-' + (12 - this.labelsm);
44071 if(this.labelxs > 0){
44072 label.cls += ' col-xs-' + this.labelxs;
44073 container.cls += ' col-xs-' + (12 - this.labelxs);
44084 var settings = this;
44086 ['xs','sm','md','lg'].map(function(size){
44087 if (settings[size]) {
44088 cfg.cls += ' col-' + size + '-' + settings[size];
44095 initEvents : function()
44097 this.indicator = this.indicatorEl();
44099 this.initCurrencyEvent();
44101 this.initNumberEvent();
44104 initCurrencyEvent : function()
44107 throw "can not find store for combo";
44110 this.store = Roo.factory(this.store, Roo.data);
44111 this.store.parent = this;
44115 this.triggerEl = this.el.select('.input-group-addon', true).first();
44117 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44122 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44123 _this.list.setWidth(lw);
44126 this.list.on('mouseover', this.onViewOver, this);
44127 this.list.on('mousemove', this.onViewMove, this);
44128 this.list.on('scroll', this.onViewScroll, this);
44131 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44134 this.view = new Roo.View(this.list, this.tpl, {
44135 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44138 this.view.on('click', this.onViewClick, this);
44140 this.store.on('beforeload', this.onBeforeLoad, this);
44141 this.store.on('load', this.onLoad, this);
44142 this.store.on('loadexception', this.onLoadException, this);
44144 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44145 "up" : function(e){
44146 this.inKeyMode = true;
44150 "down" : function(e){
44151 if(!this.isExpanded()){
44152 this.onTriggerClick();
44154 this.inKeyMode = true;
44159 "enter" : function(e){
44162 if(this.fireEvent("specialkey", this, e)){
44163 this.onViewClick(false);
44169 "esc" : function(e){
44173 "tab" : function(e){
44176 if(this.fireEvent("specialkey", this, e)){
44177 this.onViewClick(false);
44185 doRelay : function(foo, bar, hname){
44186 if(hname == 'down' || this.scope.isExpanded()){
44187 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44195 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44199 initNumberEvent : function(e)
44201 this.inputEl().on("keydown" , this.fireKey, this);
44202 this.inputEl().on("focus", this.onFocus, this);
44203 this.inputEl().on("blur", this.onBlur, this);
44205 this.inputEl().relayEvent('keyup', this);
44207 if(this.indicator){
44208 this.indicator.addClass('invisible');
44211 this.originalValue = this.getValue();
44213 if(this.validationEvent == 'keyup'){
44214 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44215 this.inputEl().on('keyup', this.filterValidation, this);
44217 else if(this.validationEvent !== false){
44218 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44221 if(this.selectOnFocus){
44222 this.on("focus", this.preFocus, this);
44225 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44226 this.inputEl().on("keypress", this.filterKeys, this);
44228 this.inputEl().relayEvent('keypress', this);
44231 var allowed = "0123456789";
44233 if(this.allowDecimals){
44234 allowed += this.decimalSeparator;
44237 if(this.allowNegative){
44241 if(this.thousandsDelimiter) {
44245 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44247 var keyPress = function(e){
44249 var k = e.getKey();
44251 var c = e.getCharCode();
44254 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44255 allowed.indexOf(String.fromCharCode(c)) === -1
44261 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44265 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44270 this.inputEl().on("keypress", keyPress, this);
44274 onTriggerClick : function(e)
44281 this.loadNext = false;
44283 if(this.isExpanded()){
44288 this.hasFocus = true;
44290 if(this.triggerAction == 'all') {
44291 this.doQuery(this.allQuery, true);
44295 this.doQuery(this.getRawValue());
44298 getCurrency : function()
44300 var v = this.currencyEl().getValue();
44305 restrictHeight : function()
44307 this.list.alignTo(this.currencyEl(), this.listAlign);
44308 this.list.alignTo(this.currencyEl(), this.listAlign);
44311 onViewClick : function(view, doFocus, el, e)
44313 var index = this.view.getSelectedIndexes()[0];
44315 var r = this.store.getAt(index);
44318 this.onSelect(r, index);
44322 onSelect : function(record, index){
44324 if(this.fireEvent('beforeselect', this, record, index) !== false){
44326 this.setFromCurrencyData(index > -1 ? record.data : false);
44330 this.fireEvent('select', this, record, index);
44334 setFromCurrencyData : function(o)
44338 this.lastCurrency = o;
44340 if (this.currencyField) {
44341 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44343 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44346 this.lastSelectionText = currency;
44348 //setting default currency
44349 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44350 this.setCurrency(this.defaultCurrency);
44354 this.setCurrency(currency);
44357 setFromData : function(o)
44361 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44363 this.setFromCurrencyData(c);
44368 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44370 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44373 this.setValue(value);
44377 setCurrency : function(v)
44379 this.currencyValue = v;
44382 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44387 setValue : function(v)
44389 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44395 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44397 this.inputEl().dom.value = (v == '') ? '' :
44398 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44400 if(!this.allowZero && v === '0') {
44401 this.hiddenEl().dom.value = '';
44402 this.inputEl().dom.value = '';
44409 getRawValue : function()
44411 var v = this.inputEl().getValue();
44416 getValue : function()
44418 return this.fixPrecision(this.parseValue(this.getRawValue()));
44421 parseValue : function(value)
44423 if(this.thousandsDelimiter) {
44425 r = new RegExp(",", "g");
44426 value = value.replace(r, "");
44429 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44430 return isNaN(value) ? '' : value;
44434 fixPrecision : function(value)
44436 if(this.thousandsDelimiter) {
44438 r = new RegExp(",", "g");
44439 value = value.replace(r, "");
44442 var nan = isNaN(value);
44444 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44445 return nan ? '' : value;
44447 return parseFloat(value).toFixed(this.decimalPrecision);
44450 decimalPrecisionFcn : function(v)
44452 return Math.floor(v);
44455 validateValue : function(value)
44457 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44461 var num = this.parseValue(value);
44464 this.markInvalid(String.format(this.nanText, value));
44468 if(num < this.minValue){
44469 this.markInvalid(String.format(this.minText, this.minValue));
44473 if(num > this.maxValue){
44474 this.markInvalid(String.format(this.maxText, this.maxValue));
44481 validate : function()
44483 if(this.disabled || this.allowBlank){
44488 var currency = this.getCurrency();
44490 if(this.validateValue(this.getRawValue()) && currency.length){
44495 this.markInvalid();
44499 getName: function()
44504 beforeBlur : function()
44510 var v = this.parseValue(this.getRawValue());
44517 onBlur : function()
44521 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44522 //this.el.removeClass(this.focusClass);
44525 this.hasFocus = false;
44527 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44531 var v = this.getValue();
44533 if(String(v) !== String(this.startValue)){
44534 this.fireEvent('change', this, v, this.startValue);
44537 this.fireEvent("blur", this);
44540 inputEl : function()
44542 return this.el.select('.roo-money-amount-input', true).first();
44545 currencyEl : function()
44547 return this.el.select('.roo-money-currency-input', true).first();
44550 hiddenEl : function()
44552 return this.el.select('input.hidden-number-input',true).first();
44556 * @class Roo.bootstrap.BezierSignature
44557 * @extends Roo.bootstrap.Component
44558 * Bootstrap BezierSignature class
44559 * This script refer to:
44560 * Title: Signature Pad
44562 * Availability: https://github.com/szimek/signature_pad
44565 * Create a new BezierSignature
44566 * @param {Object} config The config object
44569 Roo.bootstrap.BezierSignature = function(config){
44570 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44576 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44583 mouse_btn_down: true,
44586 * @cfg {int} canvas height
44588 canvas_height: '200px',
44591 * @cfg {float|function} Radius of a single dot.
44596 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44601 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44606 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44611 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44616 * @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.
44618 bg_color: 'rgba(0, 0, 0, 0)',
44621 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44623 dot_color: 'black',
44626 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44628 velocity_filter_weight: 0.7,
44631 * @cfg {function} Callback when stroke begin.
44636 * @cfg {function} Callback when stroke end.
44640 getAutoCreate : function()
44642 var cls = 'roo-signature column';
44645 cls += ' ' + this.cls;
44655 for(var i = 0; i < col_sizes.length; i++) {
44656 if(this[col_sizes[i]]) {
44657 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44667 cls: 'roo-signature-body',
44671 cls: 'roo-signature-body-canvas',
44672 height: this.canvas_height,
44673 width: this.canvas_width
44680 style: 'display: none'
44688 initEvents: function()
44690 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44692 var canvas = this.canvasEl();
44694 // mouse && touch event swapping...
44695 canvas.dom.style.touchAction = 'none';
44696 canvas.dom.style.msTouchAction = 'none';
44698 this.mouse_btn_down = false;
44699 canvas.on('mousedown', this._handleMouseDown, this);
44700 canvas.on('mousemove', this._handleMouseMove, this);
44701 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44703 if (window.PointerEvent) {
44704 canvas.on('pointerdown', this._handleMouseDown, this);
44705 canvas.on('pointermove', this._handleMouseMove, this);
44706 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44709 if ('ontouchstart' in window) {
44710 canvas.on('touchstart', this._handleTouchStart, this);
44711 canvas.on('touchmove', this._handleTouchMove, this);
44712 canvas.on('touchend', this._handleTouchEnd, this);
44715 Roo.EventManager.onWindowResize(this.resize, this, true);
44717 // file input event
44718 this.fileEl().on('change', this.uploadImage, this);
44725 resize: function(){
44727 var canvas = this.canvasEl().dom;
44728 var ctx = this.canvasElCtx();
44729 var img_data = false;
44731 if(canvas.width > 0) {
44732 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44734 // setting canvas width will clean img data
44737 var style = window.getComputedStyle ?
44738 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44740 var padding_left = parseInt(style.paddingLeft) || 0;
44741 var padding_right = parseInt(style.paddingRight) || 0;
44743 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44746 ctx.putImageData(img_data, 0, 0);
44750 _handleMouseDown: function(e)
44752 if (e.browserEvent.which === 1) {
44753 this.mouse_btn_down = true;
44754 this.strokeBegin(e);
44758 _handleMouseMove: function (e)
44760 if (this.mouse_btn_down) {
44761 this.strokeMoveUpdate(e);
44765 _handleMouseUp: function (e)
44767 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44768 this.mouse_btn_down = false;
44773 _handleTouchStart: function (e) {
44775 e.preventDefault();
44776 if (e.browserEvent.targetTouches.length === 1) {
44777 // var touch = e.browserEvent.changedTouches[0];
44778 // this.strokeBegin(touch);
44780 this.strokeBegin(e); // assume e catching the correct xy...
44784 _handleTouchMove: function (e) {
44785 e.preventDefault();
44786 // var touch = event.targetTouches[0];
44787 // _this._strokeMoveUpdate(touch);
44788 this.strokeMoveUpdate(e);
44791 _handleTouchEnd: function (e) {
44792 var wasCanvasTouched = e.target === this.canvasEl().dom;
44793 if (wasCanvasTouched) {
44794 e.preventDefault();
44795 // var touch = event.changedTouches[0];
44796 // _this._strokeEnd(touch);
44801 reset: function () {
44802 this._lastPoints = [];
44803 this._lastVelocity = 0;
44804 this._lastWidth = (this.min_width + this.max_width) / 2;
44805 this.canvasElCtx().fillStyle = this.dot_color;
44808 strokeMoveUpdate: function(e)
44810 this.strokeUpdate(e);
44812 if (this.throttle) {
44813 this.throttleStroke(this.strokeUpdate, this.throttle);
44816 this.strokeUpdate(e);
44820 strokeBegin: function(e)
44822 var newPointGroup = {
44823 color: this.dot_color,
44827 if (typeof this.onBegin === 'function') {
44831 this.curve_data.push(newPointGroup);
44833 this.strokeUpdate(e);
44836 strokeUpdate: function(e)
44838 var rect = this.canvasEl().dom.getBoundingClientRect();
44839 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44840 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44841 var lastPoints = lastPointGroup.points;
44842 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44843 var isLastPointTooClose = lastPoint
44844 ? point.distanceTo(lastPoint) <= this.min_distance
44846 var color = lastPointGroup.color;
44847 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44848 var curve = this.addPoint(point);
44850 this.drawDot({color: color, point: point});
44853 this.drawCurve({color: color, curve: curve});
44863 strokeEnd: function(e)
44865 this.strokeUpdate(e);
44866 if (typeof this.onEnd === 'function') {
44871 addPoint: function (point) {
44872 var _lastPoints = this._lastPoints;
44873 _lastPoints.push(point);
44874 if (_lastPoints.length > 2) {
44875 if (_lastPoints.length === 3) {
44876 _lastPoints.unshift(_lastPoints[0]);
44878 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44879 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44880 _lastPoints.shift();
44886 calculateCurveWidths: function (startPoint, endPoint) {
44887 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44888 (1 - this.velocity_filter_weight) * this._lastVelocity;
44890 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44893 start: this._lastWidth
44896 this._lastVelocity = velocity;
44897 this._lastWidth = newWidth;
44901 drawDot: function (_a) {
44902 var color = _a.color, point = _a.point;
44903 var ctx = this.canvasElCtx();
44904 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44906 this.drawCurveSegment(point.x, point.y, width);
44908 ctx.fillStyle = color;
44912 drawCurve: function (_a) {
44913 var color = _a.color, curve = _a.curve;
44914 var ctx = this.canvasElCtx();
44915 var widthDelta = curve.endWidth - curve.startWidth;
44916 var drawSteps = Math.floor(curve.length()) * 2;
44918 ctx.fillStyle = color;
44919 for (var i = 0; i < drawSteps; i += 1) {
44920 var t = i / drawSteps;
44926 var x = uuu * curve.startPoint.x;
44927 x += 3 * uu * t * curve.control1.x;
44928 x += 3 * u * tt * curve.control2.x;
44929 x += ttt * curve.endPoint.x;
44930 var y = uuu * curve.startPoint.y;
44931 y += 3 * uu * t * curve.control1.y;
44932 y += 3 * u * tt * curve.control2.y;
44933 y += ttt * curve.endPoint.y;
44934 var width = curve.startWidth + ttt * widthDelta;
44935 this.drawCurveSegment(x, y, width);
44941 drawCurveSegment: function (x, y, width) {
44942 var ctx = this.canvasElCtx();
44944 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44945 this.is_empty = false;
44950 var ctx = this.canvasElCtx();
44951 var canvas = this.canvasEl().dom;
44952 ctx.fillStyle = this.bg_color;
44953 ctx.clearRect(0, 0, canvas.width, canvas.height);
44954 ctx.fillRect(0, 0, canvas.width, canvas.height);
44955 this.curve_data = [];
44957 this.is_empty = true;
44962 return this.el.select('input',true).first();
44965 canvasEl: function()
44967 return this.el.select('canvas',true).first();
44970 canvasElCtx: function()
44972 return this.el.select('canvas',true).first().dom.getContext('2d');
44975 getImage: function(type)
44977 if(this.is_empty) {
44982 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44985 drawFromImage: function(img_src)
44987 var img = new Image();
44989 img.onload = function(){
44990 this.canvasElCtx().drawImage(img, 0, 0);
44995 this.is_empty = false;
44998 selectImage: function()
45000 this.fileEl().dom.click();
45003 uploadImage: function(e)
45005 var reader = new FileReader();
45007 reader.onload = function(e){
45008 var img = new Image();
45009 img.onload = function(){
45011 this.canvasElCtx().drawImage(img, 0, 0);
45013 img.src = e.target.result;
45016 reader.readAsDataURL(e.target.files[0]);
45019 // Bezier Point Constructor
45020 Point: (function () {
45021 function Point(x, y, time) {
45024 this.time = time || Date.now();
45026 Point.prototype.distanceTo = function (start) {
45027 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45029 Point.prototype.equals = function (other) {
45030 return this.x === other.x && this.y === other.y && this.time === other.time;
45032 Point.prototype.velocityFrom = function (start) {
45033 return this.time !== start.time
45034 ? this.distanceTo(start) / (this.time - start.time)
45041 // Bezier Constructor
45042 Bezier: (function () {
45043 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45044 this.startPoint = startPoint;
45045 this.control2 = control2;
45046 this.control1 = control1;
45047 this.endPoint = endPoint;
45048 this.startWidth = startWidth;
45049 this.endWidth = endWidth;
45051 Bezier.fromPoints = function (points, widths, scope) {
45052 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45053 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45054 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45056 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45057 var dx1 = s1.x - s2.x;
45058 var dy1 = s1.y - s2.y;
45059 var dx2 = s2.x - s3.x;
45060 var dy2 = s2.y - s3.y;
45061 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45062 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45063 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45064 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45065 var dxm = m1.x - m2.x;
45066 var dym = m1.y - m2.y;
45067 var k = l2 / (l1 + l2);
45068 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45069 var tx = s2.x - cm.x;
45070 var ty = s2.y - cm.y;
45072 c1: new scope.Point(m1.x + tx, m1.y + ty),
45073 c2: new scope.Point(m2.x + tx, m2.y + ty)
45076 Bezier.prototype.length = function () {
45081 for (var i = 0; i <= steps; i += 1) {
45083 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45084 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45086 var xdiff = cx - px;
45087 var ydiff = cy - py;
45088 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45095 Bezier.prototype.point = function (t, start, c1, c2, end) {
45096 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45097 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45098 + (3.0 * c2 * (1.0 - t) * t * t)
45099 + (end * t * t * t);
45104 throttleStroke: function(fn, wait) {
45105 if (wait === void 0) { wait = 250; }
45107 var timeout = null;
45111 var later = function () {
45112 previous = Date.now();
45114 result = fn.apply(storedContext, storedArgs);
45116 storedContext = null;
45120 return function wrapper() {
45122 for (var _i = 0; _i < arguments.length; _i++) {
45123 args[_i] = arguments[_i];
45125 var now = Date.now();
45126 var remaining = wait - (now - previous);
45127 storedContext = this;
45129 if (remaining <= 0 || remaining > wait) {
45131 clearTimeout(timeout);
45135 result = fn.apply(storedContext, storedArgs);
45137 storedContext = null;
45141 else if (!timeout) {
45142 timeout = window.setTimeout(later, remaining);
45152 // old names for form elements
45153 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45154 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45155 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45156 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45157 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45158 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45159 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45160 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45161 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45162 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45163 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45164 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45165 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45166 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45167 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45168 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45169 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45170 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45171 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45172 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45173 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45174 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45175 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45176 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45177 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45178 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45180 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45181 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45183 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45184 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45186 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45187 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45188 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45189 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator