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;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
100 * @cfg {String} offset
101 * The number of pixels to offset the shadow from the element (defaults to 4)
109 * Displays the shadow under the target element
110 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
112 show : function(target){
113 target = Roo.get(target);
115 this.el = Roo.Shadow.Pool.pull();
116 if(this.el.dom.nextSibling != target.dom){
117 this.el.insertBefore(target);
120 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
122 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
125 target.getLeft(true),
130 this.el.dom.style.display = "block";
134 * Returns true if the shadow is visible, else false
136 isVisible : function(){
137 return this.el ? true : false;
141 * Direct alignment when values are already available. Show must be called at least once before
142 * calling this method to ensure it is initialized.
143 * @param {Number} left The target element left position
144 * @param {Number} top The target element top position
145 * @param {Number} width The target element width
146 * @param {Number} height The target element height
148 realign : function(l, t, w, h){
152 var a = this.adjusts, d = this.el.dom, s = d.style;
154 s.left = (l+a.l)+"px";
155 s.top = (t+a.t)+"px";
156 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
158 if(s.width != sws || s.height != shs){
162 var cn = d.childNodes;
163 var sww = Math.max(0, (sw-12))+"px";
164 cn[0].childNodes[1].style.width = sww;
165 cn[1].childNodes[1].style.width = sww;
166 cn[2].childNodes[1].style.width = sww;
167 cn[1].style.height = Math.max(0, (sh-12))+"px";
177 this.el.dom.style.display = "none";
178 Roo.Shadow.Pool.push(this.el);
184 * Adjust the z-index of this shadow
185 * @param {Number} zindex The new z-index
187 setZIndex : function(z){
190 this.el.setStyle("z-index", z);
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
198 var markup = Roo.isIE ?
199 '<div class="x-ie-shadow"></div>' :
200 '<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>';
205 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206 sh.autoBoxAdjust = false;
218 * base class for bootstrap elements.
222 Roo.bootstrap = Roo.bootstrap || {};
224 * @class Roo.bootstrap.Component
225 * @extends Roo.Component
227 * @children Roo.bootstrap.Component
228 * Bootstrap Component base class
229 * @cfg {String} cls css class
230 * @cfg {String} style any extra css
231 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
233 * @cfg {string} dataId cutomer id
234 * @cfg {string} name Specifies name attribute
235 * @cfg {string} tooltip Text for the tooltip
236 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
237 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
240 * Do not use directly - it does not do anything..
241 * @param {Object} config The config object
246 Roo.bootstrap.Component = function(config){
247 Roo.bootstrap.Component.superclass.constructor.call(this, config);
251 * @event childrenrendered
252 * Fires when the children have been rendered..
253 * @param {Roo.bootstrap.Component} this
255 "childrenrendered" : true
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
267 allowDomMove : false, // to stop relocations in parent onRender...
277 * Initialize Events for the element
279 initEvents : function() { },
285 can_build_overlaid : true,
287 container_method : false,
294 // returns the parent component..
295 return Roo.ComponentMgr.get(this.parentId)
301 onRender : function(ct, position)
303 // Roo.log("Call onRender: " + this.xtype);
305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
308 if (this.el.attr('xtype')) {
309 this.el.attr('xtypex', this.el.attr('xtype'));
310 this.el.dom.removeAttribute('xtype');
320 var cfg = Roo.apply({}, this.getAutoCreate());
322 cfg.id = this.id || Roo.id();
324 // fill in the extra attributes
325 if (this.xattr && typeof(this.xattr) =='object') {
326 for (var i in this.xattr) {
327 cfg[i] = this.xattr[i];
332 cfg.dataId = this.dataId;
336 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
339 if (this.style) { // fixme needs to support more complex style data.
340 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344 cfg.name = this.name;
347 this.el = ct.createChild(cfg, position);
350 this.tooltipEl().attr('tooltip', this.tooltip);
353 if(this.tabIndex !== undefined){
354 this.el.dom.setAttribute('tabIndex', this.tabIndex);
361 * Fetch the element to add children to
362 * @return {Roo.Element} defaults to this.el
364 getChildContainer : function()
368 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
370 return Roo.get(document.body);
374 * Fetch the element to display the tooltip on.
375 * @return {Roo.Element} defaults to this.el
377 tooltipEl : function()
382 addxtype : function(tree,cntr)
386 cn = Roo.factory(tree);
387 //Roo.log(['addxtype', cn]);
389 cn.parentType = this.xtype; //??
390 cn.parentId = this.id;
392 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393 if (typeof(cn.container_method) == 'string') {
394 cntr = cn.container_method;
398 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
400 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
402 var build_from_html = Roo.XComponent.build_from_html;
404 var is_body = (tree.xtype == 'Body') ;
406 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
408 var self_cntr_el = Roo.get(this[cntr](false));
410 // do not try and build conditional elements
411 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417 return this.addxtypeChild(tree,cntr, is_body);
420 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
423 return this.addxtypeChild(Roo.apply({}, tree),cntr);
426 Roo.log('skipping render');
432 if (!build_from_html) {
436 // this i think handles overlaying multiple children of the same type
437 // with the sam eelement.. - which might be buggy..
439 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
445 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456 addxtypeChild : function (tree, cntr, is_body)
458 Roo.debug && Roo.log('addxtypeChild:' + cntr);
460 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
463 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464 (typeof(tree['flexy:foreach']) != 'undefined');
468 skip_children = false;
469 // render the element if it's not BODY.
472 // if parent was disabled, then do not try and create the children..
473 if(!this[cntr](true)){
478 cn = Roo.factory(tree);
480 cn.parentType = this.xtype; //??
481 cn.parentId = this.id;
483 var build_from_html = Roo.XComponent.build_from_html;
486 // does the container contain child eleemnts with 'xtype' attributes.
487 // that match this xtype..
488 // note - when we render we create these as well..
489 // so we should check to see if body has xtype set.
490 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
492 var self_cntr_el = Roo.get(this[cntr](false));
493 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
495 //Roo.log(Roo.XComponent.build_from_html);
496 //Roo.log("got echild:");
499 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500 // and are not displayed -this causes this to use up the wrong element when matching.
501 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
504 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
511 //echild.dom.removeAttribute('xtype');
513 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514 Roo.debug && Roo.log(self_cntr_el);
515 Roo.debug && Roo.log(echild);
516 Roo.debug && Roo.log(cn);
522 // if object has flexy:if - then it may or may not be rendered.
523 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
524 // skip a flexy if element.
525 Roo.debug && Roo.log('skipping render');
526 Roo.debug && Roo.log(tree);
528 Roo.debug && Roo.log('skipping all children');
529 skip_children = true;
534 // actually if flexy:foreach is found, we really want to create
535 // multiple copies here...
537 //Roo.log(this[cntr]());
538 // some elements do not have render methods.. like the layouts...
540 if(this[cntr](true) === false){
545 cn.render && cn.render(this[cntr](true));
548 // then add the element..
555 if (typeof (tree.menu) != 'undefined') {
556 tree.menu.parentType = cn.xtype;
557 tree.menu.triggerEl = cn.el;
558 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562 if (!tree.items || !tree.items.length) {
564 //Roo.log(["no children", this]);
569 var items = tree.items;
572 //Roo.log(items.length);
574 if (!skip_children) {
575 for(var i =0;i < items.length;i++) {
576 // Roo.log(['add child', items[i]]);
577 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
583 //Roo.log("fire childrenrendered");
585 cn.fireEvent('childrenrendered', this);
591 * Set the element that will be used to show or hide
593 setVisibilityEl : function(el)
595 this.visibilityEl = el;
599 * Get the element that will be used to show or hide
601 getVisibilityEl : function()
603 if (typeof(this.visibilityEl) == 'object') {
604 return this.visibilityEl;
607 if (typeof(this.visibilityEl) == 'string') {
608 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615 * Show a component - removes 'hidden' class
619 if(!this.getVisibilityEl()){
623 this.getVisibilityEl().removeClass(['hidden','d-none']);
625 this.fireEvent('show', this);
630 * Hide a component - adds 'hidden' class
634 if(!this.getVisibilityEl()){
638 this.getVisibilityEl().addClass(['hidden','d-none']);
640 this.fireEvent('hide', this);
653 * @class Roo.bootstrap.Element
654 * @extends Roo.bootstrap.Component
655 * @children Roo.bootstrap.Component
656 * Bootstrap Element class (basically a DIV used to make random stuff )
658 * @cfg {String} html contents of the element
659 * @cfg {String} tag tag of the element
660 * @cfg {String} cls class of the element
661 * @cfg {Boolean} preventDefault (true|false) default false
662 * @cfg {Boolean} clickable (true|false) default false
663 * @cfg {String} role default blank - set to button to force cursor pointer
667 * Create a new Element
668 * @param {Object} config The config object
671 Roo.bootstrap.Element = function(config){
672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
678 * When a element is chick
679 * @param {Roo.bootstrap.Element} this
680 * @param {Roo.EventObject} e
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
693 preventDefault: false,
698 getAutoCreate : function(){
702 // cls: this.cls, double assign in parent class Component.js :: onRender
705 if (this.role !== false) {
706 cfg.role = this.role;
712 initEvents: function()
714 Roo.bootstrap.Element.superclass.initEvents.call(this);
717 this.el.on('click', this.onClick, this);
723 onClick : function(e)
725 if(this.preventDefault){
729 this.fireEvent('click', this, e); // why was this double click before?
737 getValue : function()
739 return this.el.dom.innerHTML;
742 setValue : function(value)
744 this.el.dom.innerHTML = value;
759 * @class Roo.bootstrap.DropTarget
760 * @extends Roo.bootstrap.Element
761 * Bootstrap DropTarget class
763 * @cfg {string} name dropable name
766 * Create a new Dropable Area
767 * @param {Object} config The config object
770 Roo.bootstrap.DropTarget = function(config){
771 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
777 * When a element is chick
778 * @param {Roo.bootstrap.Element} this
779 * @param {Roo.EventObject} e
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
788 getAutoCreate : function(){
793 initEvents: function()
795 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
799 drop : this.dragDrop.createDelegate(this),
800 enter : this.dragEnter.createDelegate(this),
801 out : this.dragOut.createDelegate(this),
802 over : this.dragOver.createDelegate(this)
806 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
809 dragDrop : function(source,e,data)
811 // user has to decide how to impliment this.
814 //this.fireEvent('drop', this, source, e ,data);
818 dragEnter : function(n, dd, e, data)
820 // probably want to resize the element to match the dropped element..
822 this.originalSize = this.el.getSize();
823 this.el.setSize( n.el.getSize());
824 this.dropZone.DDM.refreshCache(this.name);
825 Roo.log([n, dd, e, data]);
828 dragOut : function(value)
830 // resize back to normal
832 this.el.setSize(this.originalSize);
833 this.dropZone.resetConstraints();
836 dragOver : function()
853 * @class Roo.bootstrap.Body
854 * @extends Roo.bootstrap.Component
856 * @children Roo.bootstrap.Component
858 * Bootstrap Body class
862 * @param {Object} config The config object
865 Roo.bootstrap.Body = function(config){
867 config = config || {};
869 Roo.bootstrap.Body.superclass.constructor.call(this, config);
870 this.el = Roo.get(config.el ? config.el : document.body );
871 if (this.cls && this.cls.length) {
872 Roo.get(document.body).addClass(this.cls);
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
878 is_body : true,// just to make sure it's constructed?
883 onRender : function(ct, position)
885 /* Roo.log("Roo.bootstrap.Body - onRender");
886 if (this.cls && this.cls.length) {
887 Roo.get(document.body).addClass(this.cls);
906 * @class Roo.bootstrap.ButtonGroup
907 * @extends Roo.bootstrap.Component
908 * Bootstrap ButtonGroup class
909 * @children Roo.bootstrap.Button Roo.bootstrap.Form
911 * @cfg {String} size lg | sm | xs (default empty normal)
912 * @cfg {String} align vertical | justified (default none)
913 * @cfg {String} direction up | down (default down)
914 * @cfg {Boolean} toolbar false | true
915 * @cfg {Boolean} btn true | false
920 * @param {Object} config The config object
923 Roo.bootstrap.ButtonGroup = function(config){
924 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
935 getAutoCreate : function(){
941 cfg.html = this.html || cfg.html;
952 if (['vertical','justified'].indexOf(this.align)!==-1) {
953 cfg.cls = 'btn-group-' + this.align;
955 if (this.align == 'justified') {
956 console.log(this.items);
960 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961 cfg.cls += ' btn-group-' + this.size;
964 if (this.direction == 'up') {
965 cfg.cls += ' dropup' ;
971 * Add a button to the group (similar to NavItem API.)
973 addItem : function(cfg)
975 var cn = new Roo.bootstrap.Button(cfg);
977 cn.parentId = this.id;
978 cn.onRender(this.el, null);
992 * @class Roo.bootstrap.Button
993 * @extends Roo.bootstrap.Component
994 * Bootstrap Button class
995 * @cfg {String} html The button content
996 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999 * @cfg {String} size (lg|sm|xs)
1000 * @cfg {String} tag (a|input|submit)
1001 * @cfg {String} href empty or href
1002 * @cfg {Boolean} disabled default false;
1003 * @cfg {Boolean} isClose default false;
1004 * @cfg {String} glyphicon depricated - use fa
1005 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006 * @cfg {String} badge text for badge
1007 * @cfg {String} theme (default|glow)
1008 * @cfg {Boolean} inverse dark themed version
1009 * @cfg {Boolean} toggle is it a slidy toggle button
1010 * @cfg {Boolean} pressed default null - if the button ahs active state
1011 * @cfg {String} ontext text for on slidy toggle state
1012 * @cfg {String} offtext text for off slidy toggle state
1013 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1014 * @cfg {Boolean} removeClass remove the standard class..
1015 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1016 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1020 * Create a new button
1021 * @param {Object} config The config object
1025 Roo.bootstrap.Button = function(config){
1026 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1032 * When a button is pressed
1033 * @param {Roo.bootstrap.Button} btn
1034 * @param {Roo.EventObject} e
1039 * When a button is double clicked
1040 * @param {Roo.bootstrap.Button} btn
1041 * @param {Roo.EventObject} e
1046 * After the button has been toggles
1047 * @param {Roo.bootstrap.Button} btn
1048 * @param {Roo.EventObject} e
1049 * @param {boolean} pressed (also available as button.pressed)
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1076 preventDefault: true,
1085 getAutoCreate : function(){
1093 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095 this.tag = 'button';
1099 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1101 if (this.toggle == true) {
1104 cls: 'slider-frame roo-button',
1108 'data-on-text':'ON',
1109 'data-off-text':'OFF',
1110 cls: 'slider-button',
1115 // why are we validating the weights?
1116 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117 cfg.cls += ' ' + this.weight;
1124 cfg.cls += ' close';
1126 cfg["aria-hidden"] = true;
1128 cfg.html = "×";
1134 if (this.theme==='default') {
1135 cfg.cls = 'btn roo-button';
1137 //if (this.parentType != 'Navbar') {
1138 this.weight = this.weight.length ? this.weight : 'default';
1140 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1142 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144 cfg.cls += ' btn-' + outline + weight;
1145 if (this.weight == 'default') {
1147 cfg.cls += ' btn-' + this.weight;
1150 } else if (this.theme==='glow') {
1153 cfg.cls = 'btn-glow roo-button';
1155 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1157 cfg.cls += ' ' + this.weight;
1163 this.cls += ' inverse';
1167 if (this.active || this.pressed === true) {
1168 cfg.cls += ' active';
1171 if (this.disabled) {
1172 cfg.disabled = 'disabled';
1176 Roo.log('changing to ul' );
1178 this.glyphicon = 'caret';
1179 if (Roo.bootstrap.version == 4) {
1180 this.fa = 'caret-down';
1185 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1187 //gsRoo.log(this.parentType);
1188 if (this.parentType === 'Navbar' && !this.parent().bar) {
1189 Roo.log('changing to li?');
1198 href : this.href || '#'
1201 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1202 cfg.cls += ' dropdown';
1209 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1211 if (this.glyphicon) {
1212 cfg.html = ' ' + cfg.html;
1217 cls: 'glyphicon glyphicon-' + this.glyphicon
1222 cfg.html = ' ' + cfg.html;
1227 cls: 'fa fas fa-' + this.fa
1237 // cfg.cls='btn roo-button';
1241 var value = cfg.html;
1246 cls: 'glyphicon glyphicon-' + this.glyphicon,
1253 cls: 'fa fas fa-' + this.fa,
1258 var bw = this.badge_weight.length ? this.badge_weight :
1259 (this.weight.length ? this.weight : 'secondary');
1260 bw = bw == 'default' ? 'secondary' : bw;
1266 cls: 'badge badge-' + bw,
1275 cfg.cls += ' dropdown';
1276 cfg.html = typeof(cfg.html) != 'undefined' ?
1277 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280 if (cfg.tag !== 'a' && this.href !== '') {
1281 throw "Tag must be a to set href.";
1282 } else if (this.href.length > 0) {
1283 cfg.href = this.href;
1286 if(this.removeClass){
1291 cfg.target = this.target;
1296 initEvents: function() {
1297 // Roo.log('init events?');
1298 // Roo.log(this.el.dom);
1301 if (typeof (this.menu) != 'undefined') {
1302 this.menu.parentType = this.xtype;
1303 this.menu.triggerEl = this.el;
1304 this.addxtype(Roo.apply({}, this.menu));
1308 if (this.el.hasClass('roo-button')) {
1309 this.el.on('click', this.onClick, this);
1310 this.el.on('dblclick', this.onDblClick, this);
1312 this.el.select('.roo-button').on('click', this.onClick, this);
1313 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317 if(this.removeClass){
1318 this.el.on('click', this.onClick, this);
1321 if (this.group === true) {
1322 if (this.pressed === false || this.pressed === true) {
1325 this.pressed = false;
1326 this.setActive(this.pressed);
1331 this.el.enableDisplayMode();
1334 onClick : function(e)
1336 if (this.disabled) {
1340 Roo.log('button on click ');
1341 if(this.preventDefault){
1350 this.setActive(true);
1351 var pi = this.parent().items;
1352 for (var i = 0;i < pi.length;i++) {
1353 if (this == pi[i]) {
1356 if (pi[i].el.hasClass('roo-button')) {
1357 pi[i].setActive(false);
1360 this.fireEvent('click', this, e);
1364 if (this.pressed === true || this.pressed === false) {
1365 this.toggleActive(e);
1369 this.fireEvent('click', this, e);
1371 onDblClick: function(e)
1373 if (this.disabled) {
1376 if(this.preventDefault){
1379 this.fireEvent('dblclick', this, e);
1382 * Enables this button
1386 this.disabled = false;
1387 this.el.removeClass('disabled');
1388 this.el.dom.removeAttribute("disabled");
1392 * Disable this button
1394 disable : function()
1396 this.disabled = true;
1397 this.el.addClass('disabled');
1398 this.el.attr("disabled", "disabled")
1401 * sets the active state on/off,
1402 * @param {Boolean} state (optional) Force a particular state
1404 setActive : function(v) {
1406 this.el[v ? 'addClass' : 'removeClass']('active');
1410 * toggles the current active state
1412 toggleActive : function(e)
1414 this.setActive(!this.pressed); // this modifies pressed...
1415 this.fireEvent('toggle', this, e, this.pressed);
1418 * get the current active state
1419 * @return {boolean} true if it's active
1421 isActive : function()
1423 return this.el.hasClass('active');
1426 * set the text of the first selected button
1428 setText : function(str)
1430 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433 * get the text of the first selected button
1435 getText : function()
1437 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440 setWeight : function(str)
1442 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1445 var outline = this.outline ? 'outline-' : '';
1446 if (str == 'default') {
1447 this.el.addClass('btn-default btn-outline-secondary');
1450 this.el.addClass('btn-' + outline + str);
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1457 Roo.bootstrap.Button.weights = [
1477 * @class Roo.bootstrap.Column
1478 * @extends Roo.bootstrap.Component
1479 * @children Roo.bootstrap.Component
1480 * Bootstrap Column class
1481 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491 * @cfg {Boolean} hidden (true|false) hide the element
1492 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493 * @cfg {String} fa (ban|check|...) font awesome icon
1494 * @cfg {Number} fasize (1|2|....) font awsome size
1496 * @cfg {String} icon (info-sign|check|...) glyphicon name
1498 * @cfg {String} html content of column.
1501 * Create a new Column
1502 * @param {Object} config The config object
1505 Roo.bootstrap.Column = function(config){
1506 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1527 getAutoCreate : function(){
1528 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1536 var sizes = ['xs','sm','md','lg'];
1537 sizes.map(function(size ,ix){
1538 //Roo.log( size + ':' + settings[size]);
1540 if (settings[size+'off'] !== false) {
1541 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544 if (settings[size] === false) {
1548 if (!settings[size]) { // 0 = hidden
1549 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1551 for (var i = ix; i > -1; i--) {
1552 cfg.cls += ' d-' + sizes[i] + '-none';
1558 cfg.cls += ' col-' + size + '-' + settings[size] + (
1559 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1565 cfg.cls += ' hidden';
1568 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569 cfg.cls +=' alert alert-' + this.alert;
1573 if (this.html.length) {
1574 cfg.html = this.html;
1578 if (this.fasize > 1) {
1579 fasize = ' fa-' + this.fasize + 'x';
1581 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1586 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1605 * @class Roo.bootstrap.Container
1606 * @extends Roo.bootstrap.Component
1608 * @children Roo.bootstrap.Component
1609 * Bootstrap Container class
1610 * @cfg {Boolean} jumbotron is it a jumbotron element
1611 * @cfg {String} html content of element
1612 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1614 * @cfg {String} header content of header (for panel)
1615 * @cfg {String} footer content of footer (for panel)
1616 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617 * @cfg {String} tag (header|aside|section) type of HTML tag.
1618 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619 * @cfg {String} fa font awesome icon
1620 * @cfg {String} icon (info-sign|check|...) glyphicon name
1621 * @cfg {Boolean} hidden (true|false) hide the element
1622 * @cfg {Boolean} expandable (true|false) default false
1623 * @cfg {Boolean} expanded (true|false) default true
1624 * @cfg {String} rheader contet on the right of header
1625 * @cfg {Boolean} clickable (true|false) default false
1629 * Create a new Container
1630 * @param {Object} config The config object
1633 Roo.bootstrap.Container = function(config){
1634 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1640 * After the panel has been expand
1642 * @param {Roo.bootstrap.Container} this
1647 * After the panel has been collapsed
1649 * @param {Roo.bootstrap.Container} this
1654 * When a element is chick
1655 * @param {Roo.bootstrap.Container} this
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1680 getChildContainer : function() {
1686 if (this.panel.length) {
1687 return this.el.select('.panel-body',true).first();
1694 getAutoCreate : function(){
1697 tag : this.tag || 'div',
1701 if (this.jumbotron) {
1702 cfg.cls = 'jumbotron';
1707 // - this is applied by the parent..
1709 // cfg.cls = this.cls + '';
1712 if (this.sticky.length) {
1714 var bd = Roo.get(document.body);
1715 if (!bd.hasClass('bootstrap-sticky')) {
1716 bd.addClass('bootstrap-sticky');
1717 Roo.select('html',true).setStyle('height', '100%');
1720 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724 if (this.well.length) {
1725 switch (this.well) {
1728 cfg.cls +=' well well-' +this.well;
1737 cfg.cls += ' hidden';
1741 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742 cfg.cls +=' alert alert-' + this.alert;
1747 if (this.panel.length) {
1748 cfg.cls += ' panel panel-' + this.panel;
1750 if (this.header.length) {
1754 if(this.expandable){
1756 cfg.cls = cfg.cls + ' expandable';
1760 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1768 cls : 'panel-title',
1769 html : (this.expandable ? ' ' : '') + this.header
1773 cls: 'panel-header-right',
1779 cls : 'panel-heading',
1780 style : this.expandable ? 'cursor: pointer' : '',
1788 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1793 if (this.footer.length) {
1795 cls : 'panel-footer',
1804 body.html = this.html || cfg.html;
1805 // prefix with the icons..
1807 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1815 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816 cfg.cls = 'container';
1822 initEvents: function()
1824 if(this.expandable){
1825 var headerEl = this.headerEl();
1828 headerEl.on('click', this.onToggleClick, this);
1833 this.el.on('click', this.onClick, this);
1838 onToggleClick : function()
1840 var headerEl = this.headerEl();
1856 if(this.fireEvent('expand', this)) {
1858 this.expanded = true;
1860 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1862 this.el.select('.panel-body',true).first().removeClass('hide');
1864 var toggleEl = this.toggleEl();
1870 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1875 collapse : function()
1877 if(this.fireEvent('collapse', this)) {
1879 this.expanded = false;
1881 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882 this.el.select('.panel-body',true).first().addClass('hide');
1884 var toggleEl = this.toggleEl();
1890 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894 toggleEl : function()
1896 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900 return this.el.select('.panel-heading .fa',true).first();
1903 headerEl : function()
1905 if(!this.el || !this.panel.length || !this.header.length){
1909 return this.el.select('.panel-heading',true).first()
1914 if(!this.el || !this.panel.length){
1918 return this.el.select('.panel-body',true).first()
1921 titleEl : function()
1923 if(!this.el || !this.panel.length || !this.header.length){
1927 return this.el.select('.panel-title',true).first();
1930 setTitle : function(v)
1932 var titleEl = this.titleEl();
1938 titleEl.dom.innerHTML = v;
1941 getTitle : function()
1944 var titleEl = this.titleEl();
1950 return titleEl.dom.innerHTML;
1953 setRightTitle : function(v)
1955 var t = this.el.select('.panel-header-right',true).first();
1961 t.dom.innerHTML = v;
1964 onClick : function(e)
1968 this.fireEvent('click', this, e);
1973 * @class Roo.bootstrap.Card
1974 * @extends Roo.bootstrap.Component
1975 * @children Roo.bootstrap.Component
1977 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980 * possible... may not be implemented..
1981 * @cfg {String} header_image src url of image.
1982 * @cfg {String|Object} header
1983 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1986 * @cfg {String} title
1987 * @cfg {String} subtitle
1988 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989 * @cfg {String} footer
1991 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1993 * @cfg {String} margin (0|1|2|3|4|5|auto)
1994 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2001 * @cfg {String} padding (0|1|2|3|4|5)
2002 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004 * @cfg {String} padding_left (0|1|2|3|4|5)
2005 * @cfg {String} padding_right (0|1|2|3|4|5)
2006 * @cfg {String} padding_x (0|1|2|3|4|5)
2007 * @cfg {String} padding_y (0|1|2|3|4|5)
2009 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @config {Boolean} dragable if this card can be dragged.
2016 * @config {String} drag_group group for drag
2017 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2018 * @config {String} drop_group group for drag
2020 * @config {Boolean} collapsable can the body be collapsed.
2021 * @config {Boolean} collapsed is the body collapsed when rendered...
2022 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023 * @config {Boolean} rotated is the body rotated when rendered...
2026 * Create a new Container
2027 * @param {Object} config The config object
2030 Roo.bootstrap.Card = function(config){
2031 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2037 * When a element a card is dropped
2038 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042 * @param {String} position 'above' or 'below'
2043 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2049 * When a element a card is rotate
2050 * @param {Roo.bootstrap.Card} this
2051 * @param {Roo.Element} n the node being dropped?
2052 * @param {Boolean} rotate status
2057 * When a card element is dragged over ready to drop (return false to block dropable)
2058 * @param {Roo.bootstrap.Card} this
2059 * @param {Object} data from dragdrop
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2072 margin: '', /// may be better in component?
2102 collapsable : false,
2111 childContainer : false,
2112 dropEl : false, /// the dom placeholde element that indicates drop location.
2113 containerEl: false, // body container
2114 bodyEl: false, // card-body
2115 headerContainerEl : false, //
2117 header_imageEl : false,
2120 layoutCls : function()
2124 Roo.log(this.margin_bottom.length);
2125 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2128 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2131 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2136 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142 // more generic support?
2150 // Roo.log("Call onRender: " + this.xtype);
2151 /* We are looking at something like this.
2153 <img src="..." class="card-img-top" alt="...">
2154 <div class="card-body">
2155 <h5 class="card-title">Card title</h5>
2156 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2158 >> this bit is really the body...
2159 <div> << we will ad dthis in hopefully it will not break shit.
2161 ** card text does not actually have any styling...
2163 <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>
2166 <a href="#" class="card-link">Card link</a>
2169 <div class="card-footer">
2170 <small class="text-muted">Last updated 3 mins ago</small>
2174 getAutoCreate : function(){
2182 if (this.weight.length && this.weight != 'light') {
2183 cfg.cls += ' text-white';
2185 cfg.cls += ' text-dark'; // need as it's nested..
2187 if (this.weight.length) {
2188 cfg.cls += ' bg-' + this.weight;
2191 cfg.cls += ' ' + this.layoutCls();
2194 var hdr_ctr = false;
2195 if (this.header.length) {
2197 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2206 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212 if (this.collapsable) {
2215 cls : 'd-block user-select-none',
2219 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2224 hdr.cn.push(hdr_ctr);
2229 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2234 if (this.header_image.length) {
2237 cls : 'card-img-top',
2238 src: this.header_image // escape?
2243 cls : 'card-img-top d-none'
2249 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2253 if (this.collapsable || this.rotateable) {
2256 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2263 if (this.title.length) {
2267 src: this.title // escape?
2271 if (this.subtitle.length) {
2275 src: this.subtitle // escape?
2281 cls : 'roo-card-body-ctr'
2284 if (this.html.length) {
2290 // fixme ? handle objects?
2292 if (this.footer.length) {
2295 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2300 cfg.cn.push({cls : 'card-footer d-none'});
2309 getCardHeader : function()
2311 var ret = this.el.select('.card-header',true).first();
2312 if (ret.hasClass('d-none')) {
2313 ret.removeClass('d-none');
2318 getCardFooter : function()
2320 var ret = this.el.select('.card-footer',true).first();
2321 if (ret.hasClass('d-none')) {
2322 ret.removeClass('d-none');
2327 getCardImageTop : function()
2329 var ret = this.header_imageEl;
2330 if (ret.hasClass('d-none')) {
2331 ret.removeClass('d-none');
2337 getChildContainer : function()
2343 return this.el.select('.roo-card-body-ctr',true).first();
2346 initEvents: function()
2348 this.bodyEl = this.el.select('.card-body',true).first();
2349 this.containerEl = this.getChildContainer();
2351 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352 containerScroll: true,
2353 ddGroup: this.drag_group || 'default_card_drag_group'
2355 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2357 if (this.dropable) {
2358 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359 containerScroll: true,
2360 ddGroup: this.drop_group || 'default_card_drag_group'
2362 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369 if (this.collapsable) {
2370 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2372 if (this.rotateable) {
2373 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2375 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2377 this.footerEl = this.el.select('.card-footer',true).first();
2378 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380 this.headerEl = this.el.select('.card-header',true).first();
2383 this.el.addClass('roo-card-rotated');
2384 this.fireEvent('rotate', this, true);
2386 this.header_imageEl = this.el.select('.card-img-top',true).first();
2387 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390 getDragData : function(e)
2392 var target = this.getEl();
2394 //this.handleSelection(e);
2399 nodes: this.getEl(),
2404 dragData.ddel = target.dom ; // the div element
2405 Roo.log(target.getWidth( ));
2406 dragData.ddel.style.width = target.getWidth() + 'px';
2413 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2414 * whole Element becomes the target, and this causes the drop gesture to append.
2416 * Returns an object:
2419 position : 'below' or 'above'
2420 card : relateive to card OBJECT (or true for no cards listed)
2421 items_n : relative to nth item in list
2422 card_n : relative to nth card in list
2427 getTargetFromEvent : function(e, dragged_card_el)
2429 var target = e.getTarget();
2430 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431 target = target.parentNode;
2442 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443 // see if target is one of the 'cards'...
2446 //Roo.log(this.items.length);
2449 var last_card_n = 0;
2451 for (var i = 0;i< this.items.length;i++) {
2453 if (!this.items[i].el.hasClass('card')) {
2456 pos = this.getDropPoint(e, this.items[i].el.dom);
2458 cards_len = ret.cards.length;
2459 //Roo.log(this.items[i].el.dom.id);
2460 ret.cards.push(this.items[i]);
2462 if (ret.card_n < 0 && pos == 'above') {
2463 ret.position = cards_len > 0 ? 'below' : pos;
2464 ret.items_n = i > 0 ? i - 1 : 0;
2465 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2466 ret.card = ret.cards[ret.card_n];
2469 if (!ret.cards.length) {
2471 ret.position = 'below';
2475 // could not find a card.. stick it at the end..
2476 if (ret.card_n < 0) {
2477 ret.card_n = last_card_n;
2478 ret.card = ret.cards[last_card_n];
2479 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480 ret.position = 'below';
2483 if (this.items[ret.items_n].el == dragged_card_el) {
2487 if (ret.position == 'below') {
2488 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2490 if (card_after && card_after.el == dragged_card_el) {
2497 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2499 if (card_before && card_before.el == dragged_card_el) {
2506 onNodeEnter : function(n, dd, e, data){
2509 onNodeOver : function(n, dd, e, data)
2512 var target_info = this.getTargetFromEvent(e,data.source.el);
2513 if (target_info === false) {
2514 this.dropPlaceHolder('hide');
2517 Roo.log(['getTargetFromEvent', target_info ]);
2520 if (this.fireEvent('cardover', this, [ data ]) === false) {
2524 this.dropPlaceHolder('show', target_info,data);
2528 onNodeOut : function(n, dd, e, data){
2529 this.dropPlaceHolder('hide');
2532 onNodeDrop : function(n, dd, e, data)
2535 // call drop - return false if
2537 // this could actually fail - if the Network drops..
2538 // we will ignore this at present..- client should probably reload
2539 // the whole set of cards if stuff like that fails.
2542 var info = this.getTargetFromEvent(e,data.source.el);
2543 if (info === false) {
2546 this.dropPlaceHolder('hide');
2550 this.acceptCard(data.source, info.position, info.card, info.items_n);
2554 firstChildCard : function()
2556 for (var i = 0;i< this.items.length;i++) {
2558 if (!this.items[i].el.hasClass('card')) {
2561 return this.items[i];
2563 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2568 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2570 acceptCard : function(move_card, position, next_to_card )
2572 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2578 move_card.parent().removeCard(move_card);
2581 var dom = move_card.el.dom;
2582 dom.style.width = ''; // clear with - which is set by drag.
2584 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585 var cardel = next_to_card.el.dom;
2587 if (position == 'above' ) {
2588 cardel.parentNode.insertBefore(dom, cardel);
2589 } else if (cardel.nextSibling) {
2590 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2592 cardel.parentNode.append(dom);
2595 // card container???
2596 this.containerEl.dom.append(dom);
2599 //FIXME HANDLE card = true
2601 // add this to the correct place in items.
2603 // remove Card from items.
2606 if (this.items.length) {
2608 //Roo.log([info.items_n, info.position, this.items.length]);
2609 for (var i =0; i < this.items.length; i++) {
2610 if (i == to_items_n && position == 'above') {
2611 nitems.push(move_card);
2613 nitems.push(this.items[i]);
2614 if (i == to_items_n && position == 'below') {
2615 nitems.push(move_card);
2618 this.items = nitems;
2619 Roo.log(this.items);
2621 this.items.push(move_card);
2624 move_card.parentId = this.id;
2630 removeCard : function(c)
2632 this.items = this.items.filter(function(e) { return e != c });
2635 dom.parentNode.removeChild(dom);
2636 dom.style.width = ''; // clear with - which is set by drag.
2641 /** Decide whether to drop above or below a View node. */
2642 getDropPoint : function(e, n, dd)
2647 if (n == this.containerEl.dom) {
2650 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651 var c = t + (b - t) / 2;
2652 var y = Roo.lib.Event.getPageY(e);
2659 onToggleCollapse : function(e)
2661 if (this.collapsed) {
2662 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663 this.collapsableEl.addClass('show');
2664 this.collapsed = false;
2667 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668 this.collapsableEl.removeClass('show');
2669 this.collapsed = true;
2674 onToggleRotate : function(e)
2676 this.collapsableEl.removeClass('show');
2677 this.footerEl.removeClass('d-none');
2678 this.el.removeClass('roo-card-rotated');
2679 this.el.removeClass('d-none');
2682 this.collapsableEl.addClass('show');
2683 this.rotated = false;
2684 this.fireEvent('rotate', this, this.rotated);
2687 this.el.addClass('roo-card-rotated');
2688 this.footerEl.addClass('d-none');
2689 this.el.select('.roo-collapsable').removeClass('show');
2691 this.rotated = true;
2692 this.fireEvent('rotate', this, this.rotated);
2696 dropPlaceHolder: function (action, info, data)
2698 if (this.dropEl === false) {
2699 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703 this.dropEl.removeClass(['d-none', 'd-block']);
2704 if (action == 'hide') {
2706 this.dropEl.addClass('d-none');
2709 // FIXME - info.card == true!!!
2710 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2712 if (info.card !== true) {
2713 var cardel = info.card.el.dom;
2715 if (info.position == 'above') {
2716 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717 } else if (cardel.nextSibling) {
2718 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2720 cardel.parentNode.append(this.dropEl.dom);
2723 // card container???
2724 this.containerEl.dom.append(this.dropEl.dom);
2727 this.dropEl.addClass('d-block roo-card-dropzone');
2729 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2736 setHeaderText: function(html)
2739 if (this.headerContainerEl) {
2740 this.headerContainerEl.dom.innerHTML = html;
2743 onHeaderImageLoad : function(ev, he)
2745 if (!this.header_image_fit_square) {
2749 var hw = he.naturalHeight / he.naturalWidth;
2752 //var w = he.dom.naturalWidth;
2755 he.style.position = 'relative';
2757 var nw = (ww * (1/hw));
2758 Roo.get(he).setSize( ww * (1/hw), ww);
2759 he.style.left = ((ww - nw)/ 2) + 'px';
2760 he.style.position = 'relative';
2771 * Card header - holder for the card header elements.
2776 * @class Roo.bootstrap.CardHeader
2777 * @extends Roo.bootstrap.Element
2778 * @parent Roo.bootstrap.Card
2779 * @children Roo.bootstrap.Component
2780 * Bootstrap CardHeader class
2782 * Create a new Card Header - that you can embed children into
2783 * @param {Object} config The config object
2786 Roo.bootstrap.CardHeader = function(config){
2787 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2793 container_method : 'getCardHeader'
2806 * Card footer - holder for the card footer elements.
2811 * @class Roo.bootstrap.CardFooter
2812 * @extends Roo.bootstrap.Element
2813 * @parent Roo.bootstrap.Card
2814 * @children Roo.bootstrap.Component
2815 * Bootstrap CardFooter class
2818 * Create a new Card Footer - that you can embed children into
2819 * @param {Object} config The config object
2822 Roo.bootstrap.CardFooter = function(config){
2823 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2829 container_method : 'getCardFooter'
2842 * Card header - holder for the card header elements.
2847 * @class Roo.bootstrap.CardImageTop
2848 * @extends Roo.bootstrap.Element
2849 * @parent Roo.bootstrap.Card
2850 * @children Roo.bootstrap.Component
2851 * Bootstrap CardImageTop class
2854 * Create a new Card Image Top container
2855 * @param {Object} config The config object
2858 Roo.bootstrap.CardImageTop = function(config){
2859 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2865 container_method : 'getCardImageTop'
2880 * @class Roo.bootstrap.ButtonUploader
2881 * @extends Roo.bootstrap.Button
2882 * Bootstrap Button Uploader class - it's a button which when you add files to it
2885 * @cfg {Number} errorTimeout default 3000
2886 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2887 * @cfg {Array} html The button text.
2888 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2891 * Create a new CardUploader
2892 * @param {Object} config The config object
2895 Roo.bootstrap.ButtonUploader = function(config){
2899 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2905 * @event beforeselect
2906 * When button is pressed, before show upload files dialog is shown
2907 * @param {Roo.bootstrap.UploaderButton} this
2910 'beforeselect' : true,
2912 * @event fired when files have been selected,
2913 * When a the download link is clicked
2914 * @param {Roo.bootstrap.UploaderButton} this
2915 * @param {Array} Array of files that have been uploaded
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2925 errorTimeout : 3000,
2929 fileCollection : false,
2934 getAutoCreate : function()
2939 cls : 'd-none roo-card-upload-selector'
2942 if (this.multiple) {
2943 im.multiple = 'multiple';
2949 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2959 initEvents : function()
2962 Roo.bootstrap.Button.prototype.initEvents.call(this);
2968 this.urlAPI = (window.createObjectURL && window) ||
2969 (window.URL && URL.revokeObjectURL && URL) ||
2970 (window.webkitURL && webkitURL);
2975 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * Bootstrap Header class
3384 * @cfg {String} html content of header
3385 * @cfg {Number} level (1|2|3|4|5|6) default 1
3388 * Create a new Header
3389 * @param {Object} config The config object
3393 Roo.bootstrap.Header = function(config){
3394 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3397 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3405 getAutoCreate : function(){
3410 tag: 'h' + (1 *this.level),
3411 html: this.html || ''
3421 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
3423 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
3424 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
3425 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
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
4095 * Bootstrap MenuItem class
4096 * @cfg {Boolean} submenu default false -
4097 * @cfg {String} html text of the item
4098 * @cfg {String} href the link
4099 * @cfg {Boolean} disabled is the item disabled - default false
4100 * @cfg {Boolean} preventDefault stop trigger click event to parent elements - default true
4101 * @cfg {String} fa Font awesome icon
4102 * @cfg {String} pos (left|right) Submenu align to default right
4103 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107 * Create a new Menu Item
4108 * @param {Object} config The config object
4112 Roo.bootstrap.menu.Item = function(config){
4113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117 * Fires when the mouse is hovering over this menu
4118 * @param {Roo.bootstrap.menu.Item} this
4119 * @param {Roo.EventObject} e
4124 * Fires when the mouse exits this menu
4125 * @param {Roo.bootstrap.menu.Item} this
4126 * @param {Roo.EventObject} e
4132 * The raw click event for the entire grid.
4133 * @param {Roo.EventObject} e
4139 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4144 preventDefault: true,
4150 isContainer : false, // ?? only a <li drowdonw-menu-item">
4152 getAutoCreate : function()
4155 if(this.isContainer){
4158 cls: 'dropdown-menu-item '
4164 cls : 'roo-menu-item-text',
4170 cls : 'dropdown-item',
4171 href : this.href || '#',
4175 if (this.fa !== false) {
4178 cls : 'fa fa-' + this.fa
4187 cls: 'dropdown-menu-item',
4190 if (this.parent().type == 'treeview') {
4191 cfg.cls = 'treeview-menu';
4194 cfg.cls += ' active';
4198 cfg.cls += ' disabled'
4202 cfg.cls += 'dropdown-submenu';
4204 if(this.pos == 'left'){
4205 cfg.cls += ' pull-left';
4208 anc.href = this.href || cfg.cn[0].href ;
4209 ctag.html = this.html || cfg.cn[0].html ;
4215 initEvents : function()
4217 if (this.parent().type == 'treeview') {
4218 this.el.select('a').on('click', this.onClick, this);
4221 this.menu.parentType = this.xtype;
4222 this.menu.triggerEl = this.el;
4223 this.menu = this.addxtype(Roo.apply({}, this.menu));
4225 this.el.on('mouseover', this.onMouseOver, this);
4226 this.el.on('mouseout', this.onMouseOut, this);
4228 this.el.select('a', true).first().on('click', this.onClick, this);
4232 onClick : function(e)
4234 if(this.preventDefault){
4238 this.fireEvent("click", this, e);
4241 onMouseOver : function(e)
4243 if(this.submenu && this.pos == 'left'){
4244 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
4247 this.fireEvent("mouseover", this, e);
4250 onMouseOut : function(e)
4252 this.fireEvent("mouseout", this, e);
4260 * @class Roo.bootstrap.menu.Separator
4261 * @extends Roo.bootstrap.Component
4263 * Bootstrap Separator class
4266 * Create a new Separator
4267 * @param {Object} config The config object
4271 Roo.bootstrap.menu.Separator = function(config){
4272 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4275 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4277 getAutoCreate : function(){
4280 cls: 'dropdown-divider divider'
4296 * @class Roo.bootstrap.Modal
4297 * @extends Roo.bootstrap.Component
4300 * @children Roo.bootstrap.Component
4301 * Bootstrap Modal class
4302 * @cfg {String} title Title of dialog
4303 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4304 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4305 * @cfg {Boolean} specificTitle default false
4306 * @cfg {Array} buttons Array of buttons or standard button set..
4307 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4308 * @cfg {Boolean} animate default true
4309 * @cfg {Boolean} allow_close default true
4310 * @cfg {Boolean} fitwindow default false
4311 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4312 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4313 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4314 * @cfg {String} size (sm|lg|xl) default empty
4315 * @cfg {Number} max_width set the max width of modal
4316 * @cfg {Boolean} editableTitle can the title be edited
4321 * Create a new Modal Dialog
4322 * @param {Object} config The config object
4325 Roo.bootstrap.Modal = function(config){
4326 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4331 * The raw btnclick event for the button
4332 * @param {Roo.EventObject} e
4337 * Fire when dialog resize
4338 * @param {Roo.bootstrap.Modal} this
4339 * @param {Roo.EventObject} e
4343 * @event titlechanged
4344 * Fire when the editable title has been changed
4345 * @param {Roo.bootstrap.Modal} this
4346 * @param {Roo.EventObject} value
4348 "titlechanged" : true
4351 this.buttons = this.buttons || [];
4354 this.tmpl = Roo.factory(this.tmpl);
4359 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4361 title : 'test dialog',
4371 specificTitle: false,
4373 buttonPosition: 'right',
4395 editableTitle : false,
4397 onRender : function(ct, position)
4399 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4402 var cfg = Roo.apply({}, this.getAutoCreate());
4405 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4407 //if (!cfg.name.length) {
4411 cfg.cls += ' ' + this.cls;
4414 cfg.style = this.style;
4416 this.el = Roo.get(document.body).createChild(cfg, position);
4418 //var type = this.el.dom.type;
4421 if(this.tabIndex !== undefined){
4422 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4425 this.dialogEl = this.el.select('.modal-dialog',true).first();
4426 this.bodyEl = this.el.select('.modal-body',true).first();
4427 this.closeEl = this.el.select('.modal-header .close', true).first();
4428 this.headerEl = this.el.select('.modal-header',true).first();
4429 this.titleEl = this.el.select('.modal-title',true).first();
4430 this.footerEl = this.el.select('.modal-footer',true).first();
4432 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4434 //this.el.addClass("x-dlg-modal");
4436 if (this.buttons.length) {
4437 Roo.each(this.buttons, function(bb) {
4438 var b = Roo.apply({}, bb);
4439 b.xns = b.xns || Roo.bootstrap;
4440 b.xtype = b.xtype || 'Button';
4441 if (typeof(b.listeners) == 'undefined') {
4442 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4445 var btn = Roo.factory(b);
4447 btn.render(this.getButtonContainer());
4451 // render the children.
4454 if(typeof(this.items) != 'undefined'){
4455 var items = this.items;
4458 for(var i =0;i < items.length;i++) {
4459 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4463 this.items = nitems;
4465 // where are these used - they used to be body/close/footer
4469 //this.el.addClass([this.fieldClass, this.cls]);
4473 getAutoCreate : function()
4475 // we will default to modal-body-overflow - might need to remove or make optional later.
4477 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4478 html : this.html || ''
4483 cls : 'modal-title',
4487 if(this.specificTitle){ // WTF is this?
4492 if (this.allow_close && Roo.bootstrap.version == 3) {
4502 if (this.editableTitle) {
4504 cls: 'form-control roo-editable-title d-none',
4510 if (this.allow_close && Roo.bootstrap.version == 4) {
4520 if(this.size.length){
4521 size = 'modal-' + this.size;
4524 var footer = Roo.bootstrap.version == 3 ?
4526 cls : 'modal-footer',
4530 cls: 'btn-' + this.buttonPosition
4535 { // BS4 uses mr-auto on left buttons....
4536 cls : 'modal-footer'
4547 cls: "modal-dialog " + size,
4550 cls : "modal-content",
4553 cls : 'modal-header',
4568 modal.cls += ' fade';
4574 getChildContainer : function() {
4579 getButtonContainer : function() {
4581 return Roo.bootstrap.version == 4 ?
4582 this.el.select('.modal-footer',true).first()
4583 : this.el.select('.modal-footer div',true).first();
4586 initEvents : function()
4588 if (this.allow_close) {
4589 this.closeEl.on('click', this.hide, this);
4591 Roo.EventManager.onWindowResize(this.resize, this, true);
4592 if (this.editableTitle) {
4593 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4594 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4595 this.headerEditEl.on('keyup', function(e) {
4596 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4597 this.toggleHeaderInput(false)
4600 this.headerEditEl.on('blur', function(e) {
4601 this.toggleHeaderInput(false)
4610 this.maskEl.setSize(
4611 Roo.lib.Dom.getViewWidth(true),
4612 Roo.lib.Dom.getViewHeight(true)
4615 if (this.fitwindow) {
4617 this.dialogEl.setStyle( { 'max-width' : '100%' });
4619 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4620 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4625 if(this.max_width !== 0) {
4627 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4630 this.setSize(w, this.height);
4634 if(this.max_height) {
4635 this.setSize(w,Math.min(
4637 Roo.lib.Dom.getViewportHeight(true) - 60
4643 if(!this.fit_content) {
4644 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4648 this.setSize(w, Math.min(
4650 this.headerEl.getHeight() +
4651 this.footerEl.getHeight() +
4652 this.getChildHeight(this.bodyEl.dom.childNodes),
4653 Roo.lib.Dom.getViewportHeight(true) - 60)
4659 setSize : function(w,h)
4670 if (!this.rendered) {
4673 this.toggleHeaderInput(false);
4674 //this.el.setStyle('display', 'block');
4675 this.el.removeClass('hideing');
4676 this.el.dom.style.display='block';
4678 Roo.get(document.body).addClass('modal-open');
4680 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4683 this.el.addClass('show');
4684 this.el.addClass('in');
4687 this.el.addClass('show');
4688 this.el.addClass('in');
4691 // not sure how we can show data in here..
4693 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4696 Roo.get(document.body).addClass("x-body-masked");
4698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4699 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4700 this.maskEl.dom.style.display = 'block';
4701 this.maskEl.addClass('show');
4706 this.fireEvent('show', this);
4708 // set zindex here - otherwise it appears to be ignored...
4709 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4712 this.items.forEach( function(e) {
4713 e.layout ? e.layout() : false;
4721 if(this.fireEvent("beforehide", this) !== false){
4723 this.maskEl.removeClass('show');
4725 this.maskEl.dom.style.display = '';
4726 Roo.get(document.body).removeClass("x-body-masked");
4727 this.el.removeClass('in');
4728 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4730 if(this.animate){ // why
4731 this.el.addClass('hideing');
4732 this.el.removeClass('show');
4734 if (!this.el.hasClass('hideing')) {
4735 return; // it's been shown again...
4738 this.el.dom.style.display='';
4740 Roo.get(document.body).removeClass('modal-open');
4741 this.el.removeClass('hideing');
4745 this.el.removeClass('show');
4746 this.el.dom.style.display='';
4747 Roo.get(document.body).removeClass('modal-open');
4750 this.fireEvent('hide', this);
4753 isVisible : function()
4756 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4760 addButton : function(str, cb)
4764 var b = Roo.apply({}, { html : str } );
4765 b.xns = b.xns || Roo.bootstrap;
4766 b.xtype = b.xtype || 'Button';
4767 if (typeof(b.listeners) == 'undefined') {
4768 b.listeners = { click : cb.createDelegate(this) };
4771 var btn = Roo.factory(b);
4773 btn.render(this.getButtonContainer());
4779 setDefaultButton : function(btn)
4781 //this.el.select('.modal-footer').()
4784 resizeTo: function(w,h)
4786 this.dialogEl.setWidth(w);
4788 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4790 this.bodyEl.setHeight(h - diff);
4792 this.fireEvent('resize', this);
4795 setContentSize : function(w, h)
4799 onButtonClick: function(btn,e)
4802 this.fireEvent('btnclick', btn.name, e);
4805 * Set the title of the Dialog
4806 * @param {String} str new Title
4808 setTitle: function(str) {
4809 this.titleEl.dom.innerHTML = str;
4813 * Set the body of the Dialog
4814 * @param {String} str new Title
4816 setBody: function(str) {
4817 this.bodyEl.dom.innerHTML = str;
4820 * Set the body of the Dialog using the template
4821 * @param {Obj} data - apply this data to the template and replace the body contents.
4823 applyBody: function(obj)
4826 Roo.log("Error - using apply Body without a template");
4829 this.tmpl.overwrite(this.bodyEl, obj);
4832 getChildHeight : function(child_nodes)
4836 child_nodes.length == 0
4841 var child_height = 0;
4843 for(var i = 0; i < child_nodes.length; i++) {
4846 * for modal with tabs...
4847 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4849 var layout_childs = child_nodes[i].childNodes;
4851 for(var j = 0; j < layout_childs.length; j++) {
4853 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4855 var layout_body_childs = layout_childs[j].childNodes;
4857 for(var k = 0; k < layout_body_childs.length; k++) {
4859 if(layout_body_childs[k].classList.contains('navbar')) {
4860 child_height += layout_body_childs[k].offsetHeight;
4864 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4866 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4868 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4870 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4871 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4886 child_height += child_nodes[i].offsetHeight;
4887 // Roo.log(child_nodes[i].offsetHeight);
4890 return child_height;
4892 toggleHeaderInput : function(is_edit)
4894 if (!this.editableTitle) {
4895 return; // not editable.
4897 if (is_edit && this.is_header_editing) {
4898 return; // already editing..
4902 this.headerEditEl.dom.value = this.title;
4903 this.headerEditEl.removeClass('d-none');
4904 this.headerEditEl.dom.focus();
4905 this.titleEl.addClass('d-none');
4907 this.is_header_editing = true;
4910 // flip back to not editing.
4911 this.title = this.headerEditEl.dom.value;
4912 this.headerEditEl.addClass('d-none');
4913 this.titleEl.removeClass('d-none');
4914 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4915 this.is_header_editing = false;
4916 this.fireEvent('titlechanged', this, this.title);
4925 Roo.apply(Roo.bootstrap.Modal, {
4927 * Button config that displays a single OK button
4936 * Button config that displays Yes and No buttons
4952 * Button config that displays OK and Cancel buttons
4967 * Button config that displays Yes, No and Cancel buttons
4992 * messagebox - can be used as a replace
4996 * @class Roo.MessageBox
4997 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
5001 Roo.Msg.alert('Status', 'Changes saved successfully.');
5003 // Prompt for user data:
5004 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
5006 // process text value...
5010 // Show a dialog using config options:
5012 title:'Save Changes?',
5013 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5014 buttons: Roo.Msg.YESNOCANCEL,
5021 Roo.bootstrap.MessageBox = function(){
5022 var dlg, opt, mask, waitTimer;
5023 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5024 var buttons, activeTextEl, bwidth;
5028 var handleButton = function(button){
5030 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5034 var handleHide = function(){
5036 dlg.el.removeClass(opt.cls);
5039 // Roo.TaskMgr.stop(waitTimer);
5040 // waitTimer = null;
5045 var updateButtons = function(b){
5048 buttons["ok"].hide();
5049 buttons["cancel"].hide();
5050 buttons["yes"].hide();
5051 buttons["no"].hide();
5052 dlg.footerEl.hide();
5056 dlg.footerEl.show();
5057 for(var k in buttons){
5058 if(typeof buttons[k] != "function"){
5061 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5062 width += buttons[k].el.getWidth()+15;
5072 var handleEsc = function(d, k, e){
5073 if(opt && opt.closable !== false){
5083 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5084 * @return {Roo.BasicDialog} The BasicDialog element
5086 getDialog : function(){
5088 dlg = new Roo.bootstrap.Modal( {
5091 //constraintoviewport:false,
5093 //collapsible : false,
5098 //buttonAlign:"center",
5099 closeClick : function(){
5100 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5103 handleButton("cancel");
5108 dlg.on("hide", handleHide);
5110 //dlg.addKeyListener(27, handleEsc);
5112 this.buttons = buttons;
5113 var bt = this.buttonText;
5114 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5115 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5116 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5117 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5119 bodyEl = dlg.bodyEl.createChild({
5121 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5122 '<textarea class="roo-mb-textarea"></textarea>' +
5123 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5125 msgEl = bodyEl.dom.firstChild;
5126 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5127 textboxEl.enableDisplayMode();
5128 textboxEl.addKeyListener([10,13], function(){
5129 if(dlg.isVisible() && opt && opt.buttons){
5132 }else if(opt.buttons.yes){
5133 handleButton("yes");
5137 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5138 textareaEl.enableDisplayMode();
5139 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5140 progressEl.enableDisplayMode();
5142 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5143 var pf = progressEl.dom.firstChild;
5145 pp = Roo.get(pf.firstChild);
5146 pp.setHeight(pf.offsetHeight);
5154 * Updates the message box body text
5155 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5156 * the XHTML-compliant non-breaking space character '&#160;')
5157 * @return {Roo.MessageBox} This message box
5159 updateText : function(text)
5161 if(!dlg.isVisible() && !opt.width){
5162 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5163 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5165 msgEl.innerHTML = text || ' ';
5167 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5168 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5170 Math.min(opt.width || cw , this.maxWidth),
5171 Math.max(opt.minWidth || this.minWidth, bwidth)
5174 activeTextEl.setWidth(w);
5176 if(dlg.isVisible()){
5177 dlg.fixedcenter = false;
5179 // to big, make it scroll. = But as usual stupid IE does not support
5182 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5183 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5184 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5186 bodyEl.dom.style.height = '';
5187 bodyEl.dom.style.overflowY = '';
5190 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5192 bodyEl.dom.style.overflowX = '';
5195 dlg.setContentSize(w, bodyEl.getHeight());
5196 if(dlg.isVisible()){
5197 dlg.fixedcenter = true;
5203 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5204 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5205 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5206 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5207 * @return {Roo.MessageBox} This message box
5209 updateProgress : function(value, text){
5211 this.updateText(text);
5214 if (pp) { // weird bug on my firefox - for some reason this is not defined
5215 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5216 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5222 * Returns true if the message box is currently displayed
5223 * @return {Boolean} True if the message box is visible, else false
5225 isVisible : function(){
5226 return dlg && dlg.isVisible();
5230 * Hides the message box if it is displayed
5233 if(this.isVisible()){
5239 * Displays a new message box, or reinitializes an existing message box, based on the config options
5240 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5241 * The following config object properties are supported:
5243 Property Type Description
5244 ---------- --------------- ------------------------------------------------------------------------------------
5245 animEl String/Element An id or Element from which the message box should animate as it opens and
5246 closes (defaults to undefined)
5247 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5248 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5249 closable Boolean False to hide the top-right close button (defaults to true). Note that
5250 progress and wait dialogs will ignore this property and always hide the
5251 close button as they can only be closed programmatically.
5252 cls String A custom CSS class to apply to the message box element
5253 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5254 displayed (defaults to 75)
5255 fn Function A callback function to execute after closing the dialog. The arguments to the
5256 function will be btn (the name of the button that was clicked, if applicable,
5257 e.g. "ok"), and text (the value of the active text field, if applicable).
5258 Progress and wait dialogs will ignore this option since they do not respond to
5259 user actions and can only be closed programmatically, so any required function
5260 should be called by the same code after it closes the dialog.
5261 icon String A CSS class that provides a background image to be used as an icon for
5262 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5263 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5264 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5265 modal Boolean False to allow user interaction with the page while the message box is
5266 displayed (defaults to true)
5267 msg String A string that will replace the existing message box body text (defaults
5268 to the XHTML-compliant non-breaking space character ' ')
5269 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5270 progress Boolean True to display a progress bar (defaults to false)
5271 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5272 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5273 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5274 title String The title text
5275 value String The string value to set into the active textbox element if displayed
5276 wait Boolean True to display a progress bar (defaults to false)
5277 width Number The width of the dialog in pixels
5284 msg: 'Please enter your address:',
5286 buttons: Roo.MessageBox.OKCANCEL,
5289 animEl: 'addAddressBtn'
5292 * @param {Object} config Configuration options
5293 * @return {Roo.MessageBox} This message box
5295 show : function(options)
5298 // this causes nightmares if you show one dialog after another
5299 // especially on callbacks..
5301 if(this.isVisible()){
5304 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5305 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5306 Roo.log("New Dialog Message:" + options.msg )
5307 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5308 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5311 var d = this.getDialog();
5313 d.setTitle(opt.title || " ");
5314 d.closeEl.setDisplayed(opt.closable !== false);
5315 activeTextEl = textboxEl;
5316 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5321 textareaEl.setHeight(typeof opt.multiline == "number" ?
5322 opt.multiline : this.defaultTextHeight);
5323 activeTextEl = textareaEl;
5332 progressEl.setDisplayed(opt.progress === true);
5334 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5336 this.updateProgress(0);
5337 activeTextEl.dom.value = opt.value || "";
5339 dlg.setDefaultButton(activeTextEl);
5341 var bs = opt.buttons;
5345 }else if(bs && bs.yes){
5346 db = buttons["yes"];
5348 dlg.setDefaultButton(db);
5350 bwidth = updateButtons(opt.buttons);
5351 this.updateText(opt.msg);
5353 d.el.addClass(opt.cls);
5355 d.proxyDrag = opt.proxyDrag === true;
5356 d.modal = opt.modal !== false;
5357 d.mask = opt.modal !== false ? mask : false;
5359 // force it to the end of the z-index stack so it gets a cursor in FF
5360 document.body.appendChild(dlg.el.dom);
5361 d.animateTarget = null;
5362 d.show(options.animEl);
5368 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5369 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5370 * and closing the message box when the process is complete.
5371 * @param {String} title The title bar text
5372 * @param {String} msg The message box body text
5373 * @return {Roo.MessageBox} This message box
5375 progress : function(title, msg){
5382 minWidth: this.minProgressWidth,
5389 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5390 * If a callback function is passed it will be called after the user clicks the button, and the
5391 * id of the button that was clicked will be passed as the only parameter to the callback
5392 * (could also be the top-right close button).
5393 * @param {String} title The title bar text
5394 * @param {String} msg The message box body text
5395 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5396 * @param {Object} scope (optional) The scope of the callback function
5397 * @return {Roo.MessageBox} This message box
5399 alert : function(title, msg, fn, scope)
5414 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5415 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5416 * You are responsible for closing the message box when the process is complete.
5417 * @param {String} msg The message box body text
5418 * @param {String} title (optional) The title bar text
5419 * @return {Roo.MessageBox} This message box
5421 wait : function(msg, title){
5432 waitTimer = Roo.TaskMgr.start({
5434 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5442 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5443 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5444 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5445 * @param {String} title The title bar text
5446 * @param {String} msg The message box body text
5447 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5448 * @param {Object} scope (optional) The scope of the callback function
5449 * @return {Roo.MessageBox} This message box
5451 confirm : function(title, msg, fn, scope){
5455 buttons: this.YESNO,
5464 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5465 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5466 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5467 * (could also be the top-right close button) and the text that was entered will be passed as the two
5468 * parameters to the callback.
5469 * @param {String} title The title bar text
5470 * @param {String} msg The message box body text
5471 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5472 * @param {Object} scope (optional) The scope of the callback function
5473 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5474 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5475 * @return {Roo.MessageBox} This message box
5477 prompt : function(title, msg, fn, scope, multiline){
5481 buttons: this.OKCANCEL,
5486 multiline: multiline,
5493 * Button config that displays a single OK button
5498 * Button config that displays Yes and No buttons
5501 YESNO : {yes:true, no:true},
5503 * Button config that displays OK and Cancel buttons
5506 OKCANCEL : {ok:true, cancel:true},
5508 * Button config that displays Yes, No and Cancel buttons
5511 YESNOCANCEL : {yes:true, no:true, cancel:true},
5514 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5517 defaultTextHeight : 75,
5519 * The maximum width in pixels of the message box (defaults to 600)
5524 * The minimum width in pixels of the message box (defaults to 100)
5529 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5530 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5533 minProgressWidth : 250,
5535 * An object containing the default button text strings that can be overriden for localized language support.
5536 * Supported properties are: ok, cancel, yes and no.
5537 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5550 * Shorthand for {@link Roo.MessageBox}
5552 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5553 Roo.Msg = Roo.Msg || Roo.MessageBox;
5562 * @class Roo.bootstrap.Navbar
5563 * @extends Roo.bootstrap.Component
5564 * Bootstrap Navbar class
5567 * Create a new Navbar
5568 * @param {Object} config The config object
5572 Roo.bootstrap.Navbar = function(config){
5573 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5577 * @event beforetoggle
5578 * Fire before toggle the menu
5579 * @param {Roo.EventObject} e
5581 "beforetoggle" : true
5585 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5594 getAutoCreate : function(){
5597 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5601 initEvents :function ()
5603 //Roo.log(this.el.select('.navbar-toggle',true));
5604 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5611 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5613 var size = this.el.getSize();
5614 this.maskEl.setSize(size.width, size.height);
5615 this.maskEl.enableDisplayMode("block");
5624 getChildContainer : function()
5626 if (this.el && this.el.select('.collapse').getCount()) {
5627 return this.el.select('.collapse',true).first();
5642 onToggle : function()
5645 if(this.fireEvent('beforetoggle', this) === false){
5648 var ce = this.el.select('.navbar-collapse',true).first();
5650 if (!ce.hasClass('show')) {
5660 * Expand the navbar pulldown
5662 expand : function ()
5665 var ce = this.el.select('.navbar-collapse',true).first();
5666 if (ce.hasClass('collapsing')) {
5669 ce.dom.style.height = '';
5671 ce.addClass('in'); // old...
5672 ce.removeClass('collapse');
5673 ce.addClass('show');
5674 var h = ce.getHeight();
5676 ce.removeClass('show');
5677 // at this point we should be able to see it..
5678 ce.addClass('collapsing');
5680 ce.setHeight(0); // resize it ...
5681 ce.on('transitionend', function() {
5682 //Roo.log('done transition');
5683 ce.removeClass('collapsing');
5684 ce.addClass('show');
5685 ce.removeClass('collapse');
5687 ce.dom.style.height = '';
5688 }, this, { single: true} );
5690 ce.dom.scrollTop = 0;
5693 * Collapse the navbar pulldown
5695 collapse : function()
5697 var ce = this.el.select('.navbar-collapse',true).first();
5699 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5700 // it's collapsed or collapsing..
5703 ce.removeClass('in'); // old...
5704 ce.setHeight(ce.getHeight());
5705 ce.removeClass('show');
5706 ce.addClass('collapsing');
5708 ce.on('transitionend', function() {
5709 ce.dom.style.height = '';
5710 ce.removeClass('collapsing');
5711 ce.addClass('collapse');
5712 }, this, { single: true} );
5732 * @class Roo.bootstrap.NavSimplebar
5733 * @extends Roo.bootstrap.Navbar
5734 * Bootstrap Sidebar class
5736 * @cfg {Boolean} inverse is inverted color
5738 * @cfg {String} type (nav | pills | tabs)
5739 * @cfg {Boolean} arrangement stacked | justified
5740 * @cfg {String} align (left | right) alignment
5742 * @cfg {Boolean} main (true|false) main nav bar? default false
5743 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5745 * @cfg {String} tag (header|footer|nav|div) default is nav
5747 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5751 * Create a new Sidebar
5752 * @param {Object} config The config object
5756 Roo.bootstrap.NavSimplebar = function(config){
5757 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5760 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5776 getAutoCreate : function(){
5780 tag : this.tag || 'div',
5781 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5783 if (['light','white'].indexOf(this.weight) > -1) {
5784 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5786 cfg.cls += ' bg-' + this.weight;
5789 cfg.cls += ' navbar-inverse';
5793 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5795 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5804 cls: 'nav nav-' + this.xtype,
5810 this.type = this.type || 'nav';
5811 if (['tabs','pills'].indexOf(this.type) != -1) {
5812 cfg.cn[0].cls += ' nav-' + this.type
5816 if (this.type!=='nav') {
5817 Roo.log('nav type must be nav/tabs/pills')
5819 cfg.cn[0].cls += ' navbar-nav'
5825 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5826 cfg.cn[0].cls += ' nav-' + this.arrangement;
5830 if (this.align === 'right') {
5831 cfg.cn[0].cls += ' navbar-right';
5856 * navbar-expand-md fixed-top
5860 * @class Roo.bootstrap.NavHeaderbar
5861 * @extends Roo.bootstrap.NavSimplebar
5862 * Bootstrap Sidebar class
5864 * @cfg {String} brand what is brand
5865 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5866 * @cfg {String} brand_href href of the brand
5867 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5868 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5869 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5870 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5873 * Create a new Sidebar
5874 * @param {Object} config The config object
5878 Roo.bootstrap.NavHeaderbar = function(config){
5879 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5883 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5890 desktopCenter : false,
5893 getAutoCreate : function(){
5896 tag: this.nav || 'nav',
5897 cls: 'navbar navbar-expand-md',
5903 if (this.desktopCenter) {
5904 cn.push({cls : 'container', cn : []});
5912 cls: 'navbar-toggle navbar-toggler',
5913 'data-toggle': 'collapse',
5918 html: 'Toggle navigation'
5922 cls: 'icon-bar navbar-toggler-icon'
5935 cn.push( Roo.bootstrap.version == 4 ? btn : {
5937 cls: 'navbar-header',
5946 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5950 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5952 if (['light','white'].indexOf(this.weight) > -1) {
5953 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5955 cfg.cls += ' bg-' + this.weight;
5958 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5959 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5961 // tag can override this..
5963 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5966 if (this.brand !== '') {
5967 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5968 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5970 href: this.brand_href ? this.brand_href : '#',
5971 cls: 'navbar-brand',
5979 cfg.cls += ' main-nav';
5987 getHeaderChildContainer : function()
5989 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5990 return this.el.select('.navbar-header',true).first();
5993 return this.getChildContainer();
5996 getChildContainer : function()
5999 return this.el.select('.roo-navbar-collapse',true).first();
6004 initEvents : function()
6006 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
6008 if (this.autohide) {
6013 Roo.get(document).on('scroll',function(e) {
6014 var ns = Roo.get(document).getScroll().top;
6015 var os = prevScroll;
6019 ft.removeClass('slideDown');
6020 ft.addClass('slideUp');
6023 ft.removeClass('slideUp');
6024 ft.addClass('slideDown');
6045 * @class Roo.bootstrap.NavSidebar
6046 * @extends Roo.bootstrap.Navbar
6047 * Bootstrap Sidebar class
6050 * Create a new Sidebar
6051 * @param {Object} config The config object
6055 Roo.bootstrap.NavSidebar = function(config){
6056 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6059 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6061 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6063 getAutoCreate : function(){
6068 cls: 'sidebar sidebar-nav'
6090 * @class Roo.bootstrap.NavGroup
6091 * @extends Roo.bootstrap.Component
6092 * Bootstrap NavGroup class
6093 * @cfg {String} align (left|right)
6094 * @cfg {Boolean} inverse
6095 * @cfg {String} type (nav|pills|tab) default nav
6096 * @cfg {String} navId - reference Id for navbar.
6097 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6100 * Create a new nav group
6101 * @param {Object} config The config object
6104 Roo.bootstrap.NavGroup = function(config){
6105 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6108 Roo.bootstrap.NavGroup.register(this);
6112 * Fires when the active item changes
6113 * @param {Roo.bootstrap.NavGroup} this
6114 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6115 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6122 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6134 getAutoCreate : function()
6136 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6142 if (Roo.bootstrap.version == 4) {
6143 if (['tabs','pills'].indexOf(this.type) != -1) {
6144 cfg.cls += ' nav-' + this.type;
6146 // trying to remove so header bar can right align top?
6147 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6148 // do not use on header bar...
6149 cfg.cls += ' navbar-nav';
6154 if (['tabs','pills'].indexOf(this.type) != -1) {
6155 cfg.cls += ' nav-' + this.type
6157 if (this.type !== 'nav') {
6158 Roo.log('nav type must be nav/tabs/pills')
6160 cfg.cls += ' navbar-nav'
6164 if (this.parent() && this.parent().sidebar) {
6167 cls: 'dashboard-menu sidebar-menu'
6173 if (this.form === true) {
6176 cls: 'navbar-form form-inline'
6178 //nav navbar-right ml-md-auto
6179 if (this.align === 'right') {
6180 cfg.cls += ' navbar-right ml-md-auto';
6182 cfg.cls += ' navbar-left';
6186 if (this.align === 'right') {
6187 cfg.cls += ' navbar-right ml-md-auto';
6189 cfg.cls += ' mr-auto';
6193 cfg.cls += ' navbar-inverse';
6201 * sets the active Navigation item
6202 * @param {Roo.bootstrap.NavItem} the new current navitem
6204 setActiveItem : function(item)
6207 Roo.each(this.navItems, function(v){
6212 v.setActive(false, true);
6219 item.setActive(true, true);
6220 this.fireEvent('changed', this, item, prev);
6225 * gets the active Navigation item
6226 * @return {Roo.bootstrap.NavItem} the current navitem
6228 getActive : function()
6232 Roo.each(this.navItems, function(v){
6243 indexOfNav : function()
6247 Roo.each(this.navItems, function(v,i){
6258 * adds a Navigation item
6259 * @param {Roo.bootstrap.NavItem} the navitem to add
6261 addItem : function(cfg)
6263 if (this.form && Roo.bootstrap.version == 4) {
6266 var cn = new Roo.bootstrap.NavItem(cfg);
6268 cn.parentId = this.id;
6269 cn.onRender(this.el, null);
6273 * register a Navigation item
6274 * @param {Roo.bootstrap.NavItem} the navitem to add
6276 register : function(item)
6278 this.navItems.push( item);
6279 item.navId = this.navId;
6284 * clear all the Navigation item
6287 clearAll : function()
6290 this.el.dom.innerHTML = '';
6293 getNavItem: function(tabId)
6296 Roo.each(this.navItems, function(e) {
6297 if (e.tabId == tabId) {
6307 setActiveNext : function()
6309 var i = this.indexOfNav(this.getActive());
6310 if (i > this.navItems.length) {
6313 this.setActiveItem(this.navItems[i+1]);
6315 setActivePrev : function()
6317 var i = this.indexOfNav(this.getActive());
6321 this.setActiveItem(this.navItems[i-1]);
6323 clearWasActive : function(except) {
6324 Roo.each(this.navItems, function(e) {
6325 if (e.tabId != except.tabId && e.was_active) {
6326 e.was_active = false;
6333 getWasActive : function ()
6336 Roo.each(this.navItems, function(e) {
6351 Roo.apply(Roo.bootstrap.NavGroup, {
6355 * register a Navigation Group
6356 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6358 register : function(navgrp)
6360 this.groups[navgrp.navId] = navgrp;
6364 * fetch a Navigation Group based on the navigation ID
6365 * @param {string} the navgroup to add
6366 * @returns {Roo.bootstrap.NavGroup} the navgroup
6368 get: function(navId) {
6369 if (typeof(this.groups[navId]) == 'undefined') {
6371 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6373 return this.groups[navId] ;
6381 * @class Roo.bootstrap.NavItem
6382 * @extends Roo.bootstrap.Component
6384 * Bootstrap Navbar.NavItem class
6386 * @cfg {String} href link to
6387 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6388 * @cfg {Boolean} button_outline show and outlined button
6389 * @cfg {String} html content of button
6390 * @cfg {String} badge text inside badge
6391 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6392 * @cfg {String} glyphicon DEPRICATED - use fa
6393 * @cfg {String} icon DEPRICATED - use fa
6394 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6395 * @cfg {Boolean} active Is item active
6396 * @cfg {Boolean} disabled Is item disabled
6397 * @cfg {String} linkcls Link Class
6398 * @cfg {Boolean} preventDefault (true | false) default false
6399 * @cfg {String} tabId the tab that this item activates.
6400 * @cfg {String} tagtype (a|span) render as a href or span?
6401 * @cfg {Boolean} animateRef (true|false) link to element default false
6402 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6405 * Create a new Navbar Item
6406 * @param {Object} config The config object
6408 Roo.bootstrap.NavItem = function(config){
6409 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6414 * The raw click event for the entire grid.
6415 * @param {Roo.EventObject} e
6420 * Fires when the active item active state changes
6421 * @param {Roo.bootstrap.NavItem} this
6422 * @param {boolean} state the new state
6428 * Fires when scroll to element
6429 * @param {Roo.bootstrap.NavItem} this
6430 * @param {Object} options
6431 * @param {Roo.EventObject} e
6439 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6448 preventDefault : false,
6456 button_outline : false,
6460 getAutoCreate : function(){
6467 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6470 cfg.cls += ' active' ;
6472 if (this.disabled) {
6473 cfg.cls += ' disabled';
6477 if (this.button_weight.length) {
6478 cfg.tag = this.href ? 'a' : 'button';
6479 cfg.html = this.html || '';
6480 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6482 cfg.href = this.href;
6485 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6487 cfg.cls += " nav-html";
6490 // menu .. should add dropdown-menu class - so no need for carat..
6492 if (this.badge !== '') {
6494 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6499 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6503 href : this.href || "#",
6504 html: this.html || '',
6508 if (this.tagtype == 'a') {
6509 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6513 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6514 } else if (this.fa) {
6515 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6516 } else if(this.glyphicon) {
6517 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6519 cfg.cn[0].cls += " nav-html";
6523 cfg.cn[0].html += " <span class='caret'></span>";
6527 if (this.badge !== '') {
6528 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6536 onRender : function(ct, position)
6538 // Roo.log("Call onRender: " + this.xtype);
6539 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6543 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6544 this.navLink = this.el.select('.nav-link',true).first();
6545 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6550 initEvents: function()
6552 if (typeof (this.menu) != 'undefined') {
6553 this.menu.parentType = this.xtype;
6554 this.menu.triggerEl = this.el;
6555 this.menu = this.addxtype(Roo.apply({}, this.menu));
6558 this.el.on('click', this.onClick, this);
6560 //if(this.tagtype == 'span'){
6561 // this.el.select('span',true).on('click', this.onClick, this);
6564 // at this point parent should be available..
6565 this.parent().register(this);
6568 onClick : function(e)
6570 if (e.getTarget('.dropdown-menu-item')) {
6571 // did you click on a menu itemm.... - then don't trigger onclick..
6576 this.preventDefault ||
6579 Roo.log("NavItem - prevent Default?");
6583 if (this.disabled) {
6587 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6588 if (tg && tg.transition) {
6589 Roo.log("waiting for the transitionend");
6595 //Roo.log("fire event clicked");
6596 if(this.fireEvent('click', this, e) === false){
6600 if(this.tagtype == 'span'){
6604 //Roo.log(this.href);
6605 var ael = this.el.select('a',true).first();
6608 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6609 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6610 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6611 return; // ignore... - it's a 'hash' to another page.
6613 Roo.log("NavItem - prevent Default?");
6615 this.scrollToElement(e);
6619 var p = this.parent();
6621 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6622 if (typeof(p.setActiveItem) !== 'undefined') {
6623 p.setActiveItem(this);
6627 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6628 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6629 // remove the collapsed menu expand...
6630 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6634 isActive: function () {
6637 setActive : function(state, fire, is_was_active)
6639 if (this.active && !state && this.navId) {
6640 this.was_active = true;
6641 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6643 nv.clearWasActive(this);
6647 this.active = state;
6650 this.el.removeClass('active');
6651 this.navLink ? this.navLink.removeClass('active') : false;
6652 } else if (!this.el.hasClass('active')) {
6654 this.el.addClass('active');
6655 if (Roo.bootstrap.version == 4 && this.navLink ) {
6656 this.navLink.addClass('active');
6661 this.fireEvent('changed', this, state);
6664 // show a panel if it's registered and related..
6666 if (!this.navId || !this.tabId || !state || is_was_active) {
6670 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6674 var pan = tg.getPanelByName(this.tabId);
6678 // if we can not flip to new panel - go back to old nav highlight..
6679 if (false == tg.showPanel(pan)) {
6680 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6682 var onav = nv.getWasActive();
6684 onav.setActive(true, false, true);
6693 // this should not be here...
6694 setDisabled : function(state)
6696 this.disabled = state;
6698 this.el.removeClass('disabled');
6699 } else if (!this.el.hasClass('disabled')) {
6700 this.el.addClass('disabled');
6706 * Fetch the element to display the tooltip on.
6707 * @return {Roo.Element} defaults to this.el
6709 tooltipEl : function()
6711 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6714 scrollToElement : function(e)
6716 var c = document.body;
6719 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6721 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6722 c = document.documentElement;
6725 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6731 var o = target.calcOffsetsTo(c);
6738 this.fireEvent('scrollto', this, options, e);
6740 Roo.get(c).scrollTo('top', options.value, true);
6745 * Set the HTML (text content) of the item
6746 * @param {string} html content for the nav item
6748 setHtml : function(html)
6751 this.htmlEl.dom.innerHTML = html;
6763 * <span> icon </span>
6764 * <span> text </span>
6765 * <span>badge </span>
6769 * @class Roo.bootstrap.NavSidebarItem
6770 * @extends Roo.bootstrap.NavItem
6771 * Bootstrap Navbar.NavSidebarItem class
6772 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6773 * {Boolean} open is the menu open
6774 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6775 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6776 * {String} buttonSize (sm|md|lg)the extra classes for the button
6777 * {Boolean} showArrow show arrow next to the text (default true)
6779 * Create a new Navbar Button
6780 * @param {Object} config The config object
6782 Roo.bootstrap.NavSidebarItem = function(config){
6783 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6788 * The raw click event for the entire grid.
6789 * @param {Roo.EventObject} e
6794 * Fires when the active item active state changes
6795 * @param {Roo.bootstrap.NavSidebarItem} this
6796 * @param {boolean} state the new state
6804 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6806 badgeWeight : 'default',
6812 buttonWeight : 'default',
6818 getAutoCreate : function(){
6823 href : this.href || '#',
6829 if(this.buttonView){
6832 href : this.href || '#',
6833 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6846 cfg.cls += ' active';
6849 if (this.disabled) {
6850 cfg.cls += ' disabled';
6853 cfg.cls += ' open x-open';
6856 if (this.glyphicon || this.icon) {
6857 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6858 a.cn.push({ tag : 'i', cls : c }) ;
6861 if(!this.buttonView){
6864 html : this.html || ''
6871 if (this.badge !== '') {
6872 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6878 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6881 a.cls += ' dropdown-toggle treeview' ;
6887 initEvents : function()
6889 if (typeof (this.menu) != 'undefined') {
6890 this.menu.parentType = this.xtype;
6891 this.menu.triggerEl = this.el;
6892 this.menu = this.addxtype(Roo.apply({}, this.menu));
6895 this.el.on('click', this.onClick, this);
6897 if(this.badge !== ''){
6898 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6903 onClick : function(e)
6910 if(this.preventDefault){
6914 this.fireEvent('click', this, e);
6917 disable : function()
6919 this.setDisabled(true);
6924 this.setDisabled(false);
6927 setDisabled : function(state)
6929 if(this.disabled == state){
6933 this.disabled = state;
6936 this.el.addClass('disabled');
6940 this.el.removeClass('disabled');
6945 setActive : function(state)
6947 if(this.active == state){
6951 this.active = state;
6954 this.el.addClass('active');
6958 this.el.removeClass('active');
6963 isActive: function ()
6968 setBadge : function(str)
6974 this.badgeEl.dom.innerHTML = str;
6989 Roo.namespace('Roo.bootstrap.breadcrumb');
6993 * @class Roo.bootstrap.breadcrumb.Nav
6994 * @extends Roo.bootstrap.Component
6995 * Bootstrap Breadcrumb Nav Class
6997 * @children Roo.bootstrap.breadcrumb.Item
7000 * Create a new breadcrumb.Nav
7001 * @param {Object} config The config object
7005 Roo.bootstrap.breadcrumb.Nav = function(config){
7006 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7011 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7013 getAutoCreate : function()
7030 initEvents: function()
7032 this.olEl = this.el.select('ol',true).first();
7034 getChildContainer : function()
7050 * @class Roo.bootstrap.breadcrumb.Nav
7051 * @extends Roo.bootstrap.Component
7052 * Bootstrap Breadcrumb Nav Class
7054 * @children Roo.bootstrap.breadcrumb.Component
7055 * @cfg {String} html the content of the link.
7056 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7057 * @cfg {Boolean} active is it active
7061 * Create a new breadcrumb.Nav
7062 * @param {Object} config The config object
7065 Roo.bootstrap.breadcrumb.Item = function(config){
7066 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7071 * The img click event for the img.
7072 * @param {Roo.EventObject} e
7079 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7084 getAutoCreate : function()
7089 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7091 if (this.href !== false) {
7098 cfg.html = this.html;
7104 initEvents: function()
7107 this.el.select('a', true).first().on('click',this.onClick, this)
7111 onClick : function(e)
7114 this.fireEvent('click',this, e);
7127 * @class Roo.bootstrap.Row
7128 * @extends Roo.bootstrap.Component
7129 * @children Roo.bootstrap.Component
7130 * Bootstrap Row class (contains columns...)
7134 * @param {Object} config The config object
7137 Roo.bootstrap.Row = function(config){
7138 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7141 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7143 getAutoCreate : function(){
7162 * @class Roo.bootstrap.Pagination
7163 * @extends Roo.bootstrap.Component
7164 * Bootstrap Pagination class
7165 * @cfg {String} size xs | sm | md | lg
7166 * @cfg {Boolean} inverse false | true
7169 * Create a new Pagination
7170 * @param {Object} config The config object
7173 Roo.bootstrap.Pagination = function(config){
7174 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7177 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7183 getAutoCreate : function(){
7189 cfg.cls += ' inverse';
7195 cfg.cls += " " + this.cls;
7213 * @class Roo.bootstrap.PaginationItem
7214 * @extends Roo.bootstrap.Component
7215 * Bootstrap PaginationItem class
7216 * @cfg {String} html text
7217 * @cfg {String} href the link
7218 * @cfg {Boolean} preventDefault (true | false) default true
7219 * @cfg {Boolean} active (true | false) default false
7220 * @cfg {Boolean} disabled default false
7224 * Create a new PaginationItem
7225 * @param {Object} config The config object
7229 Roo.bootstrap.PaginationItem = function(config){
7230 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7235 * The raw click event for the entire grid.
7236 * @param {Roo.EventObject} e
7242 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7246 preventDefault: true,
7251 getAutoCreate : function(){
7257 href : this.href ? this.href : '#',
7258 html : this.html ? this.html : ''
7268 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7272 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7278 initEvents: function() {
7280 this.el.on('click', this.onClick, this);
7283 onClick : function(e)
7285 Roo.log('PaginationItem on click ');
7286 if(this.preventDefault){
7294 this.fireEvent('click', this, e);
7310 * @class Roo.bootstrap.Slider
7311 * @extends Roo.bootstrap.Component
7312 * Bootstrap Slider class
7315 * Create a new Slider
7316 * @param {Object} config The config object
7319 Roo.bootstrap.Slider = function(config){
7320 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7323 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7325 getAutoCreate : function(){
7329 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7333 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7345 * Ext JS Library 1.1.1
7346 * Copyright(c) 2006-2007, Ext JS, LLC.
7348 * Originally Released Under LGPL - original licence link has changed is not relivant.
7351 * <script type="text/javascript">
7354 * @extends Roo.dd.DDProxy
7355 * @class Roo.grid.SplitDragZone
7356 * Support for Column Header resizing
7358 * @param {Object} config
7361 // This is a support class used internally by the Grid components
7362 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7364 this.view = grid.getView();
7365 this.proxy = this.view.resizeProxy;
7366 Roo.grid.SplitDragZone.superclass.constructor.call(
7369 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7371 dragElId : Roo.id(this.proxy.dom),
7376 this.setHandleElId(Roo.id(hd));
7377 if (hd2 !== false) {
7378 this.setOuterHandleElId(Roo.id(hd2));
7381 this.scroll = false;
7383 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7384 fly: Roo.Element.fly,
7386 b4StartDrag : function(x, y){
7387 this.view.headersDisabled = true;
7388 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7389 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7391 this.proxy.setHeight(h);
7393 // for old system colWidth really stored the actual width?
7394 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7395 // which in reality did not work.. - it worked only for fixed sizes
7396 // for resizable we need to use actual sizes.
7397 var w = this.cm.getColumnWidth(this.cellIndex);
7398 if (!this.view.mainWrap) {
7400 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7405 // this was w-this.grid.minColumnWidth;
7406 // doesnt really make sense? - w = thie curren width or the rendered one?
7407 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7408 this.resetConstraints();
7409 this.setXConstraint(minw, 1000);
7410 this.setYConstraint(0, 0);
7411 this.minX = x - minw;
7412 this.maxX = x + 1000;
7414 if (!this.view.mainWrap) { // this is Bootstrap code..
7415 this.getDragEl().style.display='block';
7418 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7422 handleMouseDown : function(e){
7423 ev = Roo.EventObject.setEvent(e);
7424 var t = this.fly(ev.getTarget());
7425 if(t.hasClass("x-grid-split")){
7426 this.cellIndex = this.view.getCellIndex(t.dom);
7428 this.cm = this.grid.colModel;
7429 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7430 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7435 endDrag : function(e){
7436 this.view.headersDisabled = false;
7437 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7438 var diff = endX - this.startPos;
7440 var w = this.cm.getColumnWidth(this.cellIndex);
7441 if (!this.view.mainWrap) {
7444 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7447 autoOffset : function(){
7452 * Ext JS Library 1.1.1
7453 * Copyright(c) 2006-2007, Ext JS, LLC.
7455 * Originally Released Under LGPL - original licence link has changed is not relivant.
7458 * <script type="text/javascript">
7462 * @class Roo.grid.AbstractSelectionModel
7463 * @extends Roo.util.Observable
7465 * Abstract base class for grid SelectionModels. It provides the interface that should be
7466 * implemented by descendant classes. This class should not be directly instantiated.
7469 Roo.grid.AbstractSelectionModel = function(){
7470 this.locked = false;
7471 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7474 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7475 /** @ignore Called by the grid automatically. Do not call directly. */
7476 init : function(grid){
7482 * Locks the selections.
7489 * Unlocks the selections.
7491 unlock : function(){
7492 this.locked = false;
7496 * Returns true if the selections are locked.
7499 isLocked : function(){
7504 * Ext JS Library 1.1.1
7505 * Copyright(c) 2006-2007, Ext JS, LLC.
7507 * Originally Released Under LGPL - original licence link has changed is not relivant.
7510 * <script type="text/javascript">
7513 * @extends Roo.grid.AbstractSelectionModel
7514 * @class Roo.grid.RowSelectionModel
7515 * The default SelectionModel used by {@link Roo.grid.Grid}.
7516 * It supports multiple selections and keyboard selection/navigation.
7518 * @param {Object} config
7520 Roo.grid.RowSelectionModel = function(config){
7521 Roo.apply(this, config);
7522 this.selections = new Roo.util.MixedCollection(false, function(o){
7527 this.lastActive = false;
7531 * @event selectionchange
7532 * Fires when the selection changes
7533 * @param {SelectionModel} this
7535 "selectionchange" : true,
7537 * @event afterselectionchange
7538 * Fires after the selection changes (eg. by key press or clicking)
7539 * @param {SelectionModel} this
7541 "afterselectionchange" : true,
7543 * @event beforerowselect
7544 * Fires when a row is selected being selected, return false to cancel.
7545 * @param {SelectionModel} this
7546 * @param {Number} rowIndex The selected index
7547 * @param {Boolean} keepExisting False if other selections will be cleared
7549 "beforerowselect" : true,
7552 * Fires when a row is selected.
7553 * @param {SelectionModel} this
7554 * @param {Number} rowIndex The selected index
7555 * @param {Roo.data.Record} r The record
7559 * @event rowdeselect
7560 * Fires when a row is deselected.
7561 * @param {SelectionModel} this
7562 * @param {Number} rowIndex The selected index
7564 "rowdeselect" : true
7566 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7567 this.locked = false;
7570 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7572 * @cfg {Boolean} singleSelect
7573 * True to allow selection of only one row at a time (defaults to false)
7575 singleSelect : false,
7578 initEvents : function(){
7580 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7581 this.grid.on("mousedown", this.handleMouseDown, this);
7582 }else{ // allow click to work like normal
7583 this.grid.on("rowclick", this.handleDragableRowClick, this);
7585 // bootstrap does not have a view..
7586 var view = this.grid.view ? this.grid.view : this.grid;
7587 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7590 this.selectPrevious(e.shiftKey);
7591 }else if(this.last !== false && this.lastActive !== false){
7592 var last = this.last;
7593 this.selectRange(this.last, this.lastActive-1);
7594 view.focusRow(this.lastActive);
7599 this.selectFirstRow();
7601 this.fireEvent("afterselectionchange", this);
7603 "down" : function(e){
7605 this.selectNext(e.shiftKey);
7606 }else if(this.last !== false && this.lastActive !== false){
7607 var last = this.last;
7608 this.selectRange(this.last, this.lastActive+1);
7609 view.focusRow(this.lastActive);
7614 this.selectFirstRow();
7616 this.fireEvent("afterselectionchange", this);
7622 view.on("refresh", this.onRefresh, this);
7623 view.on("rowupdated", this.onRowUpdated, this);
7624 view.on("rowremoved", this.onRemove, this);
7628 onRefresh : function(){
7629 var ds = this.grid.ds, i, v = this.grid.view;
7630 var s = this.selections;
7632 if((i = ds.indexOfId(r.id)) != -1){
7634 s.add(ds.getAt(i)); // updating the selection relate data
7642 onRemove : function(v, index, r){
7643 this.selections.remove(r);
7647 onRowUpdated : function(v, index, r){
7648 if(this.isSelected(r)){
7649 v.onRowSelect(index);
7655 * @param {Array} records The records to select
7656 * @param {Boolean} keepExisting (optional) True to keep existing selections
7658 selectRecords : function(records, keepExisting){
7660 this.clearSelections();
7662 var ds = this.grid.ds;
7663 for(var i = 0, len = records.length; i < len; i++){
7664 this.selectRow(ds.indexOf(records[i]), true);
7669 * Gets the number of selected rows.
7672 getCount : function(){
7673 return this.selections.length;
7677 * Selects the first row in the grid.
7679 selectFirstRow : function(){
7684 * Select the last row.
7685 * @param {Boolean} keepExisting (optional) True to keep existing selections
7687 selectLastRow : function(keepExisting){
7688 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7692 * Selects the row immediately following the last selected row.
7693 * @param {Boolean} keepExisting (optional) True to keep existing selections
7695 selectNext : function(keepExisting){
7696 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7697 this.selectRow(this.last+1, keepExisting);
7698 var view = this.grid.view ? this.grid.view : this.grid;
7699 view.focusRow(this.last);
7704 * Selects the row that precedes the last selected row.
7705 * @param {Boolean} keepExisting (optional) True to keep existing selections
7707 selectPrevious : function(keepExisting){
7709 this.selectRow(this.last-1, keepExisting);
7710 var view = this.grid.view ? this.grid.view : this.grid;
7711 view.focusRow(this.last);
7716 * Returns the selected records
7717 * @return {Array} Array of selected records
7719 getSelections : function(){
7720 return [].concat(this.selections.items);
7724 * Returns the first selected record.
7727 getSelected : function(){
7728 return this.selections.itemAt(0);
7733 * Clears all selections.
7735 clearSelections : function(fast){
7740 var ds = this.grid.ds;
7741 var s = this.selections;
7743 this.deselectRow(ds.indexOfId(r.id));
7747 this.selections.clear();
7756 selectAll : function(){
7760 this.selections.clear();
7761 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7762 this.selectRow(i, true);
7767 * Returns True if there is a selection.
7770 hasSelection : function(){
7771 return this.selections.length > 0;
7775 * Returns True if the specified row is selected.
7776 * @param {Number/Record} record The record or index of the record to check
7779 isSelected : function(index){
7780 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7781 return (r && this.selections.key(r.id) ? true : false);
7785 * Returns True if the specified record id is selected.
7786 * @param {String} id The id of record to check
7789 isIdSelected : function(id){
7790 return (this.selections.key(id) ? true : false);
7794 handleMouseDown : function(e, t)
7796 var view = this.grid.view ? this.grid.view : this.grid;
7798 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7801 if(e.shiftKey && this.last !== false){
7802 var last = this.last;
7803 this.selectRange(last, rowIndex, e.ctrlKey);
7804 this.last = last; // reset the last
7805 view.focusRow(rowIndex);
7807 var isSelected = this.isSelected(rowIndex);
7808 if(e.button !== 0 && isSelected){
7809 view.focusRow(rowIndex);
7810 }else if(e.ctrlKey && isSelected){
7811 this.deselectRow(rowIndex);
7812 }else if(!isSelected){
7813 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7814 view.focusRow(rowIndex);
7817 this.fireEvent("afterselectionchange", this);
7820 handleDragableRowClick : function(grid, rowIndex, e)
7822 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7823 this.selectRow(rowIndex, false);
7824 var view = this.grid.view ? this.grid.view : this.grid;
7825 view.focusRow(rowIndex);
7826 this.fireEvent("afterselectionchange", this);
7831 * Selects multiple rows.
7832 * @param {Array} rows Array of the indexes of the row to select
7833 * @param {Boolean} keepExisting (optional) True to keep existing selections
7835 selectRows : function(rows, keepExisting){
7837 this.clearSelections();
7839 for(var i = 0, len = rows.length; i < len; i++){
7840 this.selectRow(rows[i], true);
7845 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7846 * @param {Number} startRow The index of the first row in the range
7847 * @param {Number} endRow The index of the last row in the range
7848 * @param {Boolean} keepExisting (optional) True to retain existing selections
7850 selectRange : function(startRow, endRow, keepExisting){
7855 this.clearSelections();
7857 if(startRow <= endRow){
7858 for(var i = startRow; i <= endRow; i++){
7859 this.selectRow(i, true);
7862 for(var i = startRow; i >= endRow; i--){
7863 this.selectRow(i, true);
7869 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7870 * @param {Number} startRow The index of the first row in the range
7871 * @param {Number} endRow The index of the last row in the range
7873 deselectRange : function(startRow, endRow, preventViewNotify){
7877 for(var i = startRow; i <= endRow; i++){
7878 this.deselectRow(i, preventViewNotify);
7884 * @param {Number} row The index of the row to select
7885 * @param {Boolean} keepExisting (optional) True to keep existing selections
7887 selectRow : function(index, keepExisting, preventViewNotify){
7888 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7891 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7892 if(!keepExisting || this.singleSelect){
7893 this.clearSelections();
7895 var r = this.grid.ds.getAt(index);
7896 this.selections.add(r);
7897 this.last = this.lastActive = index;
7898 if(!preventViewNotify){
7899 var view = this.grid.view ? this.grid.view : this.grid;
7900 view.onRowSelect(index);
7902 this.fireEvent("rowselect", this, index, r);
7903 this.fireEvent("selectionchange", this);
7909 * @param {Number} row The index of the row to deselect
7911 deselectRow : function(index, preventViewNotify){
7915 if(this.last == index){
7918 if(this.lastActive == index){
7919 this.lastActive = false;
7921 var r = this.grid.ds.getAt(index);
7922 this.selections.remove(r);
7923 if(!preventViewNotify){
7924 var view = this.grid.view ? this.grid.view : this.grid;
7925 view.onRowDeselect(index);
7927 this.fireEvent("rowdeselect", this, index);
7928 this.fireEvent("selectionchange", this);
7932 restoreLast : function(){
7934 this.last = this._last;
7939 acceptsNav : function(row, col, cm){
7940 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7944 onEditorKey : function(field, e){
7945 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7950 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7952 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7954 }else if(k == e.ENTER && !e.ctrlKey){
7958 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7960 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7962 }else if(k == e.ESC){
7966 g.startEditing(newCell[0], newCell[1]);
7971 * Ext JS Library 1.1.1
7972 * Copyright(c) 2006-2007, Ext JS, LLC.
7974 * Originally Released Under LGPL - original licence link has changed is not relivant.
7977 * <script type="text/javascript">
7982 * @class Roo.grid.ColumnModel
7983 * @extends Roo.util.Observable
7984 * This is the default implementation of a ColumnModel used by the Grid. It defines
7985 * the columns in the grid.
7988 var colModel = new Roo.grid.ColumnModel([
7989 {header: "Ticker", width: 60, sortable: true, locked: true},
7990 {header: "Company Name", width: 150, sortable: true},
7991 {header: "Market Cap.", width: 100, sortable: true},
7992 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7993 {header: "Employees", width: 100, sortable: true, resizable: false}
7998 * The config options listed for this class are options which may appear in each
7999 * individual column definition.
8000 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8002 * @param {Object} config An Array of column config objects. See this class's
8003 * config objects for details.
8005 Roo.grid.ColumnModel = function(config){
8007 * The config passed into the constructor
8009 this.config = []; //config;
8012 // if no id, create one
8013 // if the column does not have a dataIndex mapping,
8014 // map it to the order it is in the config
8015 for(var i = 0, len = config.length; i < len; i++){
8016 this.addColumn(config[i]);
8021 * The width of columns which have no width specified (defaults to 100)
8024 this.defaultWidth = 100;
8027 * Default sortable of columns which have no sortable specified (defaults to false)
8030 this.defaultSortable = false;
8034 * @event widthchange
8035 * Fires when the width of a column changes.
8036 * @param {ColumnModel} this
8037 * @param {Number} columnIndex The column index
8038 * @param {Number} newWidth The new width
8040 "widthchange": true,
8042 * @event headerchange
8043 * Fires when the text of a header changes.
8044 * @param {ColumnModel} this
8045 * @param {Number} columnIndex The column index
8046 * @param {Number} newText The new header text
8048 "headerchange": true,
8050 * @event hiddenchange
8051 * Fires when a column is hidden or "unhidden".
8052 * @param {ColumnModel} this
8053 * @param {Number} columnIndex The column index
8054 * @param {Boolean} hidden true if hidden, false otherwise
8056 "hiddenchange": true,
8058 * @event columnmoved
8059 * Fires when a column is moved.
8060 * @param {ColumnModel} this
8061 * @param {Number} oldIndex
8062 * @param {Number} newIndex
8064 "columnmoved" : true,
8066 * @event columlockchange
8067 * Fires when a column's locked state is changed
8068 * @param {ColumnModel} this
8069 * @param {Number} colIndex
8070 * @param {Boolean} locked true if locked
8072 "columnlockchange" : true
8074 Roo.grid.ColumnModel.superclass.constructor.call(this);
8076 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8078 * @cfg {String} header The header text to display in the Grid view.
8081 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8084 * @cfg {String} smHeader Header at Bootsrap Small width
8087 * @cfg {String} mdHeader Header at Bootsrap Medium width
8090 * @cfg {String} lgHeader Header at Bootsrap Large width
8093 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8096 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8097 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8098 * specified, the column's index is used as an index into the Record's data Array.
8101 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8102 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8105 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8106 * Defaults to the value of the {@link #defaultSortable} property.
8107 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8110 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8113 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8116 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8119 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8122 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8123 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8124 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8125 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8128 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8131 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8134 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8137 * @cfg {String} cursor (Optional)
8140 * @cfg {String} tooltip (Optional)
8143 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8146 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8149 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8152 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8155 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8158 * Returns the id of the column at the specified index.
8159 * @param {Number} index The column index
8160 * @return {String} the id
8162 getColumnId : function(index){
8163 return this.config[index].id;
8167 * Returns the column for a specified id.
8168 * @param {String} id The column id
8169 * @return {Object} the column
8171 getColumnById : function(id){
8172 return this.lookup[id];
8177 * Returns the column Object for a specified dataIndex.
8178 * @param {String} dataIndex The column dataIndex
8179 * @return {Object|Boolean} the column or false if not found
8181 getColumnByDataIndex: function(dataIndex){
8182 var index = this.findColumnIndex(dataIndex);
8183 return index > -1 ? this.config[index] : false;
8187 * Returns the index for a specified column id.
8188 * @param {String} id The column id
8189 * @return {Number} the index, or -1 if not found
8191 getIndexById : function(id){
8192 for(var i = 0, len = this.config.length; i < len; i++){
8193 if(this.config[i].id == id){
8201 * Returns the index for a specified column dataIndex.
8202 * @param {String} dataIndex The column dataIndex
8203 * @return {Number} the index, or -1 if not found
8206 findColumnIndex : function(dataIndex){
8207 for(var i = 0, len = this.config.length; i < len; i++){
8208 if(this.config[i].dataIndex == dataIndex){
8216 moveColumn : function(oldIndex, newIndex){
8217 var c = this.config[oldIndex];
8218 this.config.splice(oldIndex, 1);
8219 this.config.splice(newIndex, 0, c);
8220 this.dataMap = null;
8221 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8224 isLocked : function(colIndex){
8225 return this.config[colIndex].locked === true;
8228 setLocked : function(colIndex, value, suppressEvent){
8229 if(this.isLocked(colIndex) == value){
8232 this.config[colIndex].locked = value;
8234 this.fireEvent("columnlockchange", this, colIndex, value);
8238 getTotalLockedWidth : function(){
8240 for(var i = 0; i < this.config.length; i++){
8241 if(this.isLocked(i) && !this.isHidden(i)){
8242 this.totalWidth += this.getColumnWidth(i);
8248 getLockedCount : function(){
8249 for(var i = 0, len = this.config.length; i < len; i++){
8250 if(!this.isLocked(i)){
8255 return this.config.length;
8259 * Returns the number of columns.
8262 getColumnCount : function(visibleOnly){
8263 if(visibleOnly === true){
8265 for(var i = 0, len = this.config.length; i < len; i++){
8266 if(!this.isHidden(i)){
8272 return this.config.length;
8276 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8277 * @param {Function} fn
8278 * @param {Object} scope (optional)
8279 * @return {Array} result
8281 getColumnsBy : function(fn, scope){
8283 for(var i = 0, len = this.config.length; i < len; i++){
8284 var c = this.config[i];
8285 if(fn.call(scope||this, c, i) === true){
8293 * Returns true if the specified column is sortable.
8294 * @param {Number} col The column index
8297 isSortable : function(col){
8298 if(typeof this.config[col].sortable == "undefined"){
8299 return this.defaultSortable;
8301 return this.config[col].sortable;
8305 * Returns the rendering (formatting) function defined for the column.
8306 * @param {Number} col The column index.
8307 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8309 getRenderer : function(col){
8310 if(!this.config[col].renderer){
8311 return Roo.grid.ColumnModel.defaultRenderer;
8313 return this.config[col].renderer;
8317 * Sets the rendering (formatting) function for a column.
8318 * @param {Number} col The column index
8319 * @param {Function} fn The function to use to process the cell's raw data
8320 * to return HTML markup for the grid view. The render function is called with
8321 * the following parameters:<ul>
8322 * <li>Data value.</li>
8323 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8324 * <li>css A CSS style string to apply to the table cell.</li>
8325 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8326 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8327 * <li>Row index</li>
8328 * <li>Column index</li>
8329 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8331 setRenderer : function(col, fn){
8332 this.config[col].renderer = fn;
8336 * Returns the width for the specified column.
8337 * @param {Number} col The column index
8338 * @param (optional) {String} gridSize bootstrap width size.
8341 getColumnWidth : function(col, gridSize)
8343 var cfg = this.config[col];
8345 if (typeof(gridSize) == 'undefined') {
8346 return cfg.width * 1 || this.defaultWidth;
8348 if (gridSize === false) { // if we set it..
8349 return cfg.width || false;
8351 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8353 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8354 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8357 return cfg[ sizes[i] ];
8364 * Sets the width for a column.
8365 * @param {Number} col The column index
8366 * @param {Number} width The new width
8368 setColumnWidth : function(col, width, suppressEvent){
8369 this.config[col].width = width;
8370 this.totalWidth = null;
8372 this.fireEvent("widthchange", this, col, width);
8377 * Returns the total width of all columns.
8378 * @param {Boolean} includeHidden True to include hidden column widths
8381 getTotalWidth : function(includeHidden){
8382 if(!this.totalWidth){
8383 this.totalWidth = 0;
8384 for(var i = 0, len = this.config.length; i < len; i++){
8385 if(includeHidden || !this.isHidden(i)){
8386 this.totalWidth += this.getColumnWidth(i);
8390 return this.totalWidth;
8394 * Returns the header for the specified column.
8395 * @param {Number} col The column index
8398 getColumnHeader : function(col){
8399 return this.config[col].header;
8403 * Sets the header for a column.
8404 * @param {Number} col The column index
8405 * @param {String} header The new header
8407 setColumnHeader : function(col, header){
8408 this.config[col].header = header;
8409 this.fireEvent("headerchange", this, col, header);
8413 * Returns the tooltip for the specified column.
8414 * @param {Number} col The column index
8417 getColumnTooltip : function(col){
8418 return this.config[col].tooltip;
8421 * Sets the tooltip for a column.
8422 * @param {Number} col The column index
8423 * @param {String} tooltip The new tooltip
8425 setColumnTooltip : function(col, tooltip){
8426 this.config[col].tooltip = tooltip;
8430 * Returns the dataIndex for the specified column.
8431 * @param {Number} col The column index
8434 getDataIndex : function(col){
8435 return this.config[col].dataIndex;
8439 * Sets the dataIndex for a column.
8440 * @param {Number} col The column index
8441 * @param {Number} dataIndex The new dataIndex
8443 setDataIndex : function(col, dataIndex){
8444 this.config[col].dataIndex = dataIndex;
8450 * Returns true if the cell is editable.
8451 * @param {Number} colIndex The column index
8452 * @param {Number} rowIndex The row index - this is nto actually used..?
8455 isCellEditable : function(colIndex, rowIndex){
8456 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8460 * Returns the editor defined for the cell/column.
8461 * return false or null to disable editing.
8462 * @param {Number} colIndex The column index
8463 * @param {Number} rowIndex The row index
8466 getCellEditor : function(colIndex, rowIndex){
8467 return this.config[colIndex].editor;
8471 * Sets if a column is editable.
8472 * @param {Number} col The column index
8473 * @param {Boolean} editable True if the column is editable
8475 setEditable : function(col, editable){
8476 this.config[col].editable = editable;
8481 * Returns true if the column is hidden.
8482 * @param {Number} colIndex The column index
8485 isHidden : function(colIndex){
8486 return this.config[colIndex].hidden;
8491 * Returns true if the column width cannot be changed
8493 isFixed : function(colIndex){
8494 return this.config[colIndex].fixed;
8498 * Returns true if the column can be resized
8501 isResizable : function(colIndex){
8502 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8505 * Sets if a column is hidden.
8506 * @param {Number} colIndex The column index
8507 * @param {Boolean} hidden True if the column is hidden
8509 setHidden : function(colIndex, hidden){
8510 this.config[colIndex].hidden = hidden;
8511 this.totalWidth = null;
8512 this.fireEvent("hiddenchange", this, colIndex, hidden);
8516 * Sets the editor for a column.
8517 * @param {Number} col The column index
8518 * @param {Object} editor The editor object
8520 setEditor : function(col, editor){
8521 this.config[col].editor = editor;
8524 * Add a column (experimental...) - defaults to adding to the end..
8525 * @param {Object} config
8527 addColumn : function(c)
8530 var i = this.config.length;
8533 if(typeof c.dataIndex == "undefined"){
8536 if(typeof c.renderer == "string"){
8537 c.renderer = Roo.util.Format[c.renderer];
8539 if(typeof c.id == "undefined"){
8542 if(c.editor && c.editor.xtype){
8543 c.editor = Roo.factory(c.editor, Roo.grid);
8545 if(c.editor && c.editor.isFormField){
8546 c.editor = new Roo.grid.GridEditor(c.editor);
8548 this.lookup[c.id] = c;
8553 Roo.grid.ColumnModel.defaultRenderer = function(value)
8555 if(typeof value == "object") {
8558 if(typeof value == "string" && value.length < 1){
8562 return String.format("{0}", value);
8565 // Alias for backwards compatibility
8566 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8569 * Ext JS Library 1.1.1
8570 * Copyright(c) 2006-2007, Ext JS, LLC.
8572 * Originally Released Under LGPL - original licence link has changed is not relivant.
8575 * <script type="text/javascript">
8579 * @class Roo.LoadMask
8580 * A simple utility class for generically masking elements while loading data. If the element being masked has
8581 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8582 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8583 * element's UpdateManager load indicator and will be destroyed after the initial load.
8585 * Create a new LoadMask
8586 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8587 * @param {Object} config The config object
8589 Roo.LoadMask = function(el, config){
8590 this.el = Roo.get(el);
8591 Roo.apply(this, config);
8593 this.store.on('beforeload', this.onBeforeLoad, this);
8594 this.store.on('load', this.onLoad, this);
8595 this.store.on('loadexception', this.onLoadException, this);
8596 this.removeMask = false;
8598 var um = this.el.getUpdateManager();
8599 um.showLoadIndicator = false; // disable the default indicator
8600 um.on('beforeupdate', this.onBeforeLoad, this);
8601 um.on('update', this.onLoad, this);
8602 um.on('failure', this.onLoad, this);
8603 this.removeMask = true;
8607 Roo.LoadMask.prototype = {
8609 * @cfg {Boolean} removeMask
8610 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8611 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8616 * The text to display in a centered loading message box (defaults to 'Loading...')
8620 * @cfg {String} msgCls
8621 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8623 msgCls : 'x-mask-loading',
8626 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8632 * Disables the mask to prevent it from being displayed
8634 disable : function(){
8635 this.disabled = true;
8639 * Enables the mask so that it can be displayed
8641 enable : function(){
8642 this.disabled = false;
8645 onLoadException : function()
8649 if (typeof(arguments[3]) != 'undefined') {
8650 Roo.MessageBox.alert("Error loading",arguments[3]);
8654 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8655 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8662 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8667 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8671 onBeforeLoad : function(){
8673 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8678 destroy : function(){
8680 this.store.un('beforeload', this.onBeforeLoad, this);
8681 this.store.un('load', this.onLoad, this);
8682 this.store.un('loadexception', this.onLoadException, this);
8684 var um = this.el.getUpdateManager();
8685 um.un('beforeupdate', this.onBeforeLoad, this);
8686 um.un('update', this.onLoad, this);
8687 um.un('failure', this.onLoad, this);
8691 * @class Roo.bootstrap.Table
8693 * @extends Roo.bootstrap.Component
8694 * @children Roo.bootstrap.TableBody
8695 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8696 * Similar to Roo.grid.Grid
8698 var table = Roo.factory({
8700 xns : Roo.bootstrap,
8701 autoSizeColumns: true,
8708 sortInfo : { direction : 'ASC', field: 'name' },
8710 xtype : 'HttpProxy',
8713 url : 'https://example.com/some.data.url.json'
8716 xtype : 'JsonReader',
8718 fields : [ 'id', 'name', whatever' ],
8725 xtype : 'ColumnModel',
8729 dataIndex : 'is_in_group',
8732 renderer : function(v, x , r) {
8734 return String.format("{0}", v)
8740 xtype : 'RowSelectionModel',
8741 xns : Roo.bootstrap.Table
8742 // you can add listeners to catch selection change here....
8748 grid.render(Roo.get("some-div"));
8751 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8756 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8757 * @cfg {Roo.data.Store} store The data store to use
8758 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
8760 * @cfg {String} cls table class
8763 * @cfg {boolean} striped Should the rows be alternative striped
8764 * @cfg {boolean} bordered Add borders to the table
8765 * @cfg {boolean} hover Add hover highlighting
8766 * @cfg {boolean} condensed Format condensed
8767 * @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,
8768 * also adds table-responsive (see bootstrap docs for details)
8769 * @cfg {Boolean} loadMask (true|false) default false
8770 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8771 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8772 * @cfg {Boolean} rowSelection (true|false) default false
8773 * @cfg {Boolean} cellSelection (true|false) default false
8774 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8775 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8776 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8777 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8778 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8779 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8782 * Create a new Table
8783 * @param {Object} config The config object
8786 Roo.bootstrap.Table = function(config)
8788 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8791 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8792 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8793 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8794 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8796 this.view = this; // compat with grid.
8798 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8800 this.sm.grid = this;
8801 this.selModel = Roo.factory(this.sm, Roo.grid);
8802 this.sm = this.selModel;
8803 this.sm.xmodule = this.xmodule || false;
8806 if (this.cm && typeof(this.cm.config) == 'undefined') {
8807 this.colModel = new Roo.grid.ColumnModel(this.cm);
8808 this.cm = this.colModel;
8809 this.cm.xmodule = this.xmodule || false;
8812 this.store= Roo.factory(this.store, Roo.data);
8813 this.ds = this.store;
8814 this.ds.xmodule = this.xmodule || false;
8817 if (this.footer && this.store) {
8818 this.footer.dataSource = this.ds;
8819 this.footer = Roo.factory(this.footer);
8826 * Fires when a cell is clicked
8827 * @param {Roo.bootstrap.Table} this
8828 * @param {Roo.Element} el
8829 * @param {Number} rowIndex
8830 * @param {Number} columnIndex
8831 * @param {Roo.EventObject} e
8835 * @event celldblclick
8836 * Fires when a cell is double clicked
8837 * @param {Roo.bootstrap.Table} this
8838 * @param {Roo.Element} el
8839 * @param {Number} rowIndex
8840 * @param {Number} columnIndex
8841 * @param {Roo.EventObject} e
8843 "celldblclick" : true,
8846 * Fires when a row is clicked
8847 * @param {Roo.bootstrap.Table} this
8848 * @param {Roo.Element} el
8849 * @param {Number} rowIndex
8850 * @param {Roo.EventObject} e
8854 * @event rowdblclick
8855 * Fires when a row is double clicked
8856 * @param {Roo.bootstrap.Table} this
8857 * @param {Roo.Element} el
8858 * @param {Number} rowIndex
8859 * @param {Roo.EventObject} e
8861 "rowdblclick" : true,
8864 * Fires when a mouseover occur
8865 * @param {Roo.bootstrap.Table} this
8866 * @param {Roo.Element} el
8867 * @param {Number} rowIndex
8868 * @param {Number} columnIndex
8869 * @param {Roo.EventObject} e
8874 * Fires when a mouseout occur
8875 * @param {Roo.bootstrap.Table} this
8876 * @param {Roo.Element} el
8877 * @param {Number} rowIndex
8878 * @param {Number} columnIndex
8879 * @param {Roo.EventObject} e
8884 * Fires when a row is rendered, so you can change add a style to it.
8885 * @param {Roo.bootstrap.Table} this
8886 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8890 * @event rowsrendered
8891 * Fires when all the rows have been rendered
8892 * @param {Roo.bootstrap.Table} this
8894 'rowsrendered' : true,
8896 * @event contextmenu
8897 * The raw contextmenu event for the entire grid.
8898 * @param {Roo.EventObject} e
8900 "contextmenu" : true,
8902 * @event rowcontextmenu
8903 * Fires when a row is right clicked
8904 * @param {Roo.bootstrap.Table} this
8905 * @param {Number} rowIndex
8906 * @param {Roo.EventObject} e
8908 "rowcontextmenu" : true,
8910 * @event cellcontextmenu
8911 * Fires when a cell is right clicked
8912 * @param {Roo.bootstrap.Table} this
8913 * @param {Number} rowIndex
8914 * @param {Number} cellIndex
8915 * @param {Roo.EventObject} e
8917 "cellcontextmenu" : true,
8919 * @event headercontextmenu
8920 * Fires when a header is right clicked
8921 * @param {Roo.bootstrap.Table} this
8922 * @param {Number} columnIndex
8923 * @param {Roo.EventObject} e
8925 "headercontextmenu" : true,
8928 * The raw mousedown event for the entire grid.
8929 * @param {Roo.EventObject} e
8936 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8952 enableColumnResize: true,
8954 rowSelection : false,
8955 cellSelection : false,
8958 minColumnWidth : 50,
8960 // Roo.Element - the tbody
8961 bodyEl: false, // <tbody> Roo.Element - thead element
8962 headEl: false, // <thead> Roo.Element - thead element
8963 resizeProxy : false, // proxy element for dragging?
8967 container: false, // used by gridpanel...
8973 auto_hide_footer : false,
8975 view: false, // actually points to this..
8977 getAutoCreate : function()
8979 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8986 // this get's auto added by panel.Grid
8987 if (this.scrollBody) {
8988 cfg.cls += ' table-body-fixed';
8991 cfg.cls += ' table-striped';
8995 cfg.cls += ' table-hover';
8997 if (this.bordered) {
8998 cfg.cls += ' table-bordered';
9000 if (this.condensed) {
9001 cfg.cls += ' table-condensed';
9004 if (this.responsive) {
9005 cfg.cls += ' table-responsive';
9009 cfg.cls+= ' ' +this.cls;
9015 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9018 if(this.store || this.cm){
9019 if(this.headerShow){
9020 cfg.cn.push(this.renderHeader());
9023 cfg.cn.push(this.renderBody());
9025 if(this.footerShow){
9026 cfg.cn.push(this.renderFooter());
9028 // where does this come from?
9029 //cfg.cls+= ' TableGrid';
9032 return { cn : [ cfg ] };
9035 initEvents : function()
9037 if(!this.store || !this.cm){
9040 if (this.selModel) {
9041 this.selModel.initEvents();
9045 //Roo.log('initEvents with ds!!!!');
9047 this.bodyEl = this.el.select('tbody', true).first();
9048 this.headEl = this.el.select('thead', true).first();
9049 this.mainFoot = this.el.select('tfoot', true).first();
9054 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9055 e.on('click', this.sort, this);
9059 // why is this done????? = it breaks dialogs??
9060 //this.parent().el.setStyle('position', 'relative');
9064 this.footer.parentId = this.id;
9065 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9068 this.el.select('tfoot tr td').first().addClass('hide');
9073 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9076 this.store.on('load', this.onLoad, this);
9077 this.store.on('beforeload', this.onBeforeLoad, this);
9078 this.store.on('update', this.onUpdate, this);
9079 this.store.on('add', this.onAdd, this);
9080 this.store.on("clear", this.clear, this);
9082 this.el.on("contextmenu", this.onContextMenu, this);
9085 this.cm.on("headerchange", this.onHeaderChange, this);
9086 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9088 //?? does bodyEl get replaced on render?
9089 this.bodyEl.on("click", this.onClick, this);
9090 this.bodyEl.on("dblclick", this.onDblClick, this);
9091 this.bodyEl.on('scroll', this.onBodyScroll, this);
9093 // guessing mainbody will work - this relays usually caught by selmodel at present.
9094 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9097 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9100 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9101 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9106 // Compatibility with grid - we implement all the view features at present.
9107 getView : function()
9112 initCSS : function()
9116 var cm = this.cm, styles = [];
9117 this.CSS.removeStyleSheet(this.id + '-cssrules');
9118 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9119 // we can honour xs/sm/md/xl as widths...
9120 // we first have to decide what widht we are currently at...
9121 var sz = Roo.getGridSize();
9125 var cols = []; // visable cols.
9127 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9128 var w = cm.getColumnWidth(i, false);
9130 cols.push( { rel : false, abs : 0 });
9134 cols.push( { rel : false, abs : w });
9136 last = i; // not really..
9139 var w = cm.getColumnWidth(i, sz);
9144 cols.push( { rel : w, abs : false });
9147 var avail = this.bodyEl.dom.clientWidth - total_abs;
9149 var unitWidth = Math.floor(avail / total);
9150 var rem = avail - (unitWidth * total);
9152 var hidden, width, pos = 0 , splithide , left;
9153 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9155 hidden = 'display:none;';
9157 width = 'width:0px;';
9159 if(!cm.isHidden(i)){
9163 // we can honour xs/sm/md/xl ?
9164 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9166 hidden = 'display:none;';
9168 // width should return a small number...
9170 w+=rem; // add the remaining with..
9173 left = "left:" + (pos -4) + "px;";
9174 width = "width:" + w+ "px;";
9177 if (this.responsive) {
9180 hidden = cm.isHidden(i) ? 'display:none;' : '';
9181 splithide = 'display: none;';
9184 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9187 splithide = 'display:none;';
9190 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9191 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9196 //Roo.log(styles.join(''));
9197 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9203 onContextMenu : function(e, t)
9205 this.processEvent("contextmenu", e);
9208 processEvent : function(name, e)
9210 if (name != 'touchstart' ) {
9211 this.fireEvent(name, e);
9214 var t = e.getTarget();
9216 var cell = Roo.get(t);
9222 if(cell.findParent('tfoot', false, true)){
9226 if(cell.findParent('thead', false, true)){
9228 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9229 cell = Roo.get(t).findParent('th', false, true);
9231 Roo.log("failed to find th in thead?");
9232 Roo.log(e.getTarget());
9237 var cellIndex = cell.dom.cellIndex;
9239 var ename = name == 'touchstart' ? 'click' : name;
9240 this.fireEvent("header" + ename, this, cellIndex, e);
9245 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9246 cell = Roo.get(t).findParent('td', false, true);
9248 Roo.log("failed to find th in tbody?");
9249 Roo.log(e.getTarget());
9254 var row = cell.findParent('tr', false, true);
9255 var cellIndex = cell.dom.cellIndex;
9256 var rowIndex = row.dom.rowIndex - 1;
9260 this.fireEvent("row" + name, this, rowIndex, e);
9264 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9270 onMouseover : function(e, el)
9272 var cell = Roo.get(el);
9278 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9279 cell = cell.findParent('td', false, true);
9282 var row = cell.findParent('tr', false, true);
9283 var cellIndex = cell.dom.cellIndex;
9284 var rowIndex = row.dom.rowIndex - 1; // start from 0
9286 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9290 onMouseout : function(e, el)
9292 var cell = Roo.get(el);
9298 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9299 cell = cell.findParent('td', false, true);
9302 var row = cell.findParent('tr', false, true);
9303 var cellIndex = cell.dom.cellIndex;
9304 var rowIndex = row.dom.rowIndex - 1; // start from 0
9306 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9310 onClick : function(e, el)
9312 var cell = Roo.get(el);
9314 if(!cell || (!this.cellSelection && !this.rowSelection)){
9318 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9319 cell = cell.findParent('td', false, true);
9322 if(!cell || typeof(cell) == 'undefined'){
9326 var row = cell.findParent('tr', false, true);
9328 if(!row || typeof(row) == 'undefined'){
9332 var cellIndex = cell.dom.cellIndex;
9333 var rowIndex = this.getRowIndex(row);
9335 // why??? - should these not be based on SelectionModel?
9336 //if(this.cellSelection){
9337 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9340 //if(this.rowSelection){
9341 this.fireEvent('rowclick', this, row, rowIndex, e);
9346 onDblClick : function(e,el)
9348 var cell = Roo.get(el);
9350 if(!cell || (!this.cellSelection && !this.rowSelection)){
9354 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9355 cell = cell.findParent('td', false, true);
9358 if(!cell || typeof(cell) == 'undefined'){
9362 var row = cell.findParent('tr', false, true);
9364 if(!row || typeof(row) == 'undefined'){
9368 var cellIndex = cell.dom.cellIndex;
9369 var rowIndex = this.getRowIndex(row);
9371 if(this.cellSelection){
9372 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9375 if(this.rowSelection){
9376 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9379 findRowIndex : function(el)
9381 var cell = Roo.get(el);
9385 var row = cell.findParent('tr', false, true);
9387 if(!row || typeof(row) == 'undefined'){
9390 return this.getRowIndex(row);
9392 sort : function(e,el)
9394 var col = Roo.get(el);
9396 if(!col.hasClass('sortable')){
9400 var sort = col.attr('sort');
9403 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9407 this.store.sortInfo = {field : sort, direction : dir};
9410 Roo.log("calling footer first");
9411 this.footer.onClick('first');
9414 this.store.load({ params : { start : 0 } });
9418 renderHeader : function()
9426 this.totalWidth = 0;
9428 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9430 var config = cm.config[i];
9434 cls : 'x-hcol-' + i,
9437 html: cm.getColumnHeader(i)
9440 var tooltip = cm.getColumnTooltip(i);
9442 c.tooltip = tooltip;
9448 if(typeof(config.sortable) != 'undefined' && config.sortable){
9449 c.cls += ' sortable';
9450 c.html = '<i class="fa"></i>' + c.html;
9453 // could use BS4 hidden-..-down
9455 if(typeof(config.lgHeader) != 'undefined'){
9456 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9459 if(typeof(config.mdHeader) != 'undefined'){
9460 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9463 if(typeof(config.smHeader) != 'undefined'){
9464 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9467 if(typeof(config.xsHeader) != 'undefined'){
9468 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9475 if(typeof(config.tooltip) != 'undefined'){
9476 c.tooltip = config.tooltip;
9479 if(typeof(config.colspan) != 'undefined'){
9480 c.colspan = config.colspan;
9483 // hidden is handled by CSS now
9485 if(typeof(config.dataIndex) != 'undefined'){
9486 c.sort = config.dataIndex;
9491 if(typeof(config.align) != 'undefined' && config.align.length){
9492 c.style += ' text-align:' + config.align + ';';
9495 /* width is done in CSS
9496 *if(typeof(config.width) != 'undefined'){
9497 c.style += ' width:' + config.width + 'px;';
9498 this.totalWidth += config.width;
9500 this.totalWidth += 100; // assume minimum of 100 per column?
9504 if(typeof(config.cls) != 'undefined'){
9505 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9507 // this is the bit that doesnt reall work at all...
9509 if (this.responsive) {
9512 ['xs','sm','md','lg'].map(function(size){
9514 if(typeof(config[size]) == 'undefined'){
9518 if (!config[size]) { // 0 = hidden
9519 // BS 4 '0' is treated as hide that column and below.
9520 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9524 c.cls += ' col-' + size + '-' + config[size] + (
9525 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9533 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9544 renderBody : function()
9554 colspan : this.cm.getColumnCount()
9564 renderFooter : function()
9574 colspan : this.cm.getColumnCount()
9588 // Roo.log('ds onload');
9593 var ds = this.store;
9595 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9596 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9597 if (_this.store.sortInfo) {
9599 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9600 e.select('i', true).addClass(['fa-arrow-up']);
9603 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9604 e.select('i', true).addClass(['fa-arrow-down']);
9609 var tbody = this.bodyEl;
9611 if(ds.getCount() > 0){
9612 ds.data.each(function(d,rowIndex){
9613 var row = this.renderRow(cm, ds, rowIndex);
9615 tbody.createChild(row);
9619 if(row.cellObjects.length){
9620 Roo.each(row.cellObjects, function(r){
9621 _this.renderCellObject(r);
9628 var tfoot = this.el.select('tfoot', true).first();
9630 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9632 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9634 var total = this.ds.getTotalCount();
9636 if(this.footer.pageSize < total){
9637 this.mainFoot.show();
9641 Roo.each(this.el.select('tbody td', true).elements, function(e){
9642 e.on('mouseover', _this.onMouseover, _this);
9645 Roo.each(this.el.select('tbody td', true).elements, function(e){
9646 e.on('mouseout', _this.onMouseout, _this);
9648 this.fireEvent('rowsrendered', this);
9652 this.initCSS(); /// resize cols
9658 onUpdate : function(ds,record)
9660 this.refreshRow(record);
9664 onRemove : function(ds, record, index, isUpdate){
9665 if(isUpdate !== true){
9666 this.fireEvent("beforerowremoved", this, index, record);
9668 var bt = this.bodyEl.dom;
9670 var rows = this.el.select('tbody > tr', true).elements;
9672 if(typeof(rows[index]) != 'undefined'){
9673 bt.removeChild(rows[index].dom);
9676 // if(bt.rows[index]){
9677 // bt.removeChild(bt.rows[index]);
9680 if(isUpdate !== true){
9681 //this.stripeRows(index);
9682 //this.syncRowHeights(index, index);
9684 this.fireEvent("rowremoved", this, index, record);
9688 onAdd : function(ds, records, rowIndex)
9690 //Roo.log('on Add called');
9691 // - note this does not handle multiple adding very well..
9692 var bt = this.bodyEl.dom;
9693 for (var i =0 ; i < records.length;i++) {
9694 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9695 //Roo.log(records[i]);
9696 //Roo.log(this.store.getAt(rowIndex+i));
9697 this.insertRow(this.store, rowIndex + i, false);
9704 refreshRow : function(record){
9705 var ds = this.store, index;
9706 if(typeof record == 'number'){
9708 record = ds.getAt(index);
9710 index = ds.indexOf(record);
9712 return; // should not happen - but seems to
9715 this.insertRow(ds, index, true);
9717 this.onRemove(ds, record, index+1, true);
9719 //this.syncRowHeights(index, index);
9721 this.fireEvent("rowupdated", this, index, record);
9723 // private - called by RowSelection
9724 onRowSelect : function(rowIndex){
9725 var row = this.getRowDom(rowIndex);
9726 row.addClass(['bg-info','info']);
9728 // private - called by RowSelection
9729 onRowDeselect : function(rowIndex)
9734 var row = this.getRowDom(rowIndex);
9735 row.removeClass(['bg-info','info']);
9738 * Focuses the specified row.
9739 * @param {Number} row The row index
9741 focusRow : function(row)
9743 //Roo.log('GridView.focusRow');
9744 var x = this.bodyEl.dom.scrollLeft;
9745 this.focusCell(row, 0, false);
9746 this.bodyEl.dom.scrollLeft = x;
9750 * Focuses the specified cell.
9751 * @param {Number} row The row index
9752 * @param {Number} col The column index
9753 * @param {Boolean} hscroll false to disable horizontal scrolling
9755 focusCell : function(row, col, hscroll)
9757 //Roo.log('GridView.focusCell');
9758 var el = this.ensureVisible(row, col, hscroll);
9759 // not sure what focusEL achives = it's a <a> pos relative
9760 //this.focusEl.alignTo(el, "tl-tl");
9762 // this.focusEl.focus();
9764 // this.focusEl.focus.defer(1, this.focusEl);
9769 * Scrolls the specified cell into view
9770 * @param {Number} row The row index
9771 * @param {Number} col The column index
9772 * @param {Boolean} hscroll false to disable horizontal scrolling
9774 ensureVisible : function(row, col, hscroll)
9776 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9777 //return null; //disable for testing.
9778 if(typeof row != "number"){
9781 if(row < 0 && row >= this.ds.getCount()){
9784 col = (col !== undefined ? col : 0);
9786 while(cm.isHidden(col)){
9790 var el = this.getCellDom(row, col);
9794 var c = this.bodyEl.dom;
9796 var ctop = parseInt(el.offsetTop, 10);
9797 var cleft = parseInt(el.offsetLeft, 10);
9798 var cbot = ctop + el.offsetHeight;
9799 var cright = cleft + el.offsetWidth;
9801 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9802 var ch = 0; //?? header is not withing the area?
9803 var stop = parseInt(c.scrollTop, 10);
9804 var sleft = parseInt(c.scrollLeft, 10);
9805 var sbot = stop + ch;
9806 var sright = sleft + c.clientWidth;
9808 Roo.log('GridView.ensureVisible:' +
9810 ' c.clientHeight:' + c.clientHeight +
9811 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9820 //Roo.log("set scrolltop to ctop DISABLE?");
9821 }else if(cbot > sbot){
9822 //Roo.log("set scrolltop to cbot-ch");
9823 c.scrollTop = cbot-ch;
9826 if(hscroll !== false){
9828 c.scrollLeft = cleft;
9829 }else if(cright > sright){
9830 c.scrollLeft = cright-c.clientWidth;
9838 insertRow : function(dm, rowIndex, isUpdate){
9841 this.fireEvent("beforerowsinserted", this, rowIndex);
9843 //var s = this.getScrollState();
9844 var row = this.renderRow(this.cm, this.store, rowIndex);
9845 // insert before rowIndex..
9846 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9850 if(row.cellObjects.length){
9851 Roo.each(row.cellObjects, function(r){
9852 _this.renderCellObject(r);
9857 this.fireEvent("rowsinserted", this, rowIndex);
9858 //this.syncRowHeights(firstRow, lastRow);
9859 //this.stripeRows(firstRow);
9866 getRowDom : function(rowIndex)
9868 var rows = this.el.select('tbody > tr', true).elements;
9870 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9873 getCellDom : function(rowIndex, colIndex)
9875 var row = this.getRowDom(rowIndex);
9876 if (row === false) {
9879 var cols = row.select('td', true).elements;
9880 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9884 // returns the object tree for a tr..
9887 renderRow : function(cm, ds, rowIndex)
9889 var d = ds.getAt(rowIndex);
9893 cls : 'x-row-' + rowIndex,
9897 var cellObjects = [];
9899 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9900 var config = cm.config[i];
9902 var renderer = cm.getRenderer(i);
9906 if(typeof(renderer) !== 'undefined'){
9907 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9909 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9910 // and are rendered into the cells after the row is rendered - using the id for the element.
9912 if(typeof(value) === 'object'){
9922 rowIndex : rowIndex,
9927 this.fireEvent('rowclass', this, rowcfg);
9931 // this might end up displaying HTML?
9932 // this is too messy... - better to only do it on columsn you know are going to be too long
9933 //tooltip : (typeof(value) === 'object') ? '' : value,
9934 cls : rowcfg.rowClass + ' x-col-' + i,
9936 html: (typeof(value) === 'object') ? '' : value
9943 if(typeof(config.colspan) != 'undefined'){
9944 td.colspan = config.colspan;
9949 if(typeof(config.align) != 'undefined' && config.align.length){
9950 td.style += ' text-align:' + config.align + ';';
9952 if(typeof(config.valign) != 'undefined' && config.valign.length){
9953 td.style += ' vertical-align:' + config.valign + ';';
9956 if(typeof(config.width) != 'undefined'){
9957 td.style += ' width:' + config.width + 'px;';
9961 if(typeof(config.cursor) != 'undefined'){
9962 td.style += ' cursor:' + config.cursor + ';';
9965 if(typeof(config.cls) != 'undefined'){
9966 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9968 if (this.responsive) {
9969 ['xs','sm','md','lg'].map(function(size){
9971 if(typeof(config[size]) == 'undefined'){
9977 if (!config[size]) { // 0 = hidden
9978 // BS 4 '0' is treated as hide that column and below.
9979 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9983 td.cls += ' col-' + size + '-' + config[size] + (
9984 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9994 row.cellObjects = cellObjects;
10002 onBeforeLoad : function()
10011 this.el.select('tbody', true).first().dom.innerHTML = '';
10014 * Show or hide a row.
10015 * @param {Number} rowIndex to show or hide
10016 * @param {Boolean} state hide
10018 setRowVisibility : function(rowIndex, state)
10020 var bt = this.bodyEl.dom;
10022 var rows = this.el.select('tbody > tr', true).elements;
10024 if(typeof(rows[rowIndex]) == 'undefined'){
10027 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10032 getSelectionModel : function(){
10033 if(!this.selModel){
10034 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10036 return this.selModel;
10039 * Render the Roo.bootstrap object from renderder
10041 renderCellObject : function(r)
10045 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10047 var t = r.cfg.render(r.container);
10050 Roo.each(r.cfg.cn, function(c){
10052 container: t.getChildContainer(),
10055 _this.renderCellObject(child);
10060 * get the Row Index from a dom element.
10061 * @param {Roo.Element} row The row to look for
10062 * @returns {Number} the row
10064 getRowIndex : function(row)
10068 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10079 * get the header TH element for columnIndex
10080 * @param {Number} columnIndex
10081 * @returns {Roo.Element}
10083 getHeaderIndex: function(colIndex)
10085 var cols = this.headEl.select('th', true).elements;
10086 return cols[colIndex];
10089 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10090 * @param {domElement} cell to look for
10091 * @returns {Number} the column
10093 getCellIndex : function(cell)
10095 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10097 return parseInt(id[1], 10);
10102 * Returns the grid's underlying element = used by panel.Grid
10103 * @return {Element} The element
10105 getGridEl : function(){
10109 * Forces a resize - used by panel.Grid
10110 * @return {Element} The element
10112 autoSize : function()
10114 //var ctr = Roo.get(this.container.dom.parentElement);
10115 var ctr = Roo.get(this.el.dom);
10117 var thd = this.getGridEl().select('thead',true).first();
10118 var tbd = this.getGridEl().select('tbody', true).first();
10119 var tfd = this.getGridEl().select('tfoot', true).first();
10121 var cw = ctr.getWidth();
10122 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10126 tbd.setWidth(ctr.getWidth());
10127 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10128 // this needs fixing for various usage - currently only hydra job advers I think..
10130 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10132 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10135 cw = Math.max(cw, this.totalWidth);
10136 this.getGridEl().select('tbody tr',true).setWidth(cw);
10139 // resize 'expandable coloumn?
10141 return; // we doe not have a view in this design..
10144 onBodyScroll: function()
10146 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10148 this.headEl.setStyle({
10149 'position' : 'relative',
10150 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10156 var scrollHeight = this.bodyEl.dom.scrollHeight;
10158 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10160 var height = this.bodyEl.getHeight();
10162 if(scrollHeight - height == scrollTop) {
10164 var total = this.ds.getTotalCount();
10166 if(this.footer.cursor + this.footer.pageSize < total){
10168 this.footer.ds.load({
10170 start : this.footer.cursor + this.footer.pageSize,
10171 limit : this.footer.pageSize
10180 onColumnSplitterMoved : function(i, diff)
10182 this.userResized = true;
10184 var cm = this.colModel;
10186 var w = this.getHeaderIndex(i).getWidth() + diff;
10189 cm.setColumnWidth(i, w, true);
10191 //var cid = cm.getColumnId(i); << not used in this version?
10192 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10194 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10195 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10196 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10198 //this.updateSplitters();
10199 //this.layout(); << ??
10200 this.fireEvent("columnresize", i, w);
10202 onHeaderChange : function()
10204 var header = this.renderHeader();
10205 var table = this.el.select('table', true).first();
10207 this.headEl.remove();
10208 this.headEl = table.createChild(header, this.bodyEl, false);
10210 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10211 e.on('click', this.sort, this);
10214 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10215 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10220 onHiddenChange : function(colModel, colIndex, hidden)
10223 this.cm.setHidden()
10224 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10225 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10227 this.CSS.updateRule(thSelector, "display", "");
10228 this.CSS.updateRule(tdSelector, "display", "");
10231 this.CSS.updateRule(thSelector, "display", "none");
10232 this.CSS.updateRule(tdSelector, "display", "none");
10235 // onload calls initCSS()
10236 this.onHeaderChange();
10240 setColumnWidth: function(col_index, width)
10242 // width = "md-2 xs-2..."
10243 if(!this.colModel.config[col_index]) {
10247 var w = width.split(" ");
10249 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10251 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10254 for(var j = 0; j < w.length; j++) {
10260 var size_cls = w[j].split("-");
10262 if(!Number.isInteger(size_cls[1] * 1)) {
10266 if(!this.colModel.config[col_index][size_cls[0]]) {
10270 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10274 h_row[0].classList.replace(
10275 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10276 "col-"+size_cls[0]+"-"+size_cls[1]
10279 for(var i = 0; i < rows.length; i++) {
10281 var size_cls = w[j].split("-");
10283 if(!Number.isInteger(size_cls[1] * 1)) {
10287 if(!this.colModel.config[col_index][size_cls[0]]) {
10291 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10295 rows[i].classList.replace(
10296 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10297 "col-"+size_cls[0]+"-"+size_cls[1]
10301 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10306 // currently only used to find the split on drag..
10307 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10312 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10313 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10322 * @class Roo.bootstrap.TableCell
10323 * @extends Roo.bootstrap.Component
10324 * @children Roo.bootstrap.Component
10325 * @parent Roo.bootstrap.TableRow
10326 * Bootstrap TableCell class
10328 * @cfg {String} html cell contain text
10329 * @cfg {String} cls cell class
10330 * @cfg {String} tag cell tag (td|th) default td
10331 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10332 * @cfg {String} align Aligns the content in a cell
10333 * @cfg {String} axis Categorizes cells
10334 * @cfg {String} bgcolor Specifies the background color of a cell
10335 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10336 * @cfg {Number} colspan Specifies the number of columns a cell should span
10337 * @cfg {String} headers Specifies one or more header cells a cell is related to
10338 * @cfg {Number} height Sets the height of a cell
10339 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10340 * @cfg {Number} rowspan Sets the number of rows a cell should span
10341 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10342 * @cfg {String} valign Vertical aligns the content in a cell
10343 * @cfg {Number} width Specifies the width of a cell
10346 * Create a new TableCell
10347 * @param {Object} config The config object
10350 Roo.bootstrap.TableCell = function(config){
10351 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10354 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10374 getAutoCreate : function(){
10375 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10382 cfg.tag = this.tag;
10395 cfg.align=this.align
10400 if (this.bgcolor) {
10401 cfg.bgcolor=this.bgcolor
10403 if (this.charoff) {
10404 cfg.charoff=this.charoff
10406 if (this.colspan) {
10407 cfg.colspan=this.colspan
10409 if (this.headers) {
10410 cfg.headers=this.headers
10413 cfg.height=this.height
10416 cfg.nowrap=this.nowrap
10418 if (this.rowspan) {
10419 cfg.rowspan=this.rowspan
10422 cfg.scope=this.scope
10425 cfg.valign=this.valign
10428 cfg.width=this.width
10447 * @class Roo.bootstrap.TableRow
10448 * @extends Roo.bootstrap.Component
10449 * @children Roo.bootstrap.TableCell
10450 * @parent Roo.bootstrap.TableBody
10451 * Bootstrap TableRow class
10452 * @cfg {String} cls row class
10453 * @cfg {String} align Aligns the content in a table row
10454 * @cfg {String} bgcolor Specifies a background color for a table row
10455 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10456 * @cfg {String} valign Vertical aligns the content in a table row
10459 * Create a new TableRow
10460 * @param {Object} config The config object
10463 Roo.bootstrap.TableRow = function(config){
10464 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10467 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10475 getAutoCreate : function(){
10476 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10483 cfg.cls = this.cls;
10486 cfg.align = this.align;
10489 cfg.bgcolor = this.bgcolor;
10492 cfg.charoff = this.charoff;
10495 cfg.valign = this.valign;
10513 * @class Roo.bootstrap.TableBody
10514 * @extends Roo.bootstrap.Component
10515 * @children Roo.bootstrap.TableRow
10516 * @parent Roo.bootstrap.Table
10517 * Bootstrap TableBody class
10518 * @cfg {String} cls element class
10519 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10520 * @cfg {String} align Aligns the content inside the element
10521 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10522 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10525 * Create a new TableBody
10526 * @param {Object} config The config object
10529 Roo.bootstrap.TableBody = function(config){
10530 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10533 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10541 getAutoCreate : function(){
10542 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10552 cfg.tag = this.tag;
10556 cfg.align = this.align;
10559 cfg.charoff = this.charoff;
10562 cfg.valign = this.valign;
10569 // initEvents : function()
10572 // if(!this.store){
10576 // this.store = Roo.factory(this.store, Roo.data);
10577 // this.store.on('load', this.onLoad, this);
10579 // this.store.load();
10583 // onLoad: function ()
10585 // this.fireEvent('load', this);
10595 * Ext JS Library 1.1.1
10596 * Copyright(c) 2006-2007, Ext JS, LLC.
10598 * Originally Released Under LGPL - original licence link has changed is not relivant.
10601 * <script type="text/javascript">
10604 // as we use this in bootstrap.
10605 Roo.namespace('Roo.form');
10607 * @class Roo.form.Action
10608 * Internal Class used to handle form actions
10610 * @param {Roo.form.BasicForm} el The form element or its id
10611 * @param {Object} config Configuration options
10616 // define the action interface
10617 Roo.form.Action = function(form, options){
10619 this.options = options || {};
10622 * Client Validation Failed
10625 Roo.form.Action.CLIENT_INVALID = 'client';
10627 * Server Validation Failed
10630 Roo.form.Action.SERVER_INVALID = 'server';
10632 * Connect to Server Failed
10635 Roo.form.Action.CONNECT_FAILURE = 'connect';
10637 * Reading Data from Server Failed
10640 Roo.form.Action.LOAD_FAILURE = 'load';
10642 Roo.form.Action.prototype = {
10644 failureType : undefined,
10645 response : undefined,
10646 result : undefined,
10648 // interface method
10649 run : function(options){
10653 // interface method
10654 success : function(response){
10658 // interface method
10659 handleResponse : function(response){
10663 // default connection failure
10664 failure : function(response){
10666 this.response = response;
10667 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10668 this.form.afterAction(this, false);
10671 processResponse : function(response){
10672 this.response = response;
10673 if(!response.responseText){
10676 this.result = this.handleResponse(response);
10677 return this.result;
10680 // utility functions used internally
10681 getUrl : function(appendParams){
10682 var url = this.options.url || this.form.url || this.form.el.dom.action;
10684 var p = this.getParams();
10686 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10692 getMethod : function(){
10693 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10696 getParams : function(){
10697 var bp = this.form.baseParams;
10698 var p = this.options.params;
10700 if(typeof p == "object"){
10701 p = Roo.urlEncode(Roo.applyIf(p, bp));
10702 }else if(typeof p == 'string' && bp){
10703 p += '&' + Roo.urlEncode(bp);
10706 p = Roo.urlEncode(bp);
10711 createCallback : function(){
10713 success: this.success,
10714 failure: this.failure,
10716 timeout: (this.form.timeout*1000),
10717 upload: this.form.fileUpload ? this.success : undefined
10722 Roo.form.Action.Submit = function(form, options){
10723 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10726 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10729 haveProgress : false,
10730 uploadComplete : false,
10732 // uploadProgress indicator.
10733 uploadProgress : function()
10735 if (!this.form.progressUrl) {
10739 if (!this.haveProgress) {
10740 Roo.MessageBox.progress("Uploading", "Uploading");
10742 if (this.uploadComplete) {
10743 Roo.MessageBox.hide();
10747 this.haveProgress = true;
10749 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10751 var c = new Roo.data.Connection();
10753 url : this.form.progressUrl,
10758 success : function(req){
10759 //console.log(data);
10763 rdata = Roo.decode(req.responseText)
10765 Roo.log("Invalid data from server..");
10769 if (!rdata || !rdata.success) {
10771 Roo.MessageBox.alert(Roo.encode(rdata));
10774 var data = rdata.data;
10776 if (this.uploadComplete) {
10777 Roo.MessageBox.hide();
10782 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10783 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10786 this.uploadProgress.defer(2000,this);
10789 failure: function(data) {
10790 Roo.log('progress url failed ');
10801 // run get Values on the form, so it syncs any secondary forms.
10802 this.form.getValues();
10804 var o = this.options;
10805 var method = this.getMethod();
10806 var isPost = method == 'POST';
10807 if(o.clientValidation === false || this.form.isValid()){
10809 if (this.form.progressUrl) {
10810 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10811 (new Date() * 1) + '' + Math.random());
10816 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10817 form:this.form.el.dom,
10818 url:this.getUrl(!isPost),
10820 params:isPost ? this.getParams() : null,
10821 isUpload: this.form.fileUpload,
10822 formData : this.form.formData
10825 this.uploadProgress();
10827 }else if (o.clientValidation !== false){ // client validation failed
10828 this.failureType = Roo.form.Action.CLIENT_INVALID;
10829 this.form.afterAction(this, false);
10833 success : function(response)
10835 this.uploadComplete= true;
10836 if (this.haveProgress) {
10837 Roo.MessageBox.hide();
10841 var result = this.processResponse(response);
10842 if(result === true || result.success){
10843 this.form.afterAction(this, true);
10847 this.form.markInvalid(result.errors);
10848 this.failureType = Roo.form.Action.SERVER_INVALID;
10850 this.form.afterAction(this, false);
10852 failure : function(response)
10854 this.uploadComplete= true;
10855 if (this.haveProgress) {
10856 Roo.MessageBox.hide();
10859 this.response = response;
10860 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10861 this.form.afterAction(this, false);
10864 handleResponse : function(response){
10865 if(this.form.errorReader){
10866 var rs = this.form.errorReader.read(response);
10869 for(var i = 0, len = rs.records.length; i < len; i++) {
10870 var r = rs.records[i];
10871 errors[i] = r.data;
10874 if(errors.length < 1){
10878 success : rs.success,
10884 ret = Roo.decode(response.responseText);
10888 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10898 Roo.form.Action.Load = function(form, options){
10899 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10900 this.reader = this.form.reader;
10903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10908 Roo.Ajax.request(Roo.apply(
10909 this.createCallback(), {
10910 method:this.getMethod(),
10911 url:this.getUrl(false),
10912 params:this.getParams()
10916 success : function(response){
10918 var result = this.processResponse(response);
10919 if(result === true || !result.success || !result.data){
10920 this.failureType = Roo.form.Action.LOAD_FAILURE;
10921 this.form.afterAction(this, false);
10924 this.form.clearInvalid();
10925 this.form.setValues(result.data);
10926 this.form.afterAction(this, true);
10929 handleResponse : function(response){
10930 if(this.form.reader){
10931 var rs = this.form.reader.read(response);
10932 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10934 success : rs.success,
10938 return Roo.decode(response.responseText);
10942 Roo.form.Action.ACTION_TYPES = {
10943 'load' : Roo.form.Action.Load,
10944 'submit' : Roo.form.Action.Submit
10953 * @class Roo.bootstrap.Form
10954 * @extends Roo.bootstrap.Component
10955 * @children Roo.bootstrap.Component
10956 * Bootstrap Form class
10957 * @cfg {String} method GET | POST (default POST)
10958 * @cfg {String} labelAlign top | left (default top)
10959 * @cfg {String} align left | right - for navbars
10960 * @cfg {Boolean} loadMask load mask when submit (default true)
10964 * Create a new Form
10965 * @param {Object} config The config object
10969 Roo.bootstrap.Form = function(config){
10971 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10973 Roo.bootstrap.Form.popover.apply();
10977 * @event clientvalidation
10978 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10979 * @param {Form} this
10980 * @param {Boolean} valid true if the form has passed client-side validation
10982 clientvalidation: true,
10984 * @event beforeaction
10985 * Fires before any action is performed. Return false to cancel the action.
10986 * @param {Form} this
10987 * @param {Action} action The action to be performed
10989 beforeaction: true,
10991 * @event actionfailed
10992 * Fires when an action fails.
10993 * @param {Form} this
10994 * @param {Action} action The action that failed
10996 actionfailed : true,
10998 * @event actioncomplete
10999 * Fires when an action is completed.
11000 * @param {Form} this
11001 * @param {Action} action The action that completed
11003 actioncomplete : true
11007 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
11010 * @cfg {String} method
11011 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11015 * @cfg {String} url
11016 * The URL to use for form actions if one isn't supplied in the action options.
11019 * @cfg {Boolean} fileUpload
11020 * Set to true if this form is a file upload.
11024 * @cfg {Object} baseParams
11025 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11029 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11033 * @cfg {Sting} align (left|right) for navbar forms
11038 activeAction : null,
11041 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11042 * element by passing it or its id or mask the form itself by passing in true.
11045 waitMsgTarget : false,
11050 * @cfg {Boolean} errorMask (true|false) default false
11055 * @cfg {Number} maskOffset Default 100
11060 * @cfg {Boolean} maskBody
11064 getAutoCreate : function(){
11068 method : this.method || 'POST',
11069 id : this.id || Roo.id(),
11072 if (this.parent().xtype.match(/^Nav/)) {
11073 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11077 if (this.labelAlign == 'left' ) {
11078 cfg.cls += ' form-horizontal';
11084 initEvents : function()
11086 this.el.on('submit', this.onSubmit, this);
11087 // this was added as random key presses on the form where triggering form submit.
11088 this.el.on('keypress', function(e) {
11089 if (e.getCharCode() != 13) {
11092 // we might need to allow it for textareas.. and some other items.
11093 // check e.getTarget().
11095 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11099 Roo.log("keypress blocked");
11101 e.preventDefault();
11107 onSubmit : function(e){
11112 * Returns true if client-side validation on the form is successful.
11115 isValid : function(){
11116 var items = this.getItems();
11118 var target = false;
11120 items.each(function(f){
11126 Roo.log('invalid field: ' + f.name);
11130 if(!target && f.el.isVisible(true)){
11136 if(this.errorMask && !valid){
11137 Roo.bootstrap.Form.popover.mask(this, target);
11144 * Returns true if any fields in this form have changed since their original load.
11147 isDirty : function(){
11149 var items = this.getItems();
11150 items.each(function(f){
11160 * Performs a predefined action (submit or load) or custom actions you define on this form.
11161 * @param {String} actionName The name of the action type
11162 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11163 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11164 * accept other config options):
11166 Property Type Description
11167 ---------------- --------------- ----------------------------------------------------------------------------------
11168 url String The url for the action (defaults to the form's url)
11169 method String The form method to use (defaults to the form's method, or POST if not defined)
11170 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11171 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11172 validate the form on the client (defaults to false)
11174 * @return {BasicForm} this
11176 doAction : function(action, options){
11177 if(typeof action == 'string'){
11178 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11180 if(this.fireEvent('beforeaction', this, action) !== false){
11181 this.beforeAction(action);
11182 action.run.defer(100, action);
11188 beforeAction : function(action){
11189 var o = action.options;
11194 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11196 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11199 // not really supported yet.. ??
11201 //if(this.waitMsgTarget === true){
11202 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11203 //}else if(this.waitMsgTarget){
11204 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11205 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11207 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11213 afterAction : function(action, success){
11214 this.activeAction = null;
11215 var o = action.options;
11220 Roo.get(document.body).unmask();
11226 //if(this.waitMsgTarget === true){
11227 // this.el.unmask();
11228 //}else if(this.waitMsgTarget){
11229 // this.waitMsgTarget.unmask();
11231 // Roo.MessageBox.updateProgress(1);
11232 // Roo.MessageBox.hide();
11239 Roo.callback(o.success, o.scope, [this, action]);
11240 this.fireEvent('actioncomplete', this, action);
11244 // failure condition..
11245 // we have a scenario where updates need confirming.
11246 // eg. if a locking scenario exists..
11247 // we look for { errors : { needs_confirm : true }} in the response.
11249 (typeof(action.result) != 'undefined') &&
11250 (typeof(action.result.errors) != 'undefined') &&
11251 (typeof(action.result.errors.needs_confirm) != 'undefined')
11254 Roo.log("not supported yet");
11257 Roo.MessageBox.confirm(
11258 "Change requires confirmation",
11259 action.result.errorMsg,
11264 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11274 Roo.callback(o.failure, o.scope, [this, action]);
11275 // show an error message if no failed handler is set..
11276 if (!this.hasListener('actionfailed')) {
11277 Roo.log("need to add dialog support");
11279 Roo.MessageBox.alert("Error",
11280 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11281 action.result.errorMsg :
11282 "Saving Failed, please check your entries or try again"
11287 this.fireEvent('actionfailed', this, action);
11292 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11293 * @param {String} id The value to search for
11296 findField : function(id){
11297 var items = this.getItems();
11298 var field = items.get(id);
11300 items.each(function(f){
11301 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11308 return field || null;
11311 * Mark fields in this form invalid in bulk.
11312 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11313 * @return {BasicForm} this
11315 markInvalid : function(errors){
11316 if(errors instanceof Array){
11317 for(var i = 0, len = errors.length; i < len; i++){
11318 var fieldError = errors[i];
11319 var f = this.findField(fieldError.id);
11321 f.markInvalid(fieldError.msg);
11327 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11328 field.markInvalid(errors[id]);
11332 //Roo.each(this.childForms || [], function (f) {
11333 // f.markInvalid(errors);
11340 * Set values for fields in this form in bulk.
11341 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11342 * @return {BasicForm} this
11344 setValues : function(values){
11345 if(values instanceof Array){ // array of objects
11346 for(var i = 0, len = values.length; i < len; i++){
11348 var f = this.findField(v.id);
11350 f.setValue(v.value);
11351 if(this.trackResetOnLoad){
11352 f.originalValue = f.getValue();
11356 }else{ // object hash
11359 if(typeof values[id] != 'function' && (field = this.findField(id))){
11361 if (field.setFromData &&
11362 field.valueField &&
11363 field.displayField &&
11364 // combos' with local stores can
11365 // be queried via setValue()
11366 // to set their value..
11367 (field.store && !field.store.isLocal)
11371 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11372 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11373 field.setFromData(sd);
11375 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11377 field.setFromData(values);
11380 field.setValue(values[id]);
11384 if(this.trackResetOnLoad){
11385 field.originalValue = field.getValue();
11391 //Roo.each(this.childForms || [], function (f) {
11392 // f.setValues(values);
11399 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11400 * they are returned as an array.
11401 * @param {Boolean} asString
11404 getValues : function(asString){
11405 //if (this.childForms) {
11406 // copy values from the child forms
11407 // Roo.each(this.childForms, function (f) {
11408 // this.setValues(f.getValues());
11414 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11415 if(asString === true){
11418 return Roo.urlDecode(fs);
11422 * Returns the fields in this form as an object with key/value pairs.
11423 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11426 getFieldValues : function(with_hidden)
11428 var items = this.getItems();
11430 items.each(function(f){
11432 if (!f.getName()) {
11436 var v = f.getValue();
11438 if (f.inputType =='radio') {
11439 if (typeof(ret[f.getName()]) == 'undefined') {
11440 ret[f.getName()] = ''; // empty..
11443 if (!f.el.dom.checked) {
11447 v = f.el.dom.value;
11451 if(f.xtype == 'MoneyField'){
11452 ret[f.currencyName] = f.getCurrency();
11455 // not sure if this supported any more..
11456 if ((typeof(v) == 'object') && f.getRawValue) {
11457 v = f.getRawValue() ; // dates..
11459 // combo boxes where name != hiddenName...
11460 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11461 ret[f.name] = f.getRawValue();
11463 ret[f.getName()] = v;
11470 * Clears all invalid messages in this form.
11471 * @return {BasicForm} this
11473 clearInvalid : function(){
11474 var items = this.getItems();
11476 items.each(function(f){
11484 * Resets this form.
11485 * @return {BasicForm} this
11487 reset : function(){
11488 var items = this.getItems();
11489 items.each(function(f){
11493 Roo.each(this.childForms || [], function (f) {
11501 getItems : function()
11503 var r=new Roo.util.MixedCollection(false, function(o){
11504 return o.id || (o.id = Roo.id());
11506 var iter = function(el) {
11513 Roo.each(el.items,function(e) {
11522 hideFields : function(items)
11524 Roo.each(items, function(i){
11526 var f = this.findField(i);
11537 showFields : function(items)
11539 Roo.each(items, function(i){
11541 var f = this.findField(i);
11554 Roo.apply(Roo.bootstrap.Form, {
11570 intervalID : false,
11576 if(this.isApplied){
11581 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11582 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11583 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11584 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11587 this.maskEl.top.enableDisplayMode("block");
11588 this.maskEl.left.enableDisplayMode("block");
11589 this.maskEl.bottom.enableDisplayMode("block");
11590 this.maskEl.right.enableDisplayMode("block");
11592 this.toolTip = new Roo.bootstrap.Tooltip({
11593 cls : 'roo-form-error-popover',
11595 'left' : ['r-l', [-2,0], 'right'],
11596 'right' : ['l-r', [2,0], 'left'],
11597 'bottom' : ['tl-bl', [0,2], 'top'],
11598 'top' : [ 'bl-tl', [0,-2], 'bottom']
11602 this.toolTip.render(Roo.get(document.body));
11604 this.toolTip.el.enableDisplayMode("block");
11606 Roo.get(document.body).on('click', function(){
11610 Roo.get(document.body).on('touchstart', function(){
11614 this.isApplied = true
11617 mask : function(form, target)
11621 this.target = target;
11623 if(!this.form.errorMask || !target.el){
11627 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11629 Roo.log(scrollable);
11631 var ot = this.target.el.calcOffsetsTo(scrollable);
11633 var scrollTo = ot[1] - this.form.maskOffset;
11635 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11637 scrollable.scrollTo('top', scrollTo);
11639 var box = this.target.el.getBox();
11641 var zIndex = Roo.bootstrap.Modal.zIndex++;
11644 this.maskEl.top.setStyle('position', 'absolute');
11645 this.maskEl.top.setStyle('z-index', zIndex);
11646 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11647 this.maskEl.top.setLeft(0);
11648 this.maskEl.top.setTop(0);
11649 this.maskEl.top.show();
11651 this.maskEl.left.setStyle('position', 'absolute');
11652 this.maskEl.left.setStyle('z-index', zIndex);
11653 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11654 this.maskEl.left.setLeft(0);
11655 this.maskEl.left.setTop(box.y - this.padding);
11656 this.maskEl.left.show();
11658 this.maskEl.bottom.setStyle('position', 'absolute');
11659 this.maskEl.bottom.setStyle('z-index', zIndex);
11660 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11661 this.maskEl.bottom.setLeft(0);
11662 this.maskEl.bottom.setTop(box.bottom + this.padding);
11663 this.maskEl.bottom.show();
11665 this.maskEl.right.setStyle('position', 'absolute');
11666 this.maskEl.right.setStyle('z-index', zIndex);
11667 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11668 this.maskEl.right.setLeft(box.right + this.padding);
11669 this.maskEl.right.setTop(box.y - this.padding);
11670 this.maskEl.right.show();
11672 this.toolTip.bindEl = this.target.el;
11674 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11676 var tip = this.target.blankText;
11678 if(this.target.getValue() !== '' ) {
11680 if (this.target.invalidText.length) {
11681 tip = this.target.invalidText;
11682 } else if (this.target.regexText.length){
11683 tip = this.target.regexText;
11687 this.toolTip.show(tip);
11689 this.intervalID = window.setInterval(function() {
11690 Roo.bootstrap.Form.popover.unmask();
11693 window.onwheel = function(){ return false;};
11695 (function(){ this.isMasked = true; }).defer(500, this);
11699 unmask : function()
11701 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11705 this.maskEl.top.setStyle('position', 'absolute');
11706 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11707 this.maskEl.top.hide();
11709 this.maskEl.left.setStyle('position', 'absolute');
11710 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11711 this.maskEl.left.hide();
11713 this.maskEl.bottom.setStyle('position', 'absolute');
11714 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11715 this.maskEl.bottom.hide();
11717 this.maskEl.right.setStyle('position', 'absolute');
11718 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11719 this.maskEl.right.hide();
11721 this.toolTip.hide();
11723 this.toolTip.el.hide();
11725 window.onwheel = function(){ return true;};
11727 if(this.intervalID){
11728 window.clearInterval(this.intervalID);
11729 this.intervalID = false;
11732 this.isMasked = false;
11742 * Ext JS Library 1.1.1
11743 * Copyright(c) 2006-2007, Ext JS, LLC.
11745 * Originally Released Under LGPL - original licence link has changed is not relivant.
11748 * <script type="text/javascript">
11751 * @class Roo.form.VTypes
11752 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11755 Roo.form.VTypes = function(){
11756 // closure these in so they are only created once.
11757 var alpha = /^[a-zA-Z_]+$/;
11758 var alphanum = /^[a-zA-Z0-9_]+$/;
11759 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11760 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11762 // All these messages and functions are configurable
11765 * The function used to validate email addresses
11766 * @param {String} value The email address
11768 'email' : function(v){
11769 return email.test(v);
11772 * The error text to display when the email validation function returns false
11775 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11777 * The keystroke filter mask to be applied on email input
11780 'emailMask' : /[a-z0-9_\.\-@]/i,
11783 * The function used to validate URLs
11784 * @param {String} value The URL
11786 'url' : function(v){
11787 return url.test(v);
11790 * The error text to display when the url validation function returns false
11793 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11796 * The function used to validate alpha values
11797 * @param {String} value The value
11799 'alpha' : function(v){
11800 return alpha.test(v);
11803 * The error text to display when the alpha validation function returns false
11806 'alphaText' : 'This field should only contain letters and _',
11808 * The keystroke filter mask to be applied on alpha input
11811 'alphaMask' : /[a-z_]/i,
11814 * The function used to validate alphanumeric values
11815 * @param {String} value The value
11817 'alphanum' : function(v){
11818 return alphanum.test(v);
11821 * The error text to display when the alphanumeric validation function returns false
11824 'alphanumText' : 'This field should only contain letters, numbers and _',
11826 * The keystroke filter mask to be applied on alphanumeric input
11829 'alphanumMask' : /[a-z0-9_]/i
11839 * @class Roo.bootstrap.Input
11840 * @extends Roo.bootstrap.Component
11841 * Bootstrap Input class
11842 * @cfg {Boolean} disabled is it disabled
11843 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11844 * @cfg {String} name name of the input
11845 * @cfg {string} fieldLabel - the label associated
11846 * @cfg {string} placeholder - placeholder to put in text.
11847 * @cfg {string} before - input group add on before
11848 * @cfg {string} after - input group add on after
11849 * @cfg {string} size - (lg|sm) or leave empty..
11850 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11851 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11852 * @cfg {Number} md colspan out of 12 for computer-sized screens
11853 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11854 * @cfg {string} value default value of the input
11855 * @cfg {Number} labelWidth set the width of label
11856 * @cfg {Number} labellg set the width of label (1-12)
11857 * @cfg {Number} labelmd set the width of label (1-12)
11858 * @cfg {Number} labelsm set the width of label (1-12)
11859 * @cfg {Number} labelxs set the width of label (1-12)
11860 * @cfg {String} labelAlign (top|left)
11861 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11862 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11863 * @cfg {String} indicatorpos (left|right) default left
11864 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11865 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11866 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11868 * @cfg {String} align (left|center|right) Default left
11869 * @cfg {Boolean} forceFeedback (true|false) Default false
11872 * Create a new Input
11873 * @param {Object} config The config object
11876 Roo.bootstrap.Input = function(config){
11878 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11883 * Fires when this field receives input focus.
11884 * @param {Roo.form.Field} this
11889 * Fires when this field loses input focus.
11890 * @param {Roo.form.Field} this
11894 * @event specialkey
11895 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11896 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11897 * @param {Roo.form.Field} this
11898 * @param {Roo.EventObject} e The event object
11903 * Fires just before the field blurs if the field value has changed.
11904 * @param {Roo.form.Field} this
11905 * @param {Mixed} newValue The new value
11906 * @param {Mixed} oldValue The original value
11911 * Fires after the field has been marked as invalid.
11912 * @param {Roo.form.Field} this
11913 * @param {String} msg The validation message
11918 * Fires after the field has been validated with no errors.
11919 * @param {Roo.form.Field} this
11924 * Fires after the key up
11925 * @param {Roo.form.Field} this
11926 * @param {Roo.EventObject} e The event Object
11931 * Fires after the user pastes into input
11932 * @param {Roo.form.Field} this
11933 * @param {Roo.EventObject} e The event Object
11939 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11941 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11942 automatic validation (defaults to "keyup").
11944 validationEvent : "keyup",
11946 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11948 validateOnBlur : true,
11950 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11952 validationDelay : 250,
11954 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11956 focusClass : "x-form-focus", // not needed???
11960 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11962 invalidClass : "has-warning",
11965 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11967 validClass : "has-success",
11970 * @cfg {Boolean} hasFeedback (true|false) default true
11972 hasFeedback : true,
11975 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11977 invalidFeedbackClass : "glyphicon-warning-sign",
11980 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11982 validFeedbackClass : "glyphicon-ok",
11985 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11987 selectOnFocus : false,
11990 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11994 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11999 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12001 disableKeyFilter : false,
12004 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12008 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12012 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12014 blankText : "Please complete this mandatory field",
12017 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12021 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12023 maxLength : Number.MAX_VALUE,
12025 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12027 minLengthText : "The minimum length for this field is {0}",
12029 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12031 maxLengthText : "The maximum length for this field is {0}",
12035 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12036 * If available, this function will be called only after the basic validators all return true, and will be passed the
12037 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12041 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12042 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12043 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12047 * @cfg {String} regexText -- Depricated - use Invalid Text
12052 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12058 autocomplete: false,
12062 inputType : 'text',
12065 placeholder: false,
12070 preventMark: false,
12071 isFormField : true,
12074 labelAlign : false,
12077 formatedValue : false,
12078 forceFeedback : false,
12080 indicatorpos : 'left',
12090 parentLabelAlign : function()
12093 while (parent.parent()) {
12094 parent = parent.parent();
12095 if (typeof(parent.labelAlign) !='undefined') {
12096 return parent.labelAlign;
12103 getAutoCreate : function()
12105 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12111 if(this.inputType != 'hidden'){
12112 cfg.cls = 'form-group' //input-group
12118 type : this.inputType,
12119 value : this.value,
12120 cls : 'form-control',
12121 placeholder : this.placeholder || '',
12122 autocomplete : this.autocomplete || 'new-password'
12124 if (this.inputType == 'file') {
12125 input.style = 'overflow:hidden'; // why not in CSS?
12128 if(this.capture.length){
12129 input.capture = this.capture;
12132 if(this.accept.length){
12133 input.accept = this.accept + "/*";
12137 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12140 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12141 input.maxLength = this.maxLength;
12144 if (this.disabled) {
12145 input.disabled=true;
12148 if (this.readOnly) {
12149 input.readonly=true;
12153 input.name = this.name;
12157 input.cls += ' input-' + this.size;
12161 ['xs','sm','md','lg'].map(function(size){
12162 if (settings[size]) {
12163 cfg.cls += ' col-' + size + '-' + settings[size];
12167 var inputblock = input;
12171 cls: 'glyphicon form-control-feedback'
12174 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12177 cls : 'has-feedback',
12185 if (this.before || this.after) {
12188 cls : 'input-group',
12192 if (this.before && typeof(this.before) == 'string') {
12194 inputblock.cn.push({
12196 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12200 if (this.before && typeof(this.before) == 'object') {
12201 this.before = Roo.factory(this.before);
12203 inputblock.cn.push({
12205 cls : 'roo-input-before input-group-prepend input-group-' +
12206 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12210 inputblock.cn.push(input);
12212 if (this.after && typeof(this.after) == 'string') {
12213 inputblock.cn.push({
12215 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12219 if (this.after && typeof(this.after) == 'object') {
12220 this.after = Roo.factory(this.after);
12222 inputblock.cn.push({
12224 cls : 'roo-input-after input-group-append input-group-' +
12225 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12229 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12230 inputblock.cls += ' has-feedback';
12231 inputblock.cn.push(feedback);
12236 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12237 tooltip : 'This field is required'
12239 if (this.allowBlank ) {
12240 indicator.style = this.allowBlank ? ' display:none' : '';
12242 if (align ==='left' && this.fieldLabel.length) {
12244 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12251 cls : 'control-label col-form-label',
12252 html : this.fieldLabel
12263 var labelCfg = cfg.cn[1];
12264 var contentCfg = cfg.cn[2];
12266 if(this.indicatorpos == 'right'){
12271 cls : 'control-label col-form-label',
12275 html : this.fieldLabel
12289 labelCfg = cfg.cn[0];
12290 contentCfg = cfg.cn[1];
12294 if(this.labelWidth > 12){
12295 labelCfg.style = "width: " + this.labelWidth + 'px';
12298 if(this.labelWidth < 13 && this.labelmd == 0){
12299 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12302 if(this.labellg > 0){
12303 labelCfg.cls += ' col-lg-' + this.labellg;
12304 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12307 if(this.labelmd > 0){
12308 labelCfg.cls += ' col-md-' + this.labelmd;
12309 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12312 if(this.labelsm > 0){
12313 labelCfg.cls += ' col-sm-' + this.labelsm;
12314 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12317 if(this.labelxs > 0){
12318 labelCfg.cls += ' col-xs-' + this.labelxs;
12319 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12323 } else if ( this.fieldLabel.length) {
12330 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12331 tooltip : 'This field is required',
12332 style : this.allowBlank ? ' display:none' : ''
12336 //cls : 'input-group-addon',
12337 html : this.fieldLabel
12345 if(this.indicatorpos == 'right'){
12350 //cls : 'input-group-addon',
12351 html : this.fieldLabel
12356 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12357 tooltip : 'This field is required',
12358 style : this.allowBlank ? ' display:none' : ''
12378 if (this.parentType === 'Navbar' && this.parent().bar) {
12379 cfg.cls += ' navbar-form';
12382 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12383 // on BS4 we do this only if not form
12384 cfg.cls += ' navbar-form';
12392 * return the real input element.
12394 inputEl: function ()
12396 return this.el.select('input.form-control',true).first();
12399 tooltipEl : function()
12401 return this.inputEl();
12404 indicatorEl : function()
12406 if (Roo.bootstrap.version == 4) {
12407 return false; // not enabled in v4 yet.
12410 var indicator = this.el.select('i.roo-required-indicator',true).first();
12420 setDisabled : function(v)
12422 var i = this.inputEl().dom;
12424 i.removeAttribute('disabled');
12428 i.setAttribute('disabled','true');
12430 initEvents : function()
12433 this.inputEl().on("keydown" , this.fireKey, this);
12434 this.inputEl().on("focus", this.onFocus, this);
12435 this.inputEl().on("blur", this.onBlur, this);
12437 this.inputEl().relayEvent('keyup', this);
12438 this.inputEl().relayEvent('paste', this);
12440 this.indicator = this.indicatorEl();
12442 if(this.indicator){
12443 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12446 // reference to original value for reset
12447 this.originalValue = this.getValue();
12448 //Roo.form.TextField.superclass.initEvents.call(this);
12449 if(this.validationEvent == 'keyup'){
12450 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12451 this.inputEl().on('keyup', this.filterValidation, this);
12453 else if(this.validationEvent !== false){
12454 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12457 if(this.selectOnFocus){
12458 this.on("focus", this.preFocus, this);
12461 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12462 this.inputEl().on("keypress", this.filterKeys, this);
12464 this.inputEl().relayEvent('keypress', this);
12467 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12468 this.el.on("click", this.autoSize, this);
12471 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12472 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12475 if (typeof(this.before) == 'object') {
12476 this.before.render(this.el.select('.roo-input-before',true).first());
12478 if (typeof(this.after) == 'object') {
12479 this.after.render(this.el.select('.roo-input-after',true).first());
12482 this.inputEl().on('change', this.onChange, this);
12485 filterValidation : function(e){
12486 if(!e.isNavKeyPress()){
12487 this.validationTask.delay(this.validationDelay);
12491 * Validates the field value
12492 * @return {Boolean} True if the value is valid, else false
12494 validate : function(){
12495 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12496 if(this.disabled || this.validateValue(this.getRawValue())){
12501 this.markInvalid();
12507 * Validates a value according to the field's validation rules and marks the field as invalid
12508 * if the validation fails
12509 * @param {Mixed} value The value to validate
12510 * @return {Boolean} True if the value is valid, else false
12512 validateValue : function(value)
12514 if(this.getVisibilityEl().hasClass('hidden')){
12518 if(value.length < 1) { // if it's blank
12519 if(this.allowBlank){
12525 if(value.length < this.minLength){
12528 if(value.length > this.maxLength){
12532 var vt = Roo.form.VTypes;
12533 if(!vt[this.vtype](value, this)){
12537 if(typeof this.validator == "function"){
12538 var msg = this.validator(value);
12542 if (typeof(msg) == 'string') {
12543 this.invalidText = msg;
12547 if(this.regex && !this.regex.test(value)){
12555 fireKey : function(e){
12556 //Roo.log('field ' + e.getKey());
12557 if(e.isNavKeyPress()){
12558 this.fireEvent("specialkey", this, e);
12561 focus : function (selectText){
12563 this.inputEl().focus();
12564 if(selectText === true){
12565 this.inputEl().dom.select();
12571 onFocus : function(){
12572 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12573 // this.el.addClass(this.focusClass);
12575 if(!this.hasFocus){
12576 this.hasFocus = true;
12577 this.startValue = this.getValue();
12578 this.fireEvent("focus", this);
12582 beforeBlur : Roo.emptyFn,
12586 onBlur : function(){
12588 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12589 //this.el.removeClass(this.focusClass);
12591 this.hasFocus = false;
12592 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12595 var v = this.getValue();
12596 if(String(v) !== String(this.startValue)){
12597 this.fireEvent('change', this, v, this.startValue);
12599 this.fireEvent("blur", this);
12602 onChange : function(e)
12604 var v = this.getValue();
12605 if(String(v) !== String(this.startValue)){
12606 this.fireEvent('change', this, v, this.startValue);
12612 * Resets the current field value to the originally loaded value and clears any validation messages
12614 reset : function(){
12615 this.setValue(this.originalValue);
12619 * Returns the name of the field
12620 * @return {Mixed} name The name field
12622 getName: function(){
12626 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12627 * @return {Mixed} value The field value
12629 getValue : function(){
12631 var v = this.inputEl().getValue();
12636 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12637 * @return {Mixed} value The field value
12639 getRawValue : function(){
12640 var v = this.inputEl().getValue();
12646 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12647 * @param {Mixed} value The value to set
12649 setRawValue : function(v){
12650 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12653 selectText : function(start, end){
12654 var v = this.getRawValue();
12656 start = start === undefined ? 0 : start;
12657 end = end === undefined ? v.length : end;
12658 var d = this.inputEl().dom;
12659 if(d.setSelectionRange){
12660 d.setSelectionRange(start, end);
12661 }else if(d.createTextRange){
12662 var range = d.createTextRange();
12663 range.moveStart("character", start);
12664 range.moveEnd("character", v.length-end);
12671 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12672 * @param {Mixed} value The value to set
12674 setValue : function(v){
12677 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12683 processValue : function(value){
12684 if(this.stripCharsRe){
12685 var newValue = value.replace(this.stripCharsRe, '');
12686 if(newValue !== value){
12687 this.setRawValue(newValue);
12694 preFocus : function(){
12696 if(this.selectOnFocus){
12697 this.inputEl().dom.select();
12700 filterKeys : function(e){
12701 var k = e.getKey();
12702 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12705 var c = e.getCharCode(), cc = String.fromCharCode(c);
12706 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12709 if(!this.maskRe.test(cc)){
12714 * Clear any invalid styles/messages for this field
12716 clearInvalid : function(){
12718 if(!this.el || this.preventMark){ // not rendered
12723 this.el.removeClass([this.invalidClass, 'is-invalid']);
12725 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12727 var feedback = this.el.select('.form-control-feedback', true).first();
12730 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12735 if(this.indicator){
12736 this.indicator.removeClass('visible');
12737 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12740 this.fireEvent('valid', this);
12744 * Mark this field as valid
12746 markValid : function()
12748 if(!this.el || this.preventMark){ // not rendered...
12752 this.el.removeClass([this.invalidClass, this.validClass]);
12753 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755 var feedback = this.el.select('.form-control-feedback', true).first();
12758 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12761 if(this.indicator){
12762 this.indicator.removeClass('visible');
12763 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12771 if(this.allowBlank && !this.getRawValue().length){
12774 if (Roo.bootstrap.version == 3) {
12775 this.el.addClass(this.validClass);
12777 this.inputEl().addClass('is-valid');
12780 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12782 var feedback = this.el.select('.form-control-feedback', true).first();
12785 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12786 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12791 this.fireEvent('valid', this);
12795 * Mark this field as invalid
12796 * @param {String} msg The validation message
12798 markInvalid : function(msg)
12800 if(!this.el || this.preventMark){ // not rendered
12804 this.el.removeClass([this.invalidClass, this.validClass]);
12805 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12807 var feedback = this.el.select('.form-control-feedback', true).first();
12810 this.el.select('.form-control-feedback', true).first().removeClass(
12811 [this.invalidFeedbackClass, this.validFeedbackClass]);
12818 if(this.allowBlank && !this.getRawValue().length){
12822 if(this.indicator){
12823 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12824 this.indicator.addClass('visible');
12826 if (Roo.bootstrap.version == 3) {
12827 this.el.addClass(this.invalidClass);
12829 this.inputEl().addClass('is-invalid');
12834 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12836 var feedback = this.el.select('.form-control-feedback', true).first();
12839 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12841 if(this.getValue().length || this.forceFeedback){
12842 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12849 this.fireEvent('invalid', this, msg);
12852 SafariOnKeyDown : function(event)
12854 // this is a workaround for a password hang bug on chrome/ webkit.
12855 if (this.inputEl().dom.type != 'password') {
12859 var isSelectAll = false;
12861 if(this.inputEl().dom.selectionEnd > 0){
12862 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12864 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12865 event.preventDefault();
12870 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12872 event.preventDefault();
12873 // this is very hacky as keydown always get's upper case.
12875 var cc = String.fromCharCode(event.getCharCode());
12876 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12880 adjustWidth : function(tag, w){
12881 tag = tag.toLowerCase();
12882 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12883 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12884 if(tag == 'input'){
12887 if(tag == 'textarea'){
12890 }else if(Roo.isOpera){
12891 if(tag == 'input'){
12894 if(tag == 'textarea'){
12902 setFieldLabel : function(v)
12904 if(!this.rendered){
12908 if(this.indicatorEl()){
12909 var ar = this.el.select('label > span',true);
12911 if (ar.elements.length) {
12912 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12913 this.fieldLabel = v;
12917 var br = this.el.select('label',true);
12919 if(br.elements.length) {
12920 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12921 this.fieldLabel = v;
12925 Roo.log('Cannot Found any of label > span || label in input');
12929 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12930 this.fieldLabel = v;
12945 * @class Roo.bootstrap.TextArea
12946 * @extends Roo.bootstrap.Input
12947 * Bootstrap TextArea class
12948 * @cfg {Number} cols Specifies the visible width of a text area
12949 * @cfg {Number} rows Specifies the visible number of lines in a text area
12950 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12951 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12952 * @cfg {string} html text
12955 * Create a new TextArea
12956 * @param {Object} config The config object
12959 Roo.bootstrap.TextArea = function(config){
12960 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12964 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12974 getAutoCreate : function(){
12976 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12982 if(this.inputType != 'hidden'){
12983 cfg.cls = 'form-group' //input-group
12991 value : this.value || '',
12992 html: this.html || '',
12993 cls : 'form-control',
12994 placeholder : this.placeholder || ''
12998 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12999 input.maxLength = this.maxLength;
13003 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13007 input.cols = this.cols;
13010 if (this.readOnly) {
13011 input.readonly = true;
13015 input.name = this.name;
13019 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13023 ['xs','sm','md','lg'].map(function(size){
13024 if (settings[size]) {
13025 cfg.cls += ' col-' + size + '-' + settings[size];
13029 var inputblock = input;
13031 if(this.hasFeedback && !this.allowBlank){
13035 cls: 'glyphicon form-control-feedback'
13039 cls : 'has-feedback',
13048 if (this.before || this.after) {
13051 cls : 'input-group',
13055 inputblock.cn.push({
13057 cls : 'input-group-addon',
13062 inputblock.cn.push(input);
13064 if(this.hasFeedback && !this.allowBlank){
13065 inputblock.cls += ' has-feedback';
13066 inputblock.cn.push(feedback);
13070 inputblock.cn.push({
13072 cls : 'input-group-addon',
13079 if (align ==='left' && this.fieldLabel.length) {
13084 cls : 'control-label',
13085 html : this.fieldLabel
13096 if(this.labelWidth > 12){
13097 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13100 if(this.labelWidth < 13 && this.labelmd == 0){
13101 this.labelmd = this.labelWidth;
13104 if(this.labellg > 0){
13105 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13106 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13109 if(this.labelmd > 0){
13110 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13111 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13114 if(this.labelsm > 0){
13115 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13116 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13119 if(this.labelxs > 0){
13120 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13121 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13124 } else if ( this.fieldLabel.length) {
13129 //cls : 'input-group-addon',
13130 html : this.fieldLabel
13148 if (this.disabled) {
13149 input.disabled=true;
13156 * return the real textarea element.
13158 inputEl: function ()
13160 return this.el.select('textarea.form-control',true).first();
13164 * Clear any invalid styles/messages for this field
13166 clearInvalid : function()
13169 if(!this.el || this.preventMark){ // not rendered
13173 var label = this.el.select('label', true).first();
13174 var icon = this.el.select('i.fa-star', true).first();
13179 this.el.removeClass( this.validClass);
13180 this.inputEl().removeClass('is-invalid');
13182 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13184 var feedback = this.el.select('.form-control-feedback', true).first();
13187 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13192 this.fireEvent('valid', this);
13196 * Mark this field as valid
13198 markValid : function()
13200 if(!this.el || this.preventMark){ // not rendered
13204 this.el.removeClass([this.invalidClass, this.validClass]);
13205 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13207 var feedback = this.el.select('.form-control-feedback', true).first();
13210 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13213 if(this.disabled || this.allowBlank){
13217 var label = this.el.select('label', true).first();
13218 var icon = this.el.select('i.fa-star', true).first();
13223 if (Roo.bootstrap.version == 3) {
13224 this.el.addClass(this.validClass);
13226 this.inputEl().addClass('is-valid');
13230 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13232 var feedback = this.el.select('.form-control-feedback', true).first();
13235 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13236 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13241 this.fireEvent('valid', this);
13245 * Mark this field as invalid
13246 * @param {String} msg The validation message
13248 markInvalid : function(msg)
13250 if(!this.el || this.preventMark){ // not rendered
13254 this.el.removeClass([this.invalidClass, this.validClass]);
13255 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13257 var feedback = this.el.select('.form-control-feedback', true).first();
13260 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13263 if(this.disabled || this.allowBlank){
13267 var label = this.el.select('label', true).first();
13268 var icon = this.el.select('i.fa-star', true).first();
13270 if(!this.getValue().length && label && !icon){
13271 this.el.createChild({
13273 cls : 'text-danger fa fa-lg fa-star',
13274 tooltip : 'This field is required',
13275 style : 'margin-right:5px;'
13279 if (Roo.bootstrap.version == 3) {
13280 this.el.addClass(this.invalidClass);
13282 this.inputEl().addClass('is-invalid');
13285 // fixme ... this may be depricated need to test..
13286 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13288 var feedback = this.el.select('.form-control-feedback', true).first();
13291 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13293 if(this.getValue().length || this.forceFeedback){
13294 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13301 this.fireEvent('invalid', this, msg);
13309 * trigger field - base class for combo..
13314 * @class Roo.bootstrap.TriggerField
13315 * @extends Roo.bootstrap.Input
13316 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13317 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13318 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13319 * for which you can provide a custom implementation. For example:
13321 var trigger = new Roo.bootstrap.TriggerField();
13322 trigger.onTriggerClick = myTriggerFn;
13323 trigger.applyTo('my-field');
13326 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13327 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13328 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13329 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13330 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13333 * Create a new TriggerField.
13334 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13335 * to the base TextField)
13337 Roo.bootstrap.TriggerField = function(config){
13338 this.mimicing = false;
13339 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13342 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13344 * @cfg {String} triggerClass A CSS class to apply to the trigger
13347 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13352 * @cfg {Boolean} removable (true|false) special filter default false
13356 /** @cfg {Boolean} grow @hide */
13357 /** @cfg {Number} growMin @hide */
13358 /** @cfg {Number} growMax @hide */
13364 autoSize: Roo.emptyFn,
13368 deferHeight : true,
13371 actionMode : 'wrap',
13376 getAutoCreate : function(){
13378 var align = this.labelAlign || this.parentLabelAlign();
13383 cls: 'form-group' //input-group
13390 type : this.inputType,
13391 cls : 'form-control',
13392 autocomplete: 'new-password',
13393 placeholder : this.placeholder || ''
13397 input.name = this.name;
13400 input.cls += ' input-' + this.size;
13403 if (this.disabled) {
13404 input.disabled=true;
13407 var inputblock = input;
13409 if(this.hasFeedback && !this.allowBlank){
13413 cls: 'glyphicon form-control-feedback'
13416 if(this.removable && !this.editable ){
13418 cls : 'has-feedback',
13424 cls : 'roo-combo-removable-btn close'
13431 cls : 'has-feedback',
13440 if(this.removable && !this.editable ){
13442 cls : 'roo-removable',
13448 cls : 'roo-combo-removable-btn close'
13455 if (this.before || this.after) {
13458 cls : 'input-group',
13462 inputblock.cn.push({
13464 cls : 'input-group-addon input-group-prepend input-group-text',
13469 inputblock.cn.push(input);
13471 if(this.hasFeedback && !this.allowBlank){
13472 inputblock.cls += ' has-feedback';
13473 inputblock.cn.push(feedback);
13477 inputblock.cn.push({
13479 cls : 'input-group-addon input-group-append input-group-text',
13488 var ibwrap = inputblock;
13493 cls: 'roo-select2-choices',
13497 cls: 'roo-select2-search-field',
13509 cls: 'roo-select2-container input-group',
13514 cls: 'form-hidden-field'
13520 if(!this.multiple && this.showToggleBtn){
13526 if (this.caret != false) {
13529 cls: 'fa fa-' + this.caret
13536 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13538 Roo.bootstrap.version == 3 ? caret : '',
13541 cls: 'combobox-clear',
13555 combobox.cls += ' roo-select2-container-multi';
13559 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13560 tooltip : 'This field is required'
13562 if (Roo.bootstrap.version == 4) {
13565 style : 'display:none'
13570 if (align ==='left' && this.fieldLabel.length) {
13572 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13579 cls : 'control-label',
13580 html : this.fieldLabel
13592 var labelCfg = cfg.cn[1];
13593 var contentCfg = cfg.cn[2];
13595 if(this.indicatorpos == 'right'){
13600 cls : 'control-label',
13604 html : this.fieldLabel
13618 labelCfg = cfg.cn[0];
13619 contentCfg = cfg.cn[1];
13622 if(this.labelWidth > 12){
13623 labelCfg.style = "width: " + this.labelWidth + 'px';
13626 if(this.labelWidth < 13 && this.labelmd == 0){
13627 this.labelmd = this.labelWidth;
13630 if(this.labellg > 0){
13631 labelCfg.cls += ' col-lg-' + this.labellg;
13632 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13635 if(this.labelmd > 0){
13636 labelCfg.cls += ' col-md-' + this.labelmd;
13637 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13640 if(this.labelsm > 0){
13641 labelCfg.cls += ' col-sm-' + this.labelsm;
13642 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13645 if(this.labelxs > 0){
13646 labelCfg.cls += ' col-xs-' + this.labelxs;
13647 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13650 } else if ( this.fieldLabel.length) {
13651 // Roo.log(" label");
13656 //cls : 'input-group-addon',
13657 html : this.fieldLabel
13665 if(this.indicatorpos == 'right'){
13673 html : this.fieldLabel
13687 // Roo.log(" no label && no align");
13694 ['xs','sm','md','lg'].map(function(size){
13695 if (settings[size]) {
13696 cfg.cls += ' col-' + size + '-' + settings[size];
13707 onResize : function(w, h){
13708 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13709 // if(typeof w == 'number'){
13710 // var x = w - this.trigger.getWidth();
13711 // this.inputEl().setWidth(this.adjustWidth('input', x));
13712 // this.trigger.setStyle('left', x+'px');
13717 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13720 getResizeEl : function(){
13721 return this.inputEl();
13725 getPositionEl : function(){
13726 return this.inputEl();
13730 alignErrorIcon : function(){
13731 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13735 initEvents : function(){
13739 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13740 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13741 if(!this.multiple && this.showToggleBtn){
13742 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13743 if(this.hideTrigger){
13744 this.trigger.setDisplayed(false);
13746 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13750 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13753 if(this.removable && !this.editable && !this.tickable){
13754 var close = this.closeTriggerEl();
13757 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13758 close.on('click', this.removeBtnClick, this, close);
13762 //this.trigger.addClassOnOver('x-form-trigger-over');
13763 //this.trigger.addClassOnClick('x-form-trigger-click');
13766 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13770 closeTriggerEl : function()
13772 var close = this.el.select('.roo-combo-removable-btn', true).first();
13773 return close ? close : false;
13776 removeBtnClick : function(e, h, el)
13778 e.preventDefault();
13780 if(this.fireEvent("remove", this) !== false){
13782 this.fireEvent("afterremove", this)
13786 createList : function()
13788 this.list = Roo.get(document.body).createChild({
13789 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13790 cls: 'typeahead typeahead-long dropdown-menu shadow',
13791 style: 'display:none'
13794 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13799 initTrigger : function(){
13804 onDestroy : function(){
13806 this.trigger.removeAllListeners();
13807 // this.trigger.remove();
13810 // this.wrap.remove();
13812 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13816 onFocus : function(){
13817 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13819 if(!this.mimicing){
13820 this.wrap.addClass('x-trigger-wrap-focus');
13821 this.mimicing = true;
13822 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13823 if(this.monitorTab){
13824 this.el.on("keydown", this.checkTab, this);
13831 checkTab : function(e){
13832 if(e.getKey() == e.TAB){
13833 this.triggerBlur();
13838 onBlur : function(){
13843 mimicBlur : function(e, t){
13845 if(!this.wrap.contains(t) && this.validateBlur()){
13846 this.triggerBlur();
13852 triggerBlur : function(){
13853 this.mimicing = false;
13854 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13855 if(this.monitorTab){
13856 this.el.un("keydown", this.checkTab, this);
13858 //this.wrap.removeClass('x-trigger-wrap-focus');
13859 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13863 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13864 validateBlur : function(e, t){
13869 onDisable : function(){
13870 this.inputEl().dom.disabled = true;
13871 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13873 // this.wrap.addClass('x-item-disabled');
13878 onEnable : function(){
13879 this.inputEl().dom.disabled = false;
13880 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13882 // this.el.removeClass('x-item-disabled');
13887 onShow : function(){
13888 var ae = this.getActionEl();
13891 ae.dom.style.display = '';
13892 ae.dom.style.visibility = 'visible';
13898 onHide : function(){
13899 var ae = this.getActionEl();
13900 ae.dom.style.display = 'none';
13904 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13905 * by an implementing function.
13907 * @param {EventObject} e
13909 onTriggerClick : Roo.emptyFn
13917 * @class Roo.bootstrap.CardUploader
13918 * @extends Roo.bootstrap.Button
13919 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13920 * @cfg {Number} errorTimeout default 3000
13921 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13922 * @cfg {Array} html The button text.
13926 * Create a new CardUploader
13927 * @param {Object} config The config object
13930 Roo.bootstrap.CardUploader = function(config){
13934 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13937 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13945 * When a image is clicked on - and needs to display a slideshow or similar..
13946 * @param {Roo.bootstrap.Card} this
13947 * @param {Object} The image information data
13953 * When a the download link is clicked
13954 * @param {Roo.bootstrap.Card} this
13955 * @param {Object} The image information data contains
13962 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13965 errorTimeout : 3000,
13969 fileCollection : false,
13972 getAutoCreate : function()
13976 cls :'form-group' ,
13981 //cls : 'input-group-addon',
13982 html : this.fieldLabel
13990 value : this.value,
13991 cls : 'd-none form-control'
13996 multiple : 'multiple',
13998 cls : 'd-none roo-card-upload-selector'
14002 cls : 'roo-card-uploader-button-container w-100 mb-2'
14005 cls : 'card-columns roo-card-uploader-container'
14015 getChildContainer : function() /// what children are added to.
14017 return this.containerEl;
14020 getButtonContainer : function() /// what children are added to.
14022 return this.el.select(".roo-card-uploader-button-container").first();
14025 initEvents : function()
14028 Roo.bootstrap.Input.prototype.initEvents.call(this);
14032 xns: Roo.bootstrap,
14035 container_method : 'getButtonContainer' ,
14036 html : this.html, // fix changable?
14039 'click' : function(btn, e) {
14048 this.urlAPI = (window.createObjectURL && window) ||
14049 (window.URL && URL.revokeObjectURL && URL) ||
14050 (window.webkitURL && webkitURL);
14055 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14057 this.selectorEl.on('change', this.onFileSelected, this);
14060 this.images.forEach(function(img) {
14063 this.images = false;
14065 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14071 onClick : function(e)
14073 e.preventDefault();
14075 this.selectorEl.dom.click();
14079 onFileSelected : function(e)
14081 e.preventDefault();
14083 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14087 Roo.each(this.selectorEl.dom.files, function(file){
14088 this.addFile(file);
14097 addFile : function(file)
14100 if(typeof(file) === 'string'){
14101 throw "Add file by name?"; // should not happen
14105 if(!file || !this.urlAPI){
14115 var url = _this.urlAPI.createObjectURL( file);
14118 id : Roo.bootstrap.CardUploader.ID--,
14119 is_uploaded : false,
14123 mimetype : file.type,
14131 * addCard - add an Attachment to the uploader
14132 * @param data - the data about the image to upload
14136 title : "Title of file",
14137 is_uploaded : false,
14138 src : "http://.....",
14139 srcfile : { the File upload object },
14140 mimetype : file.type,
14143 .. any other data...
14149 addCard : function (data)
14151 // hidden input element?
14152 // if the file is not an image...
14153 //then we need to use something other that and header_image
14158 xns : Roo.bootstrap,
14159 xtype : 'CardFooter',
14162 xns : Roo.bootstrap,
14168 xns : Roo.bootstrap,
14170 html : String.format("<small>{0}</small>", data.title),
14171 cls : 'col-10 text-left',
14176 click : function() {
14178 t.fireEvent( "download", t, data );
14184 xns : Roo.bootstrap,
14186 style: 'max-height: 28px; ',
14192 click : function() {
14193 t.removeCard(data.id)
14205 var cn = this.addxtype(
14208 xns : Roo.bootstrap,
14211 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14212 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14213 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14218 initEvents : function() {
14219 Roo.bootstrap.Card.prototype.initEvents.call(this);
14221 this.imgEl = this.el.select('.card-img-top').first();
14223 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14224 this.imgEl.set({ 'pointer' : 'cursor' });
14227 this.getCardFooter().addClass('p-1');
14234 // dont' really need ot update items.
14235 // this.items.push(cn);
14236 this.fileCollection.add(cn);
14238 if (!data.srcfile) {
14239 this.updateInput();
14244 var reader = new FileReader();
14245 reader.addEventListener("load", function() {
14246 data.srcdata = reader.result;
14249 reader.readAsDataURL(data.srcfile);
14254 removeCard : function(id)
14257 var card = this.fileCollection.get(id);
14258 card.data.is_deleted = 1;
14259 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14260 //this.fileCollection.remove(card);
14261 //this.items = this.items.filter(function(e) { return e != card });
14262 // dont' really need ot update items.
14263 card.el.dom.parentNode.removeChild(card.el.dom);
14264 this.updateInput();
14270 this.fileCollection.each(function(card) {
14271 if (card.el.dom && card.el.dom.parentNode) {
14272 card.el.dom.parentNode.removeChild(card.el.dom);
14275 this.fileCollection.clear();
14276 this.updateInput();
14279 updateInput : function()
14282 this.fileCollection.each(function(e) {
14286 this.inputEl().dom.value = JSON.stringify(data);
14296 Roo.bootstrap.CardUploader.ID = -1;/*
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14309 * @class Roo.data.SortTypes
14311 * Defines the default sorting (casting?) comparison functions used when sorting data.
14313 Roo.data.SortTypes = {
14315 * Default sort that does nothing
14316 * @param {Mixed} s The value being converted
14317 * @return {Mixed} The comparison value
14319 none : function(s){
14324 * The regular expression used to strip tags
14328 stripTagsRE : /<\/?[^>]+>/gi,
14331 * Strips all HTML tags to sort on text only
14332 * @param {Mixed} s The value being converted
14333 * @return {String} The comparison value
14335 asText : function(s){
14336 return String(s).replace(this.stripTagsRE, "");
14340 * Strips all HTML tags to sort on text only - Case insensitive
14341 * @param {Mixed} s The value being converted
14342 * @return {String} The comparison value
14344 asUCText : function(s){
14345 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14349 * Case insensitive string
14350 * @param {Mixed} s The value being converted
14351 * @return {String} The comparison value
14353 asUCString : function(s) {
14354 return String(s).toUpperCase();
14359 * @param {Mixed} s The value being converted
14360 * @return {Number} The comparison value
14362 asDate : function(s) {
14366 if(s instanceof Date){
14367 return s.getTime();
14369 return Date.parse(String(s));
14374 * @param {Mixed} s The value being converted
14375 * @return {Float} The comparison value
14377 asFloat : function(s) {
14378 var val = parseFloat(String(s).replace(/,/g, ""));
14387 * @param {Mixed} s The value being converted
14388 * @return {Number} The comparison value
14390 asInt : function(s) {
14391 var val = parseInt(String(s).replace(/,/g, ""));
14399 * Ext JS Library 1.1.1
14400 * Copyright(c) 2006-2007, Ext JS, LLC.
14402 * Originally Released Under LGPL - original licence link has changed is not relivant.
14405 * <script type="text/javascript">
14409 * @class Roo.data.Record
14410 * Instances of this class encapsulate both record <em>definition</em> information, and record
14411 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14412 * to access Records cached in an {@link Roo.data.Store} object.<br>
14414 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14415 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14418 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14420 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14421 * {@link #create}. The parameters are the same.
14422 * @param {Array} data An associative Array of data values keyed by the field name.
14423 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14424 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14425 * not specified an integer id is generated.
14427 Roo.data.Record = function(data, id){
14428 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14433 * Generate a constructor for a specific record layout.
14434 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14435 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14436 * Each field definition object may contain the following properties: <ul>
14437 * <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,
14438 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14439 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14440 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14441 * is being used, then this is a string containing the javascript expression to reference the data relative to
14442 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14443 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14444 * this may be omitted.</p></li>
14445 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14446 * <ul><li>auto (Default, implies no conversion)</li>
14451 * <li>date</li></ul></p></li>
14452 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14453 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14454 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14455 * by the Reader into an object that will be stored in the Record. It is passed the
14456 * following parameters:<ul>
14457 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14459 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14461 * <br>usage:<br><pre><code>
14462 var TopicRecord = Roo.data.Record.create(
14463 {name: 'title', mapping: 'topic_title'},
14464 {name: 'author', mapping: 'username'},
14465 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14466 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14467 {name: 'lastPoster', mapping: 'user2'},
14468 {name: 'excerpt', mapping: 'post_text'}
14471 var myNewRecord = new TopicRecord({
14472 title: 'Do my job please',
14475 lastPost: new Date(),
14476 lastPoster: 'Animal',
14477 excerpt: 'No way dude!'
14479 myStore.add(myNewRecord);
14484 Roo.data.Record.create = function(o){
14485 var f = function(){
14486 f.superclass.constructor.apply(this, arguments);
14488 Roo.extend(f, Roo.data.Record);
14489 var p = f.prototype;
14490 p.fields = new Roo.util.MixedCollection(false, function(field){
14493 for(var i = 0, len = o.length; i < len; i++){
14494 p.fields.add(new Roo.data.Field(o[i]));
14496 f.getField = function(name){
14497 return p.fields.get(name);
14502 Roo.data.Record.AUTO_ID = 1000;
14503 Roo.data.Record.EDIT = 'edit';
14504 Roo.data.Record.REJECT = 'reject';
14505 Roo.data.Record.COMMIT = 'commit';
14507 Roo.data.Record.prototype = {
14509 * Readonly flag - true if this record has been modified.
14518 join : function(store){
14519 this.store = store;
14523 * Set the named field to the specified value.
14524 * @param {String} name The name of the field to set.
14525 * @param {Object} value The value to set the field to.
14527 set : function(name, value){
14528 if(this.data[name] == value){
14532 if(!this.modified){
14533 this.modified = {};
14535 if(typeof this.modified[name] == 'undefined'){
14536 this.modified[name] = this.data[name];
14538 this.data[name] = value;
14539 if(!this.editing && this.store){
14540 this.store.afterEdit(this);
14545 * Get the value of the named field.
14546 * @param {String} name The name of the field to get the value of.
14547 * @return {Object} The value of the field.
14549 get : function(name){
14550 return this.data[name];
14554 beginEdit : function(){
14555 this.editing = true;
14556 this.modified = {};
14560 cancelEdit : function(){
14561 this.editing = false;
14562 delete this.modified;
14566 endEdit : function(){
14567 this.editing = false;
14568 if(this.dirty && this.store){
14569 this.store.afterEdit(this);
14574 * Usually called by the {@link Roo.data.Store} which owns the Record.
14575 * Rejects all changes made to the Record since either creation, or the last commit operation.
14576 * Modified fields are reverted to their original values.
14578 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14579 * of reject operations.
14581 reject : function(){
14582 var m = this.modified;
14584 if(typeof m[n] != "function"){
14585 this.data[n] = m[n];
14588 this.dirty = false;
14589 delete this.modified;
14590 this.editing = false;
14592 this.store.afterReject(this);
14597 * Usually called by the {@link Roo.data.Store} which owns the Record.
14598 * Commits all changes made to the Record since either creation, or the last commit operation.
14600 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14601 * of commit operations.
14603 commit : function(){
14604 this.dirty = false;
14605 delete this.modified;
14606 this.editing = false;
14608 this.store.afterCommit(this);
14613 hasError : function(){
14614 return this.error != null;
14618 clearError : function(){
14623 * Creates a copy of this record.
14624 * @param {String} id (optional) A new record id if you don't want to use this record's id
14627 copy : function(newId) {
14628 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14632 * Ext JS Library 1.1.1
14633 * Copyright(c) 2006-2007, Ext JS, LLC.
14635 * Originally Released Under LGPL - original licence link has changed is not relivant.
14638 * <script type="text/javascript">
14644 * @class Roo.data.Store
14645 * @extends Roo.util.Observable
14646 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14647 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14649 * 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
14650 * has no knowledge of the format of the data returned by the Proxy.<br>
14652 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14653 * instances from the data object. These records are cached and made available through accessor functions.
14655 * Creates a new Store.
14656 * @param {Object} config A config object containing the objects needed for the Store to access data,
14657 * and read the data into Records.
14659 Roo.data.Store = function(config){
14660 this.data = new Roo.util.MixedCollection(false);
14661 this.data.getKey = function(o){
14664 this.baseParams = {};
14666 this.paramNames = {
14671 "multisort" : "_multisort"
14674 if(config && config.data){
14675 this.inlineData = config.data;
14676 delete config.data;
14679 Roo.apply(this, config);
14681 if(this.reader){ // reader passed
14682 this.reader = Roo.factory(this.reader, Roo.data);
14683 this.reader.xmodule = this.xmodule || false;
14684 if(!this.recordType){
14685 this.recordType = this.reader.recordType;
14687 if(this.reader.onMetaChange){
14688 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14692 if(this.recordType){
14693 this.fields = this.recordType.prototype.fields;
14695 this.modified = [];
14699 * @event datachanged
14700 * Fires when the data cache has changed, and a widget which is using this Store
14701 * as a Record cache should refresh its view.
14702 * @param {Store} this
14704 datachanged : true,
14706 * @event metachange
14707 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14708 * @param {Store} this
14709 * @param {Object} meta The JSON metadata
14714 * Fires when Records have been added to the Store
14715 * @param {Store} this
14716 * @param {Roo.data.Record[]} records The array of Records added
14717 * @param {Number} index The index at which the record(s) were added
14722 * Fires when a Record has been removed from the Store
14723 * @param {Store} this
14724 * @param {Roo.data.Record} record The Record that was removed
14725 * @param {Number} index The index at which the record was removed
14730 * Fires when a Record has been updated
14731 * @param {Store} this
14732 * @param {Roo.data.Record} record The Record that was updated
14733 * @param {String} operation The update operation being performed. Value may be one of:
14735 Roo.data.Record.EDIT
14736 Roo.data.Record.REJECT
14737 Roo.data.Record.COMMIT
14743 * Fires when the data cache has been cleared.
14744 * @param {Store} this
14748 * @event beforeload
14749 * Fires before a request is made for a new data object. If the beforeload handler returns false
14750 * the load action will be canceled.
14751 * @param {Store} this
14752 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14756 * @event beforeloadadd
14757 * Fires after a new set of Records has been loaded.
14758 * @param {Store} this
14759 * @param {Roo.data.Record[]} records The Records that were loaded
14760 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14762 beforeloadadd : true,
14765 * Fires after a new set of Records has been loaded, before they are added to the store.
14766 * @param {Store} this
14767 * @param {Roo.data.Record[]} records The Records that were loaded
14768 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14769 * @params {Object} return from reader
14773 * @event loadexception
14774 * Fires if an exception occurs in the Proxy during loading.
14775 * Called with the signature of the Proxy's "loadexception" event.
14776 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14779 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14780 * @param {Object} load options
14781 * @param {Object} jsonData from your request (normally this contains the Exception)
14783 loadexception : true
14787 this.proxy = Roo.factory(this.proxy, Roo.data);
14788 this.proxy.xmodule = this.xmodule || false;
14789 this.relayEvents(this.proxy, ["loadexception"]);
14791 this.sortToggle = {};
14792 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14794 Roo.data.Store.superclass.constructor.call(this);
14796 if(this.inlineData){
14797 this.loadData(this.inlineData);
14798 delete this.inlineData;
14802 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14804 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14805 * without a remote query - used by combo/forms at present.
14809 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14812 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14815 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14816 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14819 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14820 * on any HTTP request
14823 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14826 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14830 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14831 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14833 remoteSort : false,
14836 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14837 * loaded or when a record is removed. (defaults to false).
14839 pruneModifiedRecords : false,
14842 lastOptions : null,
14845 * Add Records to the Store and fires the add event.
14846 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14848 add : function(records){
14849 records = [].concat(records);
14850 for(var i = 0, len = records.length; i < len; i++){
14851 records[i].join(this);
14853 var index = this.data.length;
14854 this.data.addAll(records);
14855 this.fireEvent("add", this, records, index);
14859 * Remove a Record from the Store and fires the remove event.
14860 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14862 remove : function(record){
14863 var index = this.data.indexOf(record);
14864 this.data.removeAt(index);
14866 if(this.pruneModifiedRecords){
14867 this.modified.remove(record);
14869 this.fireEvent("remove", this, record, index);
14873 * Remove all Records from the Store and fires the clear event.
14875 removeAll : function(){
14877 if(this.pruneModifiedRecords){
14878 this.modified = [];
14880 this.fireEvent("clear", this);
14884 * Inserts Records to the Store at the given index and fires the add event.
14885 * @param {Number} index The start index at which to insert the passed Records.
14886 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14888 insert : function(index, records){
14889 records = [].concat(records);
14890 for(var i = 0, len = records.length; i < len; i++){
14891 this.data.insert(index, records[i]);
14892 records[i].join(this);
14894 this.fireEvent("add", this, records, index);
14898 * Get the index within the cache of the passed Record.
14899 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14900 * @return {Number} The index of the passed Record. Returns -1 if not found.
14902 indexOf : function(record){
14903 return this.data.indexOf(record);
14907 * Get the index within the cache of the Record with the passed id.
14908 * @param {String} id The id of the Record to find.
14909 * @return {Number} The index of the Record. Returns -1 if not found.
14911 indexOfId : function(id){
14912 return this.data.indexOfKey(id);
14916 * Get the Record with the specified id.
14917 * @param {String} id The id of the Record to find.
14918 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14920 getById : function(id){
14921 return this.data.key(id);
14925 * Get the Record at the specified index.
14926 * @param {Number} index The index of the Record to find.
14927 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14929 getAt : function(index){
14930 return this.data.itemAt(index);
14934 * Returns a range of Records between specified indices.
14935 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14936 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14937 * @return {Roo.data.Record[]} An array of Records
14939 getRange : function(start, end){
14940 return this.data.getRange(start, end);
14944 storeOptions : function(o){
14945 o = Roo.apply({}, o);
14948 this.lastOptions = o;
14952 * Loads the Record cache from the configured Proxy using the configured Reader.
14954 * If using remote paging, then the first load call must specify the <em>start</em>
14955 * and <em>limit</em> properties in the options.params property to establish the initial
14956 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14958 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14959 * and this call will return before the new data has been loaded. Perform any post-processing
14960 * in a callback function, or in a "load" event handler.</strong>
14962 * @param {Object} options An object containing properties which control loading options:<ul>
14963 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14964 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14965 * passed the following arguments:<ul>
14966 * <li>r : Roo.data.Record[]</li>
14967 * <li>options: Options object from the load call</li>
14968 * <li>success: Boolean success indicator</li></ul></li>
14969 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14970 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14973 load : function(options){
14974 options = options || {};
14975 if(this.fireEvent("beforeload", this, options) !== false){
14976 this.storeOptions(options);
14977 var p = Roo.apply(options.params || {}, this.baseParams);
14978 // if meta was not loaded from remote source.. try requesting it.
14979 if (!this.reader.metaFromRemote) {
14980 p._requestMeta = 1;
14982 if(this.sortInfo && this.remoteSort){
14983 var pn = this.paramNames;
14984 p[pn["sort"]] = this.sortInfo.field;
14985 p[pn["dir"]] = this.sortInfo.direction;
14987 if (this.multiSort) {
14988 var pn = this.paramNames;
14989 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14992 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14997 * Reloads the Record cache from the configured Proxy using the configured Reader and
14998 * the options from the last load operation performed.
14999 * @param {Object} options (optional) An object containing properties which may override the options
15000 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15001 * the most recently used options are reused).
15003 reload : function(options){
15004 this.load(Roo.applyIf(options||{}, this.lastOptions));
15008 // Called as a callback by the Reader during a load operation.
15009 loadRecords : function(o, options, success){
15010 if(!o || success === false){
15011 if(success !== false){
15012 this.fireEvent("load", this, [], options, o);
15014 if(options.callback){
15015 options.callback.call(options.scope || this, [], options, false);
15019 // if data returned failure - throw an exception.
15020 if (o.success === false) {
15021 // show a message if no listener is registered.
15022 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15023 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15025 // loadmask wil be hooked into this..
15026 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15029 var r = o.records, t = o.totalRecords || r.length;
15031 this.fireEvent("beforeloadadd", this, r, options, o);
15033 if(!options || options.add !== true){
15034 if(this.pruneModifiedRecords){
15035 this.modified = [];
15037 for(var i = 0, len = r.length; i < len; i++){
15041 this.data = this.snapshot;
15042 delete this.snapshot;
15045 this.data.addAll(r);
15046 this.totalLength = t;
15048 this.fireEvent("datachanged", this);
15050 this.totalLength = Math.max(t, this.data.length+r.length);
15054 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15056 var e = new Roo.data.Record({});
15058 e.set(this.parent.displayField, this.parent.emptyTitle);
15059 e.set(this.parent.valueField, '');
15064 this.fireEvent("load", this, r, options, o);
15065 if(options.callback){
15066 options.callback.call(options.scope || this, r, options, true);
15072 * Loads data from a passed data block. A Reader which understands the format of the data
15073 * must have been configured in the constructor.
15074 * @param {Object} data The data block from which to read the Records. The format of the data expected
15075 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15076 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15078 loadData : function(o, append){
15079 var r = this.reader.readRecords(o);
15080 this.loadRecords(r, {add: append}, true);
15084 * using 'cn' the nested child reader read the child array into it's child stores.
15085 * @param {Object} rec The record with a 'children array
15087 loadDataFromChildren : function(rec)
15089 this.loadData(this.reader.toLoadData(rec));
15094 * Gets the number of cached records.
15096 * <em>If using paging, this may not be the total size of the dataset. If the data object
15097 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15098 * the data set size</em>
15100 getCount : function(){
15101 return this.data.length || 0;
15105 * Gets the total number of records in the dataset as returned by the server.
15107 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15108 * the dataset size</em>
15110 getTotalCount : function(){
15111 return this.totalLength || 0;
15115 * Returns the sort state of the Store as an object with two properties:
15117 field {String} The name of the field by which the Records are sorted
15118 direction {String} The sort order, "ASC" or "DESC"
15121 getSortState : function(){
15122 return this.sortInfo;
15126 applySort : function(){
15127 if(this.sortInfo && !this.remoteSort){
15128 var s = this.sortInfo, f = s.field;
15129 var st = this.fields.get(f).sortType;
15130 var fn = function(r1, r2){
15131 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15132 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15134 this.data.sort(s.direction, fn);
15135 if(this.snapshot && this.snapshot != this.data){
15136 this.snapshot.sort(s.direction, fn);
15142 * Sets the default sort column and order to be used by the next load operation.
15143 * @param {String} fieldName The name of the field to sort by.
15144 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15146 setDefaultSort : function(field, dir){
15147 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15151 * Sort the Records.
15152 * If remote sorting is used, the sort is performed on the server, and the cache is
15153 * reloaded. If local sorting is used, the cache is sorted internally.
15154 * @param {String} fieldName The name of the field to sort by.
15155 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15157 sort : function(fieldName, dir){
15158 var f = this.fields.get(fieldName);
15160 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15162 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15163 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15168 this.sortToggle[f.name] = dir;
15169 this.sortInfo = {field: f.name, direction: dir};
15170 if(!this.remoteSort){
15172 this.fireEvent("datachanged", this);
15174 this.load(this.lastOptions);
15179 * Calls the specified function for each of the Records in the cache.
15180 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15181 * Returning <em>false</em> aborts and exits the iteration.
15182 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15184 each : function(fn, scope){
15185 this.data.each(fn, scope);
15189 * Gets all records modified since the last commit. Modified records are persisted across load operations
15190 * (e.g., during paging).
15191 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15193 getModifiedRecords : function(){
15194 return this.modified;
15198 createFilterFn : function(property, value, anyMatch){
15199 if(!value.exec){ // not a regex
15200 value = String(value);
15201 if(value.length == 0){
15204 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15206 return function(r){
15207 return value.test(r.data[property]);
15212 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15213 * @param {String} property A field on your records
15214 * @param {Number} start The record index to start at (defaults to 0)
15215 * @param {Number} end The last record index to include (defaults to length - 1)
15216 * @return {Number} The sum
15218 sum : function(property, start, end){
15219 var rs = this.data.items, v = 0;
15220 start = start || 0;
15221 end = (end || end === 0) ? end : rs.length-1;
15223 for(var i = start; i <= end; i++){
15224 v += (rs[i].data[property] || 0);
15230 * Filter the records by a specified property.
15231 * @param {String} field A field on your records
15232 * @param {String/RegExp} value Either a string that the field
15233 * should start with or a RegExp to test against the field
15234 * @param {Boolean} anyMatch True to match any part not just the beginning
15236 filter : function(property, value, anyMatch){
15237 var fn = this.createFilterFn(property, value, anyMatch);
15238 return fn ? this.filterBy(fn) : this.clearFilter();
15242 * Filter by a function. The specified function will be called with each
15243 * record in this data source. If the function returns true the record is included,
15244 * otherwise it is filtered.
15245 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15246 * @param {Object} scope (optional) The scope of the function (defaults to this)
15248 filterBy : function(fn, scope){
15249 this.snapshot = this.snapshot || this.data;
15250 this.data = this.queryBy(fn, scope||this);
15251 this.fireEvent("datachanged", this);
15255 * Query the records by a specified property.
15256 * @param {String} field A field on your records
15257 * @param {String/RegExp} value Either a string that the field
15258 * should start with or a RegExp to test against the field
15259 * @param {Boolean} anyMatch True to match any part not just the beginning
15260 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15262 query : function(property, value, anyMatch){
15263 var fn = this.createFilterFn(property, value, anyMatch);
15264 return fn ? this.queryBy(fn) : this.data.clone();
15268 * Query by a function. The specified function will be called with each
15269 * record in this data source. If the function returns true the record is included
15271 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15272 * @param {Object} scope (optional) The scope of the function (defaults to this)
15273 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15275 queryBy : function(fn, scope){
15276 var data = this.snapshot || this.data;
15277 return data.filterBy(fn, scope||this);
15281 * Collects unique values for a particular dataIndex from this store.
15282 * @param {String} dataIndex The property to collect
15283 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15284 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15285 * @return {Array} An array of the unique values
15287 collect : function(dataIndex, allowNull, bypassFilter){
15288 var d = (bypassFilter === true && this.snapshot) ?
15289 this.snapshot.items : this.data.items;
15290 var v, sv, r = [], l = {};
15291 for(var i = 0, len = d.length; i < len; i++){
15292 v = d[i].data[dataIndex];
15294 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15303 * Revert to a view of the Record cache with no filtering applied.
15304 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15306 clearFilter : function(suppressEvent){
15307 if(this.snapshot && this.snapshot != this.data){
15308 this.data = this.snapshot;
15309 delete this.snapshot;
15310 if(suppressEvent !== true){
15311 this.fireEvent("datachanged", this);
15317 afterEdit : function(record){
15318 if(this.modified.indexOf(record) == -1){
15319 this.modified.push(record);
15321 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15325 afterReject : function(record){
15326 this.modified.remove(record);
15327 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15331 afterCommit : function(record){
15332 this.modified.remove(record);
15333 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15337 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15338 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15340 commitChanges : function(){
15341 var m = this.modified.slice(0);
15342 this.modified = [];
15343 for(var i = 0, len = m.length; i < len; i++){
15349 * Cancel outstanding changes on all changed records.
15351 rejectChanges : function(){
15352 var m = this.modified.slice(0);
15353 this.modified = [];
15354 for(var i = 0, len = m.length; i < len; i++){
15359 onMetaChange : function(meta, rtype, o){
15360 this.recordType = rtype;
15361 this.fields = rtype.prototype.fields;
15362 delete this.snapshot;
15363 this.sortInfo = meta.sortInfo || this.sortInfo;
15364 this.modified = [];
15365 this.fireEvent('metachange', this, this.reader.meta);
15368 moveIndex : function(data, type)
15370 var index = this.indexOf(data);
15372 var newIndex = index + type;
15376 this.insert(newIndex, data);
15381 * Ext JS Library 1.1.1
15382 * Copyright(c) 2006-2007, Ext JS, LLC.
15384 * Originally Released Under LGPL - original licence link has changed is not relivant.
15387 * <script type="text/javascript">
15391 * @class Roo.data.SimpleStore
15392 * @extends Roo.data.Store
15393 * Small helper class to make creating Stores from Array data easier.
15394 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15395 * @cfg {Array} fields An array of field definition objects, or field name strings.
15396 * @cfg {Object} an existing reader (eg. copied from another store)
15397 * @cfg {Array} data The multi-dimensional array of data
15398 * @cfg {Roo.data.DataProxy} proxy [not-required]
15399 * @cfg {Roo.data.Reader} reader [not-required]
15401 * @param {Object} config
15403 Roo.data.SimpleStore = function(config)
15405 Roo.data.SimpleStore.superclass.constructor.call(this, {
15407 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15410 Roo.data.Record.create(config.fields)
15412 proxy : new Roo.data.MemoryProxy(config.data)
15416 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15418 * Ext JS Library 1.1.1
15419 * Copyright(c) 2006-2007, Ext JS, LLC.
15421 * Originally Released Under LGPL - original licence link has changed is not relivant.
15424 * <script type="text/javascript">
15429 * @extends Roo.data.Store
15430 * @class Roo.data.JsonStore
15431 * Small helper class to make creating Stores for JSON data easier. <br/>
15433 var store = new Roo.data.JsonStore({
15434 url: 'get-images.php',
15436 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15439 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15440 * JsonReader and HttpProxy (unless inline data is provided).</b>
15441 * @cfg {Array} fields An array of field definition objects, or field name strings.
15443 * @param {Object} config
15445 Roo.data.JsonStore = function(c){
15446 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15447 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15448 reader: new Roo.data.JsonReader(c, c.fields)
15451 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15453 * Ext JS Library 1.1.1
15454 * Copyright(c) 2006-2007, Ext JS, LLC.
15456 * Originally Released Under LGPL - original licence link has changed is not relivant.
15459 * <script type="text/javascript">
15463 Roo.data.Field = function(config){
15464 if(typeof config == "string"){
15465 config = {name: config};
15467 Roo.apply(this, config);
15470 this.type = "auto";
15473 var st = Roo.data.SortTypes;
15474 // named sortTypes are supported, here we look them up
15475 if(typeof this.sortType == "string"){
15476 this.sortType = st[this.sortType];
15479 // set default sortType for strings and dates
15480 if(!this.sortType){
15483 this.sortType = st.asUCString;
15486 this.sortType = st.asDate;
15489 this.sortType = st.none;
15494 var stripRe = /[\$,%]/g;
15496 // prebuilt conversion function for this field, instead of
15497 // switching every time we're reading a value
15499 var cv, dateFormat = this.dateFormat;
15504 cv = function(v){ return v; };
15507 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15511 return v !== undefined && v !== null && v !== '' ?
15512 parseInt(String(v).replace(stripRe, ""), 10) : '';
15517 return v !== undefined && v !== null && v !== '' ?
15518 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15523 cv = function(v){ return v === true || v === "true" || v == 1; };
15530 if(v instanceof Date){
15534 if(dateFormat == "timestamp"){
15535 return new Date(v*1000);
15537 return Date.parseDate(v, dateFormat);
15539 var parsed = Date.parse(v);
15540 return parsed ? new Date(parsed) : null;
15549 Roo.data.Field.prototype = {
15557 * Ext JS Library 1.1.1
15558 * Copyright(c) 2006-2007, Ext JS, LLC.
15560 * Originally Released Under LGPL - original licence link has changed is not relivant.
15563 * <script type="text/javascript">
15566 // Base class for reading structured data from a data source. This class is intended to be
15567 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15570 * @class Roo.data.DataReader
15572 * Base class for reading structured data from a data source. This class is intended to be
15573 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15576 Roo.data.DataReader = function(meta, recordType){
15580 this.recordType = recordType instanceof Array ?
15581 Roo.data.Record.create(recordType) : recordType;
15584 Roo.data.DataReader.prototype = {
15587 readerType : 'Data',
15589 * Create an empty record
15590 * @param {Object} data (optional) - overlay some values
15591 * @return {Roo.data.Record} record created.
15593 newRow : function(d) {
15595 this.recordType.prototype.fields.each(function(c) {
15597 case 'int' : da[c.name] = 0; break;
15598 case 'date' : da[c.name] = new Date(); break;
15599 case 'float' : da[c.name] = 0.0; break;
15600 case 'boolean' : da[c.name] = false; break;
15601 default : da[c.name] = ""; break;
15605 return new this.recordType(Roo.apply(da, d));
15611 * Ext JS Library 1.1.1
15612 * Copyright(c) 2006-2007, Ext JS, LLC.
15614 * Originally Released Under LGPL - original licence link has changed is not relivant.
15617 * <script type="text/javascript">
15621 * @class Roo.data.DataProxy
15622 * @extends Roo.data.Observable
15624 * This class is an abstract base class for implementations which provide retrieval of
15625 * unformatted data objects.<br>
15627 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15628 * (of the appropriate type which knows how to parse the data object) to provide a block of
15629 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15631 * Custom implementations must implement the load method as described in
15632 * {@link Roo.data.HttpProxy#load}.
15634 Roo.data.DataProxy = function(){
15637 * @event beforeload
15638 * Fires before a network request is made to retrieve a data object.
15639 * @param {Object} This DataProxy object.
15640 * @param {Object} params The params parameter to the load function.
15645 * Fires before the load method's callback is called.
15646 * @param {Object} This DataProxy object.
15647 * @param {Object} o The data object.
15648 * @param {Object} arg The callback argument object passed to the load function.
15652 * @event loadexception
15653 * Fires if an Exception occurs during data retrieval.
15654 * @param {Object} This DataProxy object.
15655 * @param {Object} o The data object.
15656 * @param {Object} arg The callback argument object passed to the load function.
15657 * @param {Object} e The Exception.
15659 loadexception : true
15661 Roo.data.DataProxy.superclass.constructor.call(this);
15664 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15667 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15671 * Ext JS Library 1.1.1
15672 * Copyright(c) 2006-2007, Ext JS, LLC.
15674 * Originally Released Under LGPL - original licence link has changed is not relivant.
15677 * <script type="text/javascript">
15680 * @class Roo.data.MemoryProxy
15681 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15682 * to the Reader when its load method is called.
15684 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15686 Roo.data.MemoryProxy = function(data){
15690 Roo.data.MemoryProxy.superclass.constructor.call(this);
15694 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15697 * Load data from the requested source (in this case an in-memory
15698 * data object passed to the constructor), read the data object into
15699 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15700 * process that block using the passed callback.
15701 * @param {Object} params This parameter is not used by the MemoryProxy class.
15702 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15703 * object into a block of Roo.data.Records.
15704 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15705 * The function must be passed <ul>
15706 * <li>The Record block object</li>
15707 * <li>The "arg" argument from the load function</li>
15708 * <li>A boolean success indicator</li>
15710 * @param {Object} scope The scope in which to call the callback
15711 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15713 load : function(params, reader, callback, scope, arg){
15714 params = params || {};
15717 result = reader.readRecords(params.data ? params.data :this.data);
15719 this.fireEvent("loadexception", this, arg, null, e);
15720 callback.call(scope, null, arg, false);
15723 callback.call(scope, result, arg, true);
15727 update : function(params, records){
15732 * Ext JS Library 1.1.1
15733 * Copyright(c) 2006-2007, Ext JS, LLC.
15735 * Originally Released Under LGPL - original licence link has changed is not relivant.
15738 * <script type="text/javascript">
15741 * @class Roo.data.HttpProxy
15742 * @extends Roo.data.DataProxy
15743 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15744 * configured to reference a certain URL.<br><br>
15746 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15747 * from which the running page was served.<br><br>
15749 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15751 * Be aware that to enable the browser to parse an XML document, the server must set
15752 * the Content-Type header in the HTTP response to "text/xml".
15754 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15755 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15756 * will be used to make the request.
15758 Roo.data.HttpProxy = function(conn){
15759 Roo.data.HttpProxy.superclass.constructor.call(this);
15760 // is conn a conn config or a real conn?
15762 this.useAjax = !conn || !conn.events;
15766 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15767 // thse are take from connection...
15770 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15773 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15774 * extra parameters to each request made by this object. (defaults to undefined)
15777 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15778 * to each request made by this object. (defaults to undefined)
15781 * @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)
15784 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15787 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15793 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15797 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15798 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15799 * a finer-grained basis than the DataProxy events.
15801 getConnection : function(){
15802 return this.useAjax ? Roo.Ajax : this.conn;
15806 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15807 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15808 * process that block using the passed callback.
15809 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15810 * for the request to the remote server.
15811 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15812 * object into a block of Roo.data.Records.
15813 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15814 * The function must be passed <ul>
15815 * <li>The Record block object</li>
15816 * <li>The "arg" argument from the load function</li>
15817 * <li>A boolean success indicator</li>
15819 * @param {Object} scope The scope in which to call the callback
15820 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15822 load : function(params, reader, callback, scope, arg){
15823 if(this.fireEvent("beforeload", this, params) !== false){
15825 params : params || {},
15827 callback : callback,
15832 callback : this.loadResponse,
15836 Roo.applyIf(o, this.conn);
15837 if(this.activeRequest){
15838 Roo.Ajax.abort(this.activeRequest);
15840 this.activeRequest = Roo.Ajax.request(o);
15842 this.conn.request(o);
15845 callback.call(scope||this, null, arg, false);
15850 loadResponse : function(o, success, response){
15851 delete this.activeRequest;
15853 this.fireEvent("loadexception", this, o, response);
15854 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15859 result = o.reader.read(response);
15861 this.fireEvent("loadexception", this, o, response, e);
15862 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15866 this.fireEvent("load", this, o, o.request.arg);
15867 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15871 update : function(dataSet){
15876 updateResponse : function(dataSet){
15881 * Ext JS Library 1.1.1
15882 * Copyright(c) 2006-2007, Ext JS, LLC.
15884 * Originally Released Under LGPL - original licence link has changed is not relivant.
15887 * <script type="text/javascript">
15891 * @class Roo.data.ScriptTagProxy
15892 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15893 * other than the originating domain of the running page.<br><br>
15895 * <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
15896 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15898 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15899 * source code that is used as the source inside a <script> tag.<br><br>
15901 * In order for the browser to process the returned data, the server must wrap the data object
15902 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15903 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15904 * depending on whether the callback name was passed:
15907 boolean scriptTag = false;
15908 String cb = request.getParameter("callback");
15911 response.setContentType("text/javascript");
15913 response.setContentType("application/x-json");
15915 Writer out = response.getWriter();
15917 out.write(cb + "(");
15919 out.print(dataBlock.toJsonString());
15926 * @param {Object} config A configuration object.
15928 Roo.data.ScriptTagProxy = function(config){
15929 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15930 Roo.apply(this, config);
15931 this.head = document.getElementsByTagName("head")[0];
15934 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15936 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15938 * @cfg {String} url The URL from which to request the data object.
15941 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15945 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15946 * the server the name of the callback function set up by the load call to process the returned data object.
15947 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15948 * javascript output which calls this named function passing the data object as its only parameter.
15950 callbackParam : "callback",
15952 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15953 * name to the request.
15958 * Load data from the configured URL, read the data object into
15959 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15960 * process that block using the passed callback.
15961 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15962 * for the request to the remote server.
15963 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15964 * object into a block of Roo.data.Records.
15965 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15966 * The function must be passed <ul>
15967 * <li>The Record block object</li>
15968 * <li>The "arg" argument from the load function</li>
15969 * <li>A boolean success indicator</li>
15971 * @param {Object} scope The scope in which to call the callback
15972 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15974 load : function(params, reader, callback, scope, arg){
15975 if(this.fireEvent("beforeload", this, params) !== false){
15977 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15979 var url = this.url;
15980 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15982 url += "&_dc=" + (new Date().getTime());
15984 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15987 cb : "stcCallback"+transId,
15988 scriptId : "stcScript"+transId,
15992 callback : callback,
15998 window[trans.cb] = function(o){
15999 conn.handleResponse(o, trans);
16002 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16004 if(this.autoAbort !== false){
16008 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16010 var script = document.createElement("script");
16011 script.setAttribute("src", url);
16012 script.setAttribute("type", "text/javascript");
16013 script.setAttribute("id", trans.scriptId);
16014 this.head.appendChild(script);
16016 this.trans = trans;
16018 callback.call(scope||this, null, arg, false);
16023 isLoading : function(){
16024 return this.trans ? true : false;
16028 * Abort the current server request.
16030 abort : function(){
16031 if(this.isLoading()){
16032 this.destroyTrans(this.trans);
16037 destroyTrans : function(trans, isLoaded){
16038 this.head.removeChild(document.getElementById(trans.scriptId));
16039 clearTimeout(trans.timeoutId);
16041 window[trans.cb] = undefined;
16043 delete window[trans.cb];
16046 // if hasn't been loaded, wait for load to remove it to prevent script error
16047 window[trans.cb] = function(){
16048 window[trans.cb] = undefined;
16050 delete window[trans.cb];
16057 handleResponse : function(o, trans){
16058 this.trans = false;
16059 this.destroyTrans(trans, true);
16062 result = trans.reader.readRecords(o);
16064 this.fireEvent("loadexception", this, o, trans.arg, e);
16065 trans.callback.call(trans.scope||window, null, trans.arg, false);
16068 this.fireEvent("load", this, o, trans.arg);
16069 trans.callback.call(trans.scope||window, result, trans.arg, true);
16073 handleFailure : function(trans){
16074 this.trans = false;
16075 this.destroyTrans(trans, false);
16076 this.fireEvent("loadexception", this, null, trans.arg);
16077 trans.callback.call(trans.scope||window, null, trans.arg, false);
16081 * Ext JS Library 1.1.1
16082 * Copyright(c) 2006-2007, Ext JS, LLC.
16084 * Originally Released Under LGPL - original licence link has changed is not relivant.
16087 * <script type="text/javascript">
16091 * @class Roo.data.JsonReader
16092 * @extends Roo.data.DataReader
16093 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16094 * based on mappings in a provided Roo.data.Record constructor.
16096 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16097 * in the reply previously.
16102 var RecordDef = Roo.data.Record.create([
16103 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16104 {name: 'occupation'} // This field will use "occupation" as the mapping.
16106 var myReader = new Roo.data.JsonReader({
16107 totalProperty: "results", // The property which contains the total dataset size (optional)
16108 root: "rows", // The property which contains an Array of row objects
16109 id: "id" // The property within each row object that provides an ID for the record (optional)
16113 * This would consume a JSON file like this:
16115 { 'results': 2, 'rows': [
16116 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16117 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16120 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16121 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16122 * paged from the remote server.
16123 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16124 * @cfg {String} root name of the property which contains the Array of row objects.
16125 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16126 * @cfg {Array} fields Array of field definition objects
16128 * Create a new JsonReader
16129 * @param {Object} meta Metadata configuration options
16130 * @param {Object} recordType Either an Array of field definition objects,
16131 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16133 Roo.data.JsonReader = function(meta, recordType){
16136 // set some defaults:
16137 Roo.applyIf(meta, {
16138 totalProperty: 'total',
16139 successProperty : 'success',
16144 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16148 readerType : 'Json',
16151 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16152 * Used by Store query builder to append _requestMeta to params.
16155 metaFromRemote : false,
16157 * This method is only used by a DataProxy which has retrieved data from a remote server.
16158 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16159 * @return {Object} data A data block which is used by an Roo.data.Store object as
16160 * a cache of Roo.data.Records.
16162 read : function(response){
16163 var json = response.responseText;
16165 var o = /* eval:var:o */ eval("("+json+")");
16167 throw {message: "JsonReader.read: Json object not found"};
16173 this.metaFromRemote = true;
16174 this.meta = o.metaData;
16175 this.recordType = Roo.data.Record.create(o.metaData.fields);
16176 this.onMetaChange(this.meta, this.recordType, o);
16178 return this.readRecords(o);
16181 // private function a store will implement
16182 onMetaChange : function(meta, recordType, o){
16189 simpleAccess: function(obj, subsc) {
16196 getJsonAccessor: function(){
16198 return function(expr) {
16200 return(re.test(expr))
16201 ? new Function("obj", "return obj." + expr)
16206 return Roo.emptyFn;
16211 * Create a data block containing Roo.data.Records from an XML document.
16212 * @param {Object} o An object which contains an Array of row objects in the property specified
16213 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16214 * which contains the total size of the dataset.
16215 * @return {Object} data A data block which is used by an Roo.data.Store object as
16216 * a cache of Roo.data.Records.
16218 readRecords : function(o){
16220 * After any data loads, the raw JSON data is available for further custom processing.
16224 var s = this.meta, Record = this.recordType,
16225 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16227 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16229 if(s.totalProperty) {
16230 this.getTotal = this.getJsonAccessor(s.totalProperty);
16232 if(s.successProperty) {
16233 this.getSuccess = this.getJsonAccessor(s.successProperty);
16235 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16237 var g = this.getJsonAccessor(s.id);
16238 this.getId = function(rec) {
16240 return (r === undefined || r === "") ? null : r;
16243 this.getId = function(){return null;};
16246 for(var jj = 0; jj < fl; jj++){
16248 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16249 this.ef[jj] = this.getJsonAccessor(map);
16253 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16254 if(s.totalProperty){
16255 var vt = parseInt(this.getTotal(o), 10);
16260 if(s.successProperty){
16261 var vs = this.getSuccess(o);
16262 if(vs === false || vs === 'false'){
16267 for(var i = 0; i < c; i++){
16270 var id = this.getId(n);
16271 for(var j = 0; j < fl; j++){
16273 var v = this.ef[j](n);
16275 Roo.log('missing convert for ' + f.name);
16279 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16281 var record = new Record(values, id);
16283 records[i] = record;
16289 totalRecords : totalRecords
16292 // used when loading children.. @see loadDataFromChildren
16293 toLoadData: function(rec)
16295 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16296 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16297 return { data : data, total : data.length };
16302 * Ext JS Library 1.1.1
16303 * Copyright(c) 2006-2007, Ext JS, LLC.
16305 * Originally Released Under LGPL - original licence link has changed is not relivant.
16308 * <script type="text/javascript">
16312 * @class Roo.data.ArrayReader
16313 * @extends Roo.data.DataReader
16314 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16315 * Each element of that Array represents a row of data fields. The
16316 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16317 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16321 var RecordDef = Roo.data.Record.create([
16322 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16323 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16325 var myReader = new Roo.data.ArrayReader({
16326 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16330 * This would consume an Array like this:
16332 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16336 * Create a new JsonReader
16337 * @param {Object} meta Metadata configuration options.
16338 * @param {Object|Array} recordType Either an Array of field definition objects
16340 * @cfg {Array} fields Array of field definition objects
16341 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16342 * as specified to {@link Roo.data.Record#create},
16343 * or an {@link Roo.data.Record} object
16346 * created using {@link Roo.data.Record#create}.
16348 Roo.data.ArrayReader = function(meta, recordType)
16350 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16353 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16356 * Create a data block containing Roo.data.Records from an XML document.
16357 * @param {Object} o An Array of row objects which represents the dataset.
16358 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16359 * a cache of Roo.data.Records.
16361 readRecords : function(o)
16363 var sid = this.meta ? this.meta.id : null;
16364 var recordType = this.recordType, fields = recordType.prototype.fields;
16367 for(var i = 0; i < root.length; i++){
16370 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16371 for(var j = 0, jlen = fields.length; j < jlen; j++){
16372 var f = fields.items[j];
16373 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16374 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16376 values[f.name] = v;
16378 var record = new recordType(values, id);
16380 records[records.length] = record;
16384 totalRecords : records.length
16387 // used when loading children.. @see loadDataFromChildren
16388 toLoadData: function(rec)
16390 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16391 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16402 * @class Roo.bootstrap.ComboBox
16403 * @extends Roo.bootstrap.TriggerField
16404 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16405 * @cfg {Boolean} append (true|false) default false
16406 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16407 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16408 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16409 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16410 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16411 * @cfg {Boolean} animate default true
16412 * @cfg {Boolean} emptyResultText only for touch device
16413 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16414 * @cfg {String} emptyTitle default ''
16415 * @cfg {Number} width fixed with? experimental
16417 * Create a new ComboBox.
16418 * @param {Object} config Configuration options
16420 Roo.bootstrap.ComboBox = function(config){
16421 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16425 * Fires when the dropdown list is expanded
16426 * @param {Roo.bootstrap.ComboBox} combo This combo box
16431 * Fires when the dropdown list is collapsed
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16436 * @event beforeselect
16437 * Fires before a list item is selected. Return false to cancel the selection.
16438 * @param {Roo.bootstrap.ComboBox} combo This combo box
16439 * @param {Roo.data.Record} record The data record returned from the underlying store
16440 * @param {Number} index The index of the selected item in the dropdown list
16442 'beforeselect' : true,
16445 * Fires when a list item is selected
16446 * @param {Roo.bootstrap.ComboBox} combo This combo box
16447 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16448 * @param {Number} index The index of the selected item in the dropdown list
16452 * @event beforequery
16453 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16454 * The event object passed has these properties:
16455 * @param {Roo.bootstrap.ComboBox} combo This combo box
16456 * @param {String} query The query
16457 * @param {Boolean} forceAll true to force "all" query
16458 * @param {Boolean} cancel true to cancel the query
16459 * @param {Object} e The query event object
16461 'beforequery': true,
16464 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16465 * @param {Roo.bootstrap.ComboBox} combo This combo box
16470 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16471 * @param {Roo.bootstrap.ComboBox} combo This combo box
16472 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16477 * Fires when the remove value from the combobox array
16478 * @param {Roo.bootstrap.ComboBox} combo This combo box
16482 * @event afterremove
16483 * Fires when the remove value from the combobox array
16484 * @param {Roo.bootstrap.ComboBox} combo This combo box
16486 'afterremove' : true,
16488 * @event specialfilter
16489 * Fires when specialfilter
16490 * @param {Roo.bootstrap.ComboBox} combo This combo box
16492 'specialfilter' : true,
16495 * Fires when tick the element
16496 * @param {Roo.bootstrap.ComboBox} combo This combo box
16500 * @event touchviewdisplay
16501 * Fires when touch view require special display (default is using displayField)
16502 * @param {Roo.bootstrap.ComboBox} combo This combo box
16503 * @param {Object} cfg set html .
16505 'touchviewdisplay' : true
16510 this.tickItems = [];
16512 this.selectedIndex = -1;
16513 if(this.mode == 'local'){
16514 if(config.queryDelay === undefined){
16515 this.queryDelay = 10;
16517 if(config.minChars === undefined){
16523 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16526 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16527 * rendering into an Roo.Editor, defaults to false)
16530 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16531 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16534 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16537 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16538 * the dropdown list (defaults to undefined, with no header element)
16542 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16546 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16548 listWidth: undefined,
16550 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16551 * mode = 'remote' or 'text' if mode = 'local')
16553 displayField: undefined,
16556 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16557 * mode = 'remote' or 'value' if mode = 'local').
16558 * Note: use of a valueField requires the user make a selection
16559 * in order for a value to be mapped.
16561 valueField: undefined,
16563 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16568 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16569 * field's data value (defaults to the underlying DOM element's name)
16571 hiddenName: undefined,
16573 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16577 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16579 selectedClass: 'active',
16582 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16586 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16587 * anchor positions (defaults to 'tl-bl')
16589 listAlign: 'tl-bl?',
16591 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16595 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16596 * query specified by the allQuery config option (defaults to 'query')
16598 triggerAction: 'query',
16600 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16601 * (defaults to 4, does not apply if editable = false)
16605 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16606 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16610 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16611 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16615 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16616 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16620 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16621 * when editable = true (defaults to false)
16623 selectOnFocus:false,
16625 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16627 queryParam: 'query',
16629 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16630 * when mode = 'remote' (defaults to 'Loading...')
16632 loadingText: 'Loading...',
16634 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16638 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16642 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16643 * traditional select (defaults to true)
16647 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16651 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16655 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16656 * listWidth has a higher value)
16660 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16661 * allow the user to set arbitrary text into the field (defaults to false)
16663 forceSelection:false,
16665 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16666 * if typeAhead = true (defaults to 250)
16668 typeAheadDelay : 250,
16670 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16671 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16673 valueNotFoundText : undefined,
16675 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16677 blockFocus : false,
16680 * @cfg {Boolean} disableClear Disable showing of clear button.
16682 disableClear : false,
16684 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16686 alwaysQuery : false,
16689 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16694 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16696 invalidClass : "has-warning",
16699 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16701 validClass : "has-success",
16704 * @cfg {Boolean} specialFilter (true|false) special filter default false
16706 specialFilter : false,
16709 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16711 mobileTouchView : true,
16714 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16716 useNativeIOS : false,
16719 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16721 mobile_restrict_height : false,
16723 ios_options : false,
16735 btnPosition : 'right',
16736 triggerList : true,
16737 showToggleBtn : true,
16739 emptyResultText: 'Empty',
16740 triggerText : 'Select',
16744 // element that contains real text value.. (when hidden is used..)
16746 getAutoCreate : function()
16751 * Render classic select for iso
16754 if(Roo.isIOS && this.useNativeIOS){
16755 cfg = this.getAutoCreateNativeIOS();
16763 if(Roo.isTouch && this.mobileTouchView){
16764 cfg = this.getAutoCreateTouchView();
16771 if(!this.tickable){
16772 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16777 * ComboBox with tickable selections
16780 var align = this.labelAlign || this.parentLabelAlign();
16783 cls : 'form-group roo-combobox-tickable' //input-group
16786 var btn_text_select = '';
16787 var btn_text_done = '';
16788 var btn_text_cancel = '';
16790 if (this.btn_text_show) {
16791 btn_text_select = 'Select';
16792 btn_text_done = 'Done';
16793 btn_text_cancel = 'Cancel';
16798 cls : 'tickable-buttons',
16803 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16804 //html : this.triggerText
16805 html: btn_text_select
16811 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16813 html: btn_text_done
16819 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16821 html: btn_text_cancel
16827 buttons.cn.unshift({
16829 cls: 'roo-select2-search-field-input'
16835 Roo.each(buttons.cn, function(c){
16837 c.cls += ' btn-' + _this.size;
16840 if (_this.disabled) {
16847 style : 'display: contents',
16852 cls: 'form-hidden-field'
16856 cls: 'roo-select2-choices',
16860 cls: 'roo-select2-search-field',
16871 cls: 'roo-select2-container input-group roo-select2-container-multi',
16877 // cls: 'typeahead typeahead-long dropdown-menu',
16878 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16883 if(this.hasFeedback && !this.allowBlank){
16887 cls: 'glyphicon form-control-feedback'
16890 combobox.cn.push(feedback);
16897 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16898 tooltip : 'This field is required'
16900 if (Roo.bootstrap.version == 4) {
16903 style : 'display:none'
16906 if (align ==='left' && this.fieldLabel.length) {
16908 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16915 cls : 'control-label col-form-label',
16916 html : this.fieldLabel
16928 var labelCfg = cfg.cn[1];
16929 var contentCfg = cfg.cn[2];
16932 if(this.indicatorpos == 'right'){
16938 cls : 'control-label col-form-label',
16942 html : this.fieldLabel
16958 labelCfg = cfg.cn[0];
16959 contentCfg = cfg.cn[1];
16963 if(this.labelWidth > 12){
16964 labelCfg.style = "width: " + this.labelWidth + 'px';
16966 if(this.width * 1 > 0){
16967 contentCfg.style = "width: " + this.width + 'px';
16969 if(this.labelWidth < 13 && this.labelmd == 0){
16970 this.labelmd = this.labelWidth;
16973 if(this.labellg > 0){
16974 labelCfg.cls += ' col-lg-' + this.labellg;
16975 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16978 if(this.labelmd > 0){
16979 labelCfg.cls += ' col-md-' + this.labelmd;
16980 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16983 if(this.labelsm > 0){
16984 labelCfg.cls += ' col-sm-' + this.labelsm;
16985 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16988 if(this.labelxs > 0){
16989 labelCfg.cls += ' col-xs-' + this.labelxs;
16990 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16994 } else if ( this.fieldLabel.length) {
16995 // Roo.log(" label");
17000 //cls : 'input-group-addon',
17001 html : this.fieldLabel
17006 if(this.indicatorpos == 'right'){
17010 //cls : 'input-group-addon',
17011 html : this.fieldLabel
17021 // Roo.log(" no label && no align");
17028 ['xs','sm','md','lg'].map(function(size){
17029 if (settings[size]) {
17030 cfg.cls += ' col-' + size + '-' + settings[size];
17038 _initEventsCalled : false,
17041 initEvents: function()
17043 if (this._initEventsCalled) { // as we call render... prevent looping...
17046 this._initEventsCalled = true;
17049 throw "can not find store for combo";
17052 this.indicator = this.indicatorEl();
17054 this.store = Roo.factory(this.store, Roo.data);
17055 this.store.parent = this;
17057 // if we are building from html. then this element is so complex, that we can not really
17058 // use the rendered HTML.
17059 // so we have to trash and replace the previous code.
17060 if (Roo.XComponent.build_from_html) {
17061 // remove this element....
17062 var e = this.el.dom, k=0;
17063 while (e ) { e = e.previousSibling; ++k;}
17068 this.rendered = false;
17070 this.render(this.parent().getChildContainer(true), k);
17073 if(Roo.isIOS && this.useNativeIOS){
17074 this.initIOSView();
17082 if(Roo.isTouch && this.mobileTouchView){
17083 this.initTouchView();
17088 this.initTickableEvents();
17092 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17094 if(this.hiddenName){
17096 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17098 this.hiddenField.dom.value =
17099 this.hiddenValue !== undefined ? this.hiddenValue :
17100 this.value !== undefined ? this.value : '';
17102 // prevent input submission
17103 this.el.dom.removeAttribute('name');
17104 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17109 // this.el.dom.setAttribute('autocomplete', 'off');
17112 var cls = 'x-combo-list';
17114 //this.list = new Roo.Layer({
17115 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17121 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17122 _this.list.setWidth(lw);
17125 this.list.on('mouseover', this.onViewOver, this);
17126 this.list.on('mousemove', this.onViewMove, this);
17127 this.list.on('scroll', this.onViewScroll, this);
17130 this.list.swallowEvent('mousewheel');
17131 this.assetHeight = 0;
17134 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17135 this.assetHeight += this.header.getHeight();
17138 this.innerList = this.list.createChild({cls:cls+'-inner'});
17139 this.innerList.on('mouseover', this.onViewOver, this);
17140 this.innerList.on('mousemove', this.onViewMove, this);
17141 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17143 if(this.allowBlank && !this.pageSize && !this.disableClear){
17144 this.footer = this.list.createChild({cls:cls+'-ft'});
17145 this.pageTb = new Roo.Toolbar(this.footer);
17149 this.footer = this.list.createChild({cls:cls+'-ft'});
17150 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17151 {pageSize: this.pageSize});
17155 if (this.pageTb && this.allowBlank && !this.disableClear) {
17157 this.pageTb.add(new Roo.Toolbar.Fill(), {
17158 cls: 'x-btn-icon x-btn-clear',
17160 handler: function()
17163 _this.clearValue();
17164 _this.onSelect(false, -1);
17169 this.assetHeight += this.footer.getHeight();
17174 this.tpl = Roo.bootstrap.version == 4 ?
17175 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17176 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17179 this.view = new Roo.View(this.list, this.tpl, {
17180 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17182 //this.view.wrapEl.setDisplayed(false);
17183 this.view.on('click', this.onViewClick, this);
17186 this.store.on('beforeload', this.onBeforeLoad, this);
17187 this.store.on('load', this.onLoad, this);
17188 this.store.on('loadexception', this.onLoadException, this);
17190 if(this.resizable){
17191 this.resizer = new Roo.Resizable(this.list, {
17192 pinned:true, handles:'se'
17194 this.resizer.on('resize', function(r, w, h){
17195 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17196 this.listWidth = w;
17197 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17198 this.restrictHeight();
17200 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17203 if(!this.editable){
17204 this.editable = true;
17205 this.setEditable(false);
17210 if (typeof(this.events.add.listeners) != 'undefined') {
17212 this.addicon = this.wrap.createChild(
17213 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17215 this.addicon.on('click', function(e) {
17216 this.fireEvent('add', this);
17219 if (typeof(this.events.edit.listeners) != 'undefined') {
17221 this.editicon = this.wrap.createChild(
17222 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17223 if (this.addicon) {
17224 this.editicon.setStyle('margin-left', '40px');
17226 this.editicon.on('click', function(e) {
17228 // we fire even if inothing is selected..
17229 this.fireEvent('edit', this, this.lastData );
17235 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17236 "up" : function(e){
17237 this.inKeyMode = true;
17241 "down" : function(e){
17242 if(!this.isExpanded()){
17243 this.onTriggerClick();
17245 this.inKeyMode = true;
17250 "enter" : function(e){
17251 // this.onViewClick();
17255 if(this.fireEvent("specialkey", this, e)){
17256 this.onViewClick(false);
17262 "esc" : function(e){
17266 "tab" : function(e){
17269 if(this.fireEvent("specialkey", this, e)){
17270 this.onViewClick(false);
17278 doRelay : function(foo, bar, hname){
17279 if(hname == 'down' || this.scope.isExpanded()){
17280 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17289 this.queryDelay = Math.max(this.queryDelay || 10,
17290 this.mode == 'local' ? 10 : 250);
17293 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17295 if(this.typeAhead){
17296 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17298 if(this.editable !== false){
17299 this.inputEl().on("keyup", this.onKeyUp, this);
17301 if(this.forceSelection){
17302 this.inputEl().on('blur', this.doForce, this);
17306 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17307 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17311 initTickableEvents: function()
17315 if(this.hiddenName){
17317 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17319 this.hiddenField.dom.value =
17320 this.hiddenValue !== undefined ? this.hiddenValue :
17321 this.value !== undefined ? this.value : '';
17323 // prevent input submission
17324 this.el.dom.removeAttribute('name');
17325 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17330 // this.list = this.el.select('ul.dropdown-menu',true).first();
17332 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17333 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17334 if(this.triggerList){
17335 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17338 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17339 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17341 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17342 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17344 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17345 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17347 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17348 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17349 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17352 this.cancelBtn.hide();
17357 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17358 _this.list.setWidth(lw);
17361 this.list.on('mouseover', this.onViewOver, this);
17362 this.list.on('mousemove', this.onViewMove, this);
17364 this.list.on('scroll', this.onViewScroll, this);
17367 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17368 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17371 this.view = new Roo.View(this.list, this.tpl, {
17376 selectedClass: this.selectedClass
17379 //this.view.wrapEl.setDisplayed(false);
17380 this.view.on('click', this.onViewClick, this);
17384 this.store.on('beforeload', this.onBeforeLoad, this);
17385 this.store.on('load', this.onLoad, this);
17386 this.store.on('loadexception', this.onLoadException, this);
17389 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17390 "up" : function(e){
17391 this.inKeyMode = true;
17395 "down" : function(e){
17396 this.inKeyMode = true;
17400 "enter" : function(e){
17401 if(this.fireEvent("specialkey", this, e)){
17402 this.onViewClick(false);
17408 "esc" : function(e){
17409 this.onTickableFooterButtonClick(e, false, false);
17412 "tab" : function(e){
17413 this.fireEvent("specialkey", this, e);
17415 this.onTickableFooterButtonClick(e, false, false);
17422 doRelay : function(e, fn, key){
17423 if(this.scope.isExpanded()){
17424 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17433 this.queryDelay = Math.max(this.queryDelay || 10,
17434 this.mode == 'local' ? 10 : 250);
17437 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17439 if(this.typeAhead){
17440 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17443 if(this.editable !== false){
17444 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17447 this.indicator = this.indicatorEl();
17449 if(this.indicator){
17450 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17451 this.indicator.hide();
17456 onDestroy : function(){
17458 this.view.setStore(null);
17459 this.view.el.removeAllListeners();
17460 this.view.el.remove();
17461 this.view.purgeListeners();
17464 this.list.dom.innerHTML = '';
17468 this.store.un('beforeload', this.onBeforeLoad, this);
17469 this.store.un('load', this.onLoad, this);
17470 this.store.un('loadexception', this.onLoadException, this);
17472 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17476 fireKey : function(e){
17477 if(e.isNavKeyPress() && !this.list.isVisible()){
17478 this.fireEvent("specialkey", this, e);
17483 onResize: function(w, h)
17487 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17489 // if(typeof w != 'number'){
17490 // // we do not handle it!?!?
17493 // var tw = this.trigger.getWidth();
17494 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17495 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17497 // this.inputEl().setWidth( this.adjustWidth('input', x));
17499 // //this.trigger.setStyle('left', x+'px');
17501 // if(this.list && this.listWidth === undefined){
17502 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17503 // this.list.setWidth(lw);
17504 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17512 * Allow or prevent the user from directly editing the field text. If false is passed,
17513 * the user will only be able to select from the items defined in the dropdown list. This method
17514 * is the runtime equivalent of setting the 'editable' config option at config time.
17515 * @param {Boolean} value True to allow the user to directly edit the field text
17517 setEditable : function(value){
17518 if(value == this.editable){
17521 this.editable = value;
17523 this.inputEl().dom.setAttribute('readOnly', true);
17524 this.inputEl().on('mousedown', this.onTriggerClick, this);
17525 this.inputEl().addClass('x-combo-noedit');
17527 this.inputEl().dom.removeAttribute('readOnly');
17528 this.inputEl().un('mousedown', this.onTriggerClick, this);
17529 this.inputEl().removeClass('x-combo-noedit');
17535 onBeforeLoad : function(combo,opts){
17536 if(!this.hasFocus){
17540 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17542 this.restrictHeight();
17543 this.selectedIndex = -1;
17547 onLoad : function(){
17549 this.hasQuery = false;
17551 if(!this.hasFocus){
17555 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17556 this.loading.hide();
17559 if(this.store.getCount() > 0){
17562 this.restrictHeight();
17563 if(this.lastQuery == this.allQuery){
17564 if(this.editable && !this.tickable){
17565 this.inputEl().dom.select();
17569 !this.selectByValue(this.value, true) &&
17572 !this.store.lastOptions ||
17573 typeof(this.store.lastOptions.add) == 'undefined' ||
17574 this.store.lastOptions.add != true
17577 this.select(0, true);
17580 if(this.autoFocus){
17583 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17584 this.taTask.delay(this.typeAheadDelay);
17588 this.onEmptyResults();
17594 onLoadException : function()
17596 this.hasQuery = false;
17598 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17599 this.loading.hide();
17602 if(this.tickable && this.editable){
17607 // only causes errors at present
17608 //Roo.log(this.store.reader.jsonData);
17609 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17611 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17617 onTypeAhead : function(){
17618 if(this.store.getCount() > 0){
17619 var r = this.store.getAt(0);
17620 var newValue = r.data[this.displayField];
17621 var len = newValue.length;
17622 var selStart = this.getRawValue().length;
17624 if(selStart != len){
17625 this.setRawValue(newValue);
17626 this.selectText(selStart, newValue.length);
17632 onSelect : function(record, index){
17634 if(this.fireEvent('beforeselect', this, record, index) !== false){
17636 this.setFromData(index > -1 ? record.data : false);
17639 this.fireEvent('select', this, record, index);
17644 * Returns the currently selected field value or empty string if no value is set.
17645 * @return {String} value The selected value
17647 getValue : function()
17649 if(Roo.isIOS && this.useNativeIOS){
17650 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17654 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17657 if(this.valueField){
17658 return typeof this.value != 'undefined' ? this.value : '';
17660 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17664 getRawValue : function()
17666 if(Roo.isIOS && this.useNativeIOS){
17667 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17670 var v = this.inputEl().getValue();
17676 * Clears any text/value currently set in the field
17678 clearValue : function(){
17680 if(this.hiddenField){
17681 this.hiddenField.dom.value = '';
17684 this.setRawValue('');
17685 this.lastSelectionText = '';
17686 this.lastData = false;
17688 var close = this.closeTriggerEl();
17699 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17700 * will be displayed in the field. If the value does not match the data value of an existing item,
17701 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17702 * Otherwise the field will be blank (although the value will still be set).
17703 * @param {String} value The value to match
17705 setValue : function(v)
17707 if(Roo.isIOS && this.useNativeIOS){
17708 this.setIOSValue(v);
17718 if(this.valueField){
17719 var r = this.findRecord(this.valueField, v);
17721 text = r.data[this.displayField];
17722 }else if(this.valueNotFoundText !== undefined){
17723 text = this.valueNotFoundText;
17726 this.lastSelectionText = text;
17727 if(this.hiddenField){
17728 this.hiddenField.dom.value = v;
17730 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17733 var close = this.closeTriggerEl();
17736 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17742 * @property {Object} the last set data for the element
17747 * Sets the value of the field based on a object which is related to the record format for the store.
17748 * @param {Object} value the value to set as. or false on reset?
17750 setFromData : function(o){
17757 var dv = ''; // display value
17758 var vv = ''; // value value..
17760 if (this.displayField) {
17761 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17763 // this is an error condition!!!
17764 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17767 if(this.valueField){
17768 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17771 var close = this.closeTriggerEl();
17774 if(dv.length || vv * 1 > 0){
17776 this.blockFocus=true;
17782 if(this.hiddenField){
17783 this.hiddenField.dom.value = vv;
17785 this.lastSelectionText = dv;
17786 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17790 // no hidden field.. - we store the value in 'value', but still display
17791 // display field!!!!
17792 this.lastSelectionText = dv;
17793 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17800 reset : function(){
17801 // overridden so that last data is reset..
17808 this.setValue(this.originalValue);
17809 //this.clearInvalid();
17810 this.lastData = false;
17812 this.view.clearSelections();
17818 findRecord : function(prop, value){
17820 if(this.store.getCount() > 0){
17821 this.store.each(function(r){
17822 if(r.data[prop] == value){
17832 getName: function()
17834 // returns hidden if it's set..
17835 if (!this.rendered) {return ''};
17836 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17840 onViewMove : function(e, t){
17841 this.inKeyMode = false;
17845 onViewOver : function(e, t){
17846 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17849 var item = this.view.findItemFromChild(t);
17852 var index = this.view.indexOf(item);
17853 this.select(index, false);
17858 onViewClick : function(view, doFocus, el, e)
17860 var index = this.view.getSelectedIndexes()[0];
17862 var r = this.store.getAt(index);
17866 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17873 Roo.each(this.tickItems, function(v,k){
17875 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17877 _this.tickItems.splice(k, 1);
17879 if(typeof(e) == 'undefined' && view == false){
17880 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17892 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17893 this.tickItems.push(r.data);
17896 if(typeof(e) == 'undefined' && view == false){
17897 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17904 this.onSelect(r, index);
17906 if(doFocus !== false && !this.blockFocus){
17907 this.inputEl().focus();
17912 restrictHeight : function(){
17913 //this.innerList.dom.style.height = '';
17914 //var inner = this.innerList.dom;
17915 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17916 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17917 //this.list.beginUpdate();
17918 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17919 this.list.alignTo(this.inputEl(), this.listAlign);
17920 this.list.alignTo(this.inputEl(), this.listAlign);
17921 //this.list.endUpdate();
17925 onEmptyResults : function(){
17927 if(this.tickable && this.editable){
17928 this.hasFocus = false;
17929 this.restrictHeight();
17937 * Returns true if the dropdown list is expanded, else false.
17939 isExpanded : function(){
17940 return this.list.isVisible();
17944 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17945 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17946 * @param {String} value The data value of the item to select
17947 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17948 * selected item if it is not currently in view (defaults to true)
17949 * @return {Boolean} True if the value matched an item in the list, else false
17951 selectByValue : function(v, scrollIntoView){
17952 if(v !== undefined && v !== null){
17953 var r = this.findRecord(this.valueField || this.displayField, v);
17955 this.select(this.store.indexOf(r), scrollIntoView);
17963 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17964 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17965 * @param {Number} index The zero-based index of the list item to select
17966 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17967 * selected item if it is not currently in view (defaults to true)
17969 select : function(index, scrollIntoView){
17970 this.selectedIndex = index;
17971 this.view.select(index);
17972 if(scrollIntoView !== false){
17973 var el = this.view.getNode(index);
17975 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17978 this.list.scrollChildIntoView(el, false);
17984 selectNext : function(){
17985 var ct = this.store.getCount();
17987 if(this.selectedIndex == -1){
17989 }else if(this.selectedIndex < ct-1){
17990 this.select(this.selectedIndex+1);
17996 selectPrev : function(){
17997 var ct = this.store.getCount();
17999 if(this.selectedIndex == -1){
18001 }else if(this.selectedIndex != 0){
18002 this.select(this.selectedIndex-1);
18008 onKeyUp : function(e){
18009 if(this.editable !== false && !e.isSpecialKey()){
18010 this.lastKey = e.getKey();
18011 this.dqTask.delay(this.queryDelay);
18016 validateBlur : function(){
18017 return !this.list || !this.list.isVisible();
18021 initQuery : function(){
18023 var v = this.getRawValue();
18025 if(this.tickable && this.editable){
18026 v = this.tickableInputEl().getValue();
18033 doForce : function(){
18034 if(this.inputEl().dom.value.length > 0){
18035 this.inputEl().dom.value =
18036 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18042 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18043 * query allowing the query action to be canceled if needed.
18044 * @param {String} query The SQL query to execute
18045 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18046 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18047 * saved in the current store (defaults to false)
18049 doQuery : function(q, forceAll){
18051 if(q === undefined || q === null){
18056 forceAll: forceAll,
18060 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18065 forceAll = qe.forceAll;
18066 if(forceAll === true || (q.length >= this.minChars)){
18068 this.hasQuery = true;
18070 if(this.lastQuery != q || this.alwaysQuery){
18071 this.lastQuery = q;
18072 if(this.mode == 'local'){
18073 this.selectedIndex = -1;
18075 this.store.clearFilter();
18078 if(this.specialFilter){
18079 this.fireEvent('specialfilter', this);
18084 this.store.filter(this.displayField, q);
18087 this.store.fireEvent("datachanged", this.store);
18094 this.store.baseParams[this.queryParam] = q;
18096 var options = {params : this.getParams(q)};
18099 options.add = true;
18100 options.params.start = this.page * this.pageSize;
18103 this.store.load(options);
18106 * this code will make the page width larger, at the beginning, the list not align correctly,
18107 * we should expand the list on onLoad
18108 * so command out it
18113 this.selectedIndex = -1;
18118 this.loadNext = false;
18122 getParams : function(q){
18124 //p[this.queryParam] = q;
18128 p.limit = this.pageSize;
18134 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18136 collapse : function(){
18137 if(!this.isExpanded()){
18143 this.hasFocus = false;
18147 this.cancelBtn.hide();
18148 this.trigger.show();
18151 this.tickableInputEl().dom.value = '';
18152 this.tickableInputEl().blur();
18157 Roo.get(document).un('mousedown', this.collapseIf, this);
18158 Roo.get(document).un('mousewheel', this.collapseIf, this);
18159 if (!this.editable) {
18160 Roo.get(document).un('keydown', this.listKeyPress, this);
18162 this.fireEvent('collapse', this);
18168 collapseIf : function(e){
18169 var in_combo = e.within(this.el);
18170 var in_list = e.within(this.list);
18171 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18173 if (in_combo || in_list || is_list) {
18174 //e.stopPropagation();
18179 this.onTickableFooterButtonClick(e, false, false);
18187 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18189 expand : function(){
18191 if(this.isExpanded() || !this.hasFocus){
18195 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18196 this.list.setWidth(lw);
18202 this.restrictHeight();
18206 this.tickItems = Roo.apply([], this.item);
18209 this.cancelBtn.show();
18210 this.trigger.hide();
18213 this.tickableInputEl().focus();
18218 Roo.get(document).on('mousedown', this.collapseIf, this);
18219 Roo.get(document).on('mousewheel', this.collapseIf, this);
18220 if (!this.editable) {
18221 Roo.get(document).on('keydown', this.listKeyPress, this);
18224 this.fireEvent('expand', this);
18228 // Implements the default empty TriggerField.onTriggerClick function
18229 onTriggerClick : function(e)
18231 Roo.log('trigger click');
18233 if(this.disabled || !this.triggerList){
18238 this.loadNext = false;
18240 if(this.isExpanded()){
18242 if (!this.blockFocus) {
18243 this.inputEl().focus();
18247 this.hasFocus = true;
18248 if(this.triggerAction == 'all') {
18249 this.doQuery(this.allQuery, true);
18251 this.doQuery(this.getRawValue());
18253 if (!this.blockFocus) {
18254 this.inputEl().focus();
18259 onTickableTriggerClick : function(e)
18266 this.loadNext = false;
18267 this.hasFocus = true;
18269 if(this.triggerAction == 'all') {
18270 this.doQuery(this.allQuery, true);
18272 this.doQuery(this.getRawValue());
18276 onSearchFieldClick : function(e)
18278 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18279 this.onTickableFooterButtonClick(e, false, false);
18283 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18288 this.loadNext = false;
18289 this.hasFocus = true;
18291 if(this.triggerAction == 'all') {
18292 this.doQuery(this.allQuery, true);
18294 this.doQuery(this.getRawValue());
18298 listKeyPress : function(e)
18300 //Roo.log('listkeypress');
18301 // scroll to first matching element based on key pres..
18302 if (e.isSpecialKey()) {
18305 var k = String.fromCharCode(e.getKey()).toUpperCase();
18308 var csel = this.view.getSelectedNodes();
18309 var cselitem = false;
18311 var ix = this.view.indexOf(csel[0]);
18312 cselitem = this.store.getAt(ix);
18313 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18319 this.store.each(function(v) {
18321 // start at existing selection.
18322 if (cselitem.id == v.id) {
18328 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18329 match = this.store.indexOf(v);
18335 if (match === false) {
18336 return true; // no more action?
18339 this.view.select(match);
18340 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18341 sn.scrollIntoView(sn.dom.parentNode, false);
18344 onViewScroll : function(e, t){
18346 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){
18350 this.hasQuery = true;
18352 this.loading = this.list.select('.loading', true).first();
18354 if(this.loading === null){
18355 this.list.createChild({
18357 cls: 'loading roo-select2-more-results roo-select2-active',
18358 html: 'Loading more results...'
18361 this.loading = this.list.select('.loading', true).first();
18363 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18365 this.loading.hide();
18368 this.loading.show();
18373 this.loadNext = true;
18375 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18380 addItem : function(o)
18382 var dv = ''; // display value
18384 if (this.displayField) {
18385 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18387 // this is an error condition!!!
18388 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18395 var choice = this.choices.createChild({
18397 cls: 'roo-select2-search-choice',
18406 cls: 'roo-select2-search-choice-close fa fa-times',
18411 }, this.searchField);
18413 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18415 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18423 this.inputEl().dom.value = '';
18428 onRemoveItem : function(e, _self, o)
18430 e.preventDefault();
18432 this.lastItem = Roo.apply([], this.item);
18434 var index = this.item.indexOf(o.data) * 1;
18437 Roo.log('not this item?!');
18441 this.item.splice(index, 1);
18446 this.fireEvent('remove', this, e);
18452 syncValue : function()
18454 if(!this.item.length){
18461 Roo.each(this.item, function(i){
18462 if(_this.valueField){
18463 value.push(i[_this.valueField]);
18470 this.value = value.join(',');
18472 if(this.hiddenField){
18473 this.hiddenField.dom.value = this.value;
18476 this.store.fireEvent("datachanged", this.store);
18481 clearItem : function()
18483 if(!this.multiple){
18489 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18497 if(this.tickable && !Roo.isTouch){
18498 this.view.refresh();
18502 inputEl: function ()
18504 if(Roo.isIOS && this.useNativeIOS){
18505 return this.el.select('select.roo-ios-select', true).first();
18508 if(Roo.isTouch && this.mobileTouchView){
18509 return this.el.select('input.form-control',true).first();
18513 return this.searchField;
18516 return this.el.select('input.form-control',true).first();
18519 onTickableFooterButtonClick : function(e, btn, el)
18521 e.preventDefault();
18523 this.lastItem = Roo.apply([], this.item);
18525 if(btn && btn.name == 'cancel'){
18526 this.tickItems = Roo.apply([], this.item);
18535 Roo.each(this.tickItems, function(o){
18543 validate : function()
18545 if(this.getVisibilityEl().hasClass('hidden')){
18549 var v = this.getRawValue();
18552 v = this.getValue();
18555 if(this.disabled || this.allowBlank || v.length){
18560 this.markInvalid();
18564 tickableInputEl : function()
18566 if(!this.tickable || !this.editable){
18567 return this.inputEl();
18570 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18574 getAutoCreateTouchView : function()
18579 cls: 'form-group' //input-group
18585 type : this.inputType,
18586 cls : 'form-control x-combo-noedit',
18587 autocomplete: 'new-password',
18588 placeholder : this.placeholder || '',
18593 input.name = this.name;
18597 input.cls += ' input-' + this.size;
18600 if (this.disabled) {
18601 input.disabled = true;
18605 cls : 'roo-combobox-wrap',
18612 inputblock.cls += ' input-group';
18614 inputblock.cn.unshift({
18616 cls : 'input-group-addon input-group-prepend input-group-text',
18621 if(this.removable && !this.multiple){
18622 inputblock.cls += ' roo-removable';
18624 inputblock.cn.push({
18627 cls : 'roo-combo-removable-btn close'
18631 if(this.hasFeedback && !this.allowBlank){
18633 inputblock.cls += ' has-feedback';
18635 inputblock.cn.push({
18637 cls: 'glyphicon form-control-feedback'
18644 inputblock.cls += (this.before) ? '' : ' input-group';
18646 inputblock.cn.push({
18648 cls : 'input-group-addon input-group-append input-group-text',
18654 var ibwrap = inputblock;
18659 cls: 'roo-select2-choices',
18663 cls: 'roo-select2-search-field',
18676 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18681 cls: 'form-hidden-field'
18687 if(!this.multiple && this.showToggleBtn){
18693 if (this.caret != false) {
18696 cls: 'fa fa-' + this.caret
18703 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18705 Roo.bootstrap.version == 3 ? caret : '',
18708 cls: 'combobox-clear',
18722 combobox.cls += ' roo-select2-container-multi';
18725 var required = this.allowBlank ? {
18727 style: 'display: none'
18730 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18731 tooltip : 'This field is required'
18734 var align = this.labelAlign || this.parentLabelAlign();
18736 if (align ==='left' && this.fieldLabel.length) {
18742 cls : 'control-label col-form-label',
18743 html : this.fieldLabel
18747 cls : 'roo-combobox-wrap ',
18754 var labelCfg = cfg.cn[1];
18755 var contentCfg = cfg.cn[2];
18758 if(this.indicatorpos == 'right'){
18763 cls : 'control-label col-form-label',
18767 html : this.fieldLabel
18773 cls : "roo-combobox-wrap ",
18781 labelCfg = cfg.cn[0];
18782 contentCfg = cfg.cn[1];
18787 if(this.labelWidth > 12){
18788 labelCfg.style = "width: " + this.labelWidth + 'px';
18791 if(this.labelWidth < 13 && this.labelmd == 0){
18792 this.labelmd = this.labelWidth;
18795 if(this.labellg > 0){
18796 labelCfg.cls += ' col-lg-' + this.labellg;
18797 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18800 if(this.labelmd > 0){
18801 labelCfg.cls += ' col-md-' + this.labelmd;
18802 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18805 if(this.labelsm > 0){
18806 labelCfg.cls += ' col-sm-' + this.labelsm;
18807 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18810 if(this.labelxs > 0){
18811 labelCfg.cls += ' col-xs-' + this.labelxs;
18812 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18816 } else if ( this.fieldLabel.length) {
18821 cls : 'control-label',
18822 html : this.fieldLabel
18833 if(this.indicatorpos == 'right'){
18837 cls : 'control-label',
18838 html : this.fieldLabel,
18856 var settings = this;
18858 ['xs','sm','md','lg'].map(function(size){
18859 if (settings[size]) {
18860 cfg.cls += ' col-' + size + '-' + settings[size];
18867 initTouchView : function()
18869 this.renderTouchView();
18871 this.touchViewEl.on('scroll', function(){
18872 this.el.dom.scrollTop = 0;
18875 this.originalValue = this.getValue();
18877 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18879 this.inputEl().on("click", this.showTouchView, this);
18880 if (this.triggerEl) {
18881 this.triggerEl.on("click", this.showTouchView, this);
18885 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18886 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18888 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18890 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18891 this.store.on('load', this.onTouchViewLoad, this);
18892 this.store.on('loadexception', this.onTouchViewLoadException, this);
18894 if(this.hiddenName){
18896 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18898 this.hiddenField.dom.value =
18899 this.hiddenValue !== undefined ? this.hiddenValue :
18900 this.value !== undefined ? this.value : '';
18902 this.el.dom.removeAttribute('name');
18903 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18907 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18908 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18911 if(this.removable && !this.multiple){
18912 var close = this.closeTriggerEl();
18914 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18915 close.on('click', this.removeBtnClick, this, close);
18919 * fix the bug in Safari iOS8
18921 this.inputEl().on("focus", function(e){
18922 document.activeElement.blur();
18925 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18932 renderTouchView : function()
18934 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18935 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18937 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18938 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18940 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18941 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18942 this.touchViewBodyEl.setStyle('overflow', 'auto');
18944 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18945 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18947 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18948 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18952 showTouchView : function()
18958 this.touchViewHeaderEl.hide();
18960 if(this.modalTitle.length){
18961 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18962 this.touchViewHeaderEl.show();
18965 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18966 this.touchViewEl.show();
18968 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18970 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18971 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18973 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18975 if(this.modalTitle.length){
18976 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18979 this.touchViewBodyEl.setHeight(bodyHeight);
18983 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18985 this.touchViewEl.addClass(['in','show']);
18988 if(this._touchViewMask){
18989 Roo.get(document.body).addClass("x-body-masked");
18990 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18991 this._touchViewMask.setStyle('z-index', 10000);
18992 this._touchViewMask.addClass('show');
18995 this.doTouchViewQuery();
18999 hideTouchView : function()
19001 this.touchViewEl.removeClass(['in','show']);
19005 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19007 this.touchViewEl.setStyle('display', 'none');
19010 if(this._touchViewMask){
19011 this._touchViewMask.removeClass('show');
19012 Roo.get(document.body).removeClass("x-body-masked");
19016 setTouchViewValue : function()
19023 Roo.each(this.tickItems, function(o){
19028 this.hideTouchView();
19031 doTouchViewQuery : function()
19040 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19044 if(!this.alwaysQuery || this.mode == 'local'){
19045 this.onTouchViewLoad();
19052 onTouchViewBeforeLoad : function(combo,opts)
19058 onTouchViewLoad : function()
19060 if(this.store.getCount() < 1){
19061 this.onTouchViewEmptyResults();
19065 this.clearTouchView();
19067 var rawValue = this.getRawValue();
19069 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19071 this.tickItems = [];
19073 this.store.data.each(function(d, rowIndex){
19074 var row = this.touchViewListGroup.createChild(template);
19076 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19077 row.addClass(d.data.cls);
19080 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19083 html : d.data[this.displayField]
19086 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19087 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19090 row.removeClass('selected');
19091 if(!this.multiple && this.valueField &&
19092 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19095 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19096 row.addClass('selected');
19099 if(this.multiple && this.valueField &&
19100 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19104 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19105 this.tickItems.push(d.data);
19108 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19112 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19114 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19116 if(this.modalTitle.length){
19117 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19120 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19122 if(this.mobile_restrict_height && listHeight < bodyHeight){
19123 this.touchViewBodyEl.setHeight(listHeight);
19128 if(firstChecked && listHeight > bodyHeight){
19129 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19134 onTouchViewLoadException : function()
19136 this.hideTouchView();
19139 onTouchViewEmptyResults : function()
19141 this.clearTouchView();
19143 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19145 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19149 clearTouchView : function()
19151 this.touchViewListGroup.dom.innerHTML = '';
19154 onTouchViewClick : function(e, el, o)
19156 e.preventDefault();
19159 var rowIndex = o.rowIndex;
19161 var r = this.store.getAt(rowIndex);
19163 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19165 if(!this.multiple){
19166 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19167 c.dom.removeAttribute('checked');
19170 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19172 this.setFromData(r.data);
19174 var close = this.closeTriggerEl();
19180 this.hideTouchView();
19182 this.fireEvent('select', this, r, rowIndex);
19187 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19188 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19189 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19193 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19194 this.addItem(r.data);
19195 this.tickItems.push(r.data);
19199 getAutoCreateNativeIOS : function()
19202 cls: 'form-group' //input-group,
19207 cls : 'roo-ios-select'
19211 combobox.name = this.name;
19214 if (this.disabled) {
19215 combobox.disabled = true;
19218 var settings = this;
19220 ['xs','sm','md','lg'].map(function(size){
19221 if (settings[size]) {
19222 cfg.cls += ' col-' + size + '-' + settings[size];
19232 initIOSView : function()
19234 this.store.on('load', this.onIOSViewLoad, this);
19239 onIOSViewLoad : function()
19241 if(this.store.getCount() < 1){
19245 this.clearIOSView();
19247 if(this.allowBlank) {
19249 var default_text = '-- SELECT --';
19251 if(this.placeholder.length){
19252 default_text = this.placeholder;
19255 if(this.emptyTitle.length){
19256 default_text += ' - ' + this.emptyTitle + ' -';
19259 var opt = this.inputEl().createChild({
19262 html : default_text
19266 o[this.valueField] = 0;
19267 o[this.displayField] = default_text;
19269 this.ios_options.push({
19276 this.store.data.each(function(d, rowIndex){
19280 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19281 html = d.data[this.displayField];
19286 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19287 value = d.data[this.valueField];
19296 if(this.value == d.data[this.valueField]){
19297 option['selected'] = true;
19300 var opt = this.inputEl().createChild(option);
19302 this.ios_options.push({
19309 this.inputEl().on('change', function(){
19310 this.fireEvent('select', this);
19315 clearIOSView: function()
19317 this.inputEl().dom.innerHTML = '';
19319 this.ios_options = [];
19322 setIOSValue: function(v)
19326 if(!this.ios_options){
19330 Roo.each(this.ios_options, function(opts){
19332 opts.el.dom.removeAttribute('selected');
19334 if(opts.data[this.valueField] != v){
19338 opts.el.dom.setAttribute('selected', true);
19344 * @cfg {Boolean} grow
19348 * @cfg {Number} growMin
19352 * @cfg {Number} growMax
19361 Roo.apply(Roo.bootstrap.ComboBox, {
19365 cls: 'modal-header',
19387 cls: 'list-group-item',
19391 cls: 'roo-combobox-list-group-item-value'
19395 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19409 listItemCheckbox : {
19411 cls: 'list-group-item',
19415 cls: 'roo-combobox-list-group-item-value'
19419 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19435 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19440 cls: 'modal-footer',
19448 cls: 'col-xs-6 text-left',
19451 cls: 'btn btn-danger roo-touch-view-cancel',
19457 cls: 'col-xs-6 text-right',
19460 cls: 'btn btn-success roo-touch-view-ok',
19471 Roo.apply(Roo.bootstrap.ComboBox, {
19473 touchViewTemplate : {
19475 cls: 'modal fade roo-combobox-touch-view',
19479 cls: 'modal-dialog',
19480 style : 'position:fixed', // we have to fix position....
19484 cls: 'modal-content',
19486 Roo.bootstrap.ComboBox.header,
19487 Roo.bootstrap.ComboBox.body,
19488 Roo.bootstrap.ComboBox.footer
19497 * Ext JS Library 1.1.1
19498 * Copyright(c) 2006-2007, Ext JS, LLC.
19500 * Originally Released Under LGPL - original licence link has changed is not relivant.
19503 * <script type="text/javascript">
19508 * @extends Roo.util.Observable
19509 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19510 * This class also supports single and multi selection modes. <br>
19511 * Create a data model bound view:
19513 var store = new Roo.data.Store(...);
19515 var view = new Roo.View({
19517 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19519 singleSelect: true,
19520 selectedClass: "ydataview-selected",
19524 // listen for node click?
19525 view.on("click", function(vw, index, node, e){
19526 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19530 dataModel.load("foobar.xml");
19532 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19534 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19535 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19537 * Note: old style constructor is still suported (container, template, config)
19540 * Create a new View
19541 * @param {Object} config The config object
19544 Roo.View = function(config, depreciated_tpl, depreciated_config){
19546 this.parent = false;
19548 if (typeof(depreciated_tpl) == 'undefined') {
19549 // new way.. - universal constructor.
19550 Roo.apply(this, config);
19551 this.el = Roo.get(this.el);
19554 this.el = Roo.get(config);
19555 this.tpl = depreciated_tpl;
19556 Roo.apply(this, depreciated_config);
19558 this.wrapEl = this.el.wrap().wrap();
19559 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19562 if(typeof(this.tpl) == "string"){
19563 this.tpl = new Roo.Template(this.tpl);
19565 // support xtype ctors..
19566 this.tpl = new Roo.factory(this.tpl, Roo);
19570 this.tpl.compile();
19575 * @event beforeclick
19576 * Fires before a click is processed. Returns false to cancel the default action.
19577 * @param {Roo.View} this
19578 * @param {Number} index The index of the target node
19579 * @param {HTMLElement} node The target node
19580 * @param {Roo.EventObject} e The raw event object
19582 "beforeclick" : true,
19585 * Fires when a template node is clicked.
19586 * @param {Roo.View} this
19587 * @param {Number} index The index of the target node
19588 * @param {HTMLElement} node The target node
19589 * @param {Roo.EventObject} e The raw event object
19594 * Fires when a template node is double clicked.
19595 * @param {Roo.View} this
19596 * @param {Number} index The index of the target node
19597 * @param {HTMLElement} node The target node
19598 * @param {Roo.EventObject} e The raw event object
19602 * @event contextmenu
19603 * Fires when a template node is right clicked.
19604 * @param {Roo.View} this
19605 * @param {Number} index The index of the target node
19606 * @param {HTMLElement} node The target node
19607 * @param {Roo.EventObject} e The raw event object
19609 "contextmenu" : true,
19611 * @event selectionchange
19612 * Fires when the selected nodes change.
19613 * @param {Roo.View} this
19614 * @param {Array} selections Array of the selected nodes
19616 "selectionchange" : true,
19619 * @event beforeselect
19620 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19621 * @param {Roo.View} this
19622 * @param {HTMLElement} node The node to be selected
19623 * @param {Array} selections Array of currently selected nodes
19625 "beforeselect" : true,
19627 * @event preparedata
19628 * Fires on every row to render, to allow you to change the data.
19629 * @param {Roo.View} this
19630 * @param {Object} data to be rendered (change this)
19632 "preparedata" : true
19640 "click": this.onClick,
19641 "dblclick": this.onDblClick,
19642 "contextmenu": this.onContextMenu,
19646 this.selections = [];
19648 this.cmp = new Roo.CompositeElementLite([]);
19650 this.store = Roo.factory(this.store, Roo.data);
19651 this.setStore(this.store, true);
19654 if ( this.footer && this.footer.xtype) {
19656 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19658 this.footer.dataSource = this.store;
19659 this.footer.container = fctr;
19660 this.footer = Roo.factory(this.footer, Roo);
19661 fctr.insertFirst(this.el);
19663 // this is a bit insane - as the paging toolbar seems to detach the el..
19664 // dom.parentNode.parentNode.parentNode
19665 // they get detached?
19669 Roo.View.superclass.constructor.call(this);
19674 Roo.extend(Roo.View, Roo.util.Observable, {
19677 * @cfg {Roo.data.Store} store Data store to load data from.
19682 * @cfg {String|Roo.Element} el The container element.
19687 * @cfg {String|Roo.Template} tpl The template used by this View
19691 * @cfg {String} dataName the named area of the template to use as the data area
19692 * Works with domtemplates roo-name="name"
19696 * @cfg {String} selectedClass The css class to add to selected nodes
19698 selectedClass : "x-view-selected",
19700 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19705 * @cfg {String} text to display on mask (default Loading)
19709 * @cfg {Boolean} multiSelect Allow multiple selection
19711 multiSelect : false,
19713 * @cfg {Boolean} singleSelect Allow single selection
19715 singleSelect: false,
19718 * @cfg {Boolean} toggleSelect - selecting
19720 toggleSelect : false,
19723 * @cfg {Boolean} tickable - selecting
19728 * Returns the element this view is bound to.
19729 * @return {Roo.Element}
19731 getEl : function(){
19732 return this.wrapEl;
19738 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19740 refresh : function(){
19741 //Roo.log('refresh');
19744 // if we are using something like 'domtemplate', then
19745 // the what gets used is:
19746 // t.applySubtemplate(NAME, data, wrapping data..)
19747 // the outer template then get' applied with
19748 // the store 'extra data'
19749 // and the body get's added to the
19750 // roo-name="data" node?
19751 // <span class='roo-tpl-{name}'></span> ?????
19755 this.clearSelections();
19756 this.el.update("");
19758 var records = this.store.getRange();
19759 if(records.length < 1) {
19761 // is this valid?? = should it render a template??
19763 this.el.update(this.emptyText);
19767 if (this.dataName) {
19768 this.el.update(t.apply(this.store.meta)); //????
19769 el = this.el.child('.roo-tpl-' + this.dataName);
19772 for(var i = 0, len = records.length; i < len; i++){
19773 var data = this.prepareData(records[i].data, i, records[i]);
19774 this.fireEvent("preparedata", this, data, i, records[i]);
19776 var d = Roo.apply({}, data);
19779 Roo.apply(d, {'roo-id' : Roo.id()});
19783 Roo.each(this.parent.item, function(item){
19784 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19787 Roo.apply(d, {'roo-data-checked' : 'checked'});
19791 html[html.length] = Roo.util.Format.trim(
19793 t.applySubtemplate(this.dataName, d, this.store.meta) :
19800 el.update(html.join(""));
19801 this.nodes = el.dom.childNodes;
19802 this.updateIndexes(0);
19807 * Function to override to reformat the data that is sent to
19808 * the template for each node.
19809 * DEPRICATED - use the preparedata event handler.
19810 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19811 * a JSON object for an UpdateManager bound view).
19813 prepareData : function(data, index, record)
19815 this.fireEvent("preparedata", this, data, index, record);
19819 onUpdate : function(ds, record){
19820 // Roo.log('on update');
19821 this.clearSelections();
19822 var index = this.store.indexOf(record);
19823 var n = this.nodes[index];
19824 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19825 n.parentNode.removeChild(n);
19826 this.updateIndexes(index, index);
19832 onAdd : function(ds, records, index)
19834 //Roo.log(['on Add', ds, records, index] );
19835 this.clearSelections();
19836 if(this.nodes.length == 0){
19840 var n = this.nodes[index];
19841 for(var i = 0, len = records.length; i < len; i++){
19842 var d = this.prepareData(records[i].data, i, records[i]);
19844 this.tpl.insertBefore(n, d);
19847 this.tpl.append(this.el, d);
19850 this.updateIndexes(index);
19853 onRemove : function(ds, record, index){
19854 // Roo.log('onRemove');
19855 this.clearSelections();
19856 var el = this.dataName ?
19857 this.el.child('.roo-tpl-' + this.dataName) :
19860 el.dom.removeChild(this.nodes[index]);
19861 this.updateIndexes(index);
19865 * Refresh an individual node.
19866 * @param {Number} index
19868 refreshNode : function(index){
19869 this.onUpdate(this.store, this.store.getAt(index));
19872 updateIndexes : function(startIndex, endIndex){
19873 var ns = this.nodes;
19874 startIndex = startIndex || 0;
19875 endIndex = endIndex || ns.length - 1;
19876 for(var i = startIndex; i <= endIndex; i++){
19877 ns[i].nodeIndex = i;
19882 * Changes the data store this view uses and refresh the view.
19883 * @param {Store} store
19885 setStore : function(store, initial){
19886 if(!initial && this.store){
19887 this.store.un("datachanged", this.refresh);
19888 this.store.un("add", this.onAdd);
19889 this.store.un("remove", this.onRemove);
19890 this.store.un("update", this.onUpdate);
19891 this.store.un("clear", this.refresh);
19892 this.store.un("beforeload", this.onBeforeLoad);
19893 this.store.un("load", this.onLoad);
19894 this.store.un("loadexception", this.onLoad);
19898 store.on("datachanged", this.refresh, this);
19899 store.on("add", this.onAdd, this);
19900 store.on("remove", this.onRemove, this);
19901 store.on("update", this.onUpdate, this);
19902 store.on("clear", this.refresh, this);
19903 store.on("beforeload", this.onBeforeLoad, this);
19904 store.on("load", this.onLoad, this);
19905 store.on("loadexception", this.onLoad, this);
19913 * onbeforeLoad - masks the loading area.
19916 onBeforeLoad : function(store,opts)
19918 //Roo.log('onBeforeLoad');
19920 this.el.update("");
19922 this.el.mask(this.mask ? this.mask : "Loading" );
19924 onLoad : function ()
19931 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19932 * @param {HTMLElement} node
19933 * @return {HTMLElement} The template node
19935 findItemFromChild : function(node){
19936 var el = this.dataName ?
19937 this.el.child('.roo-tpl-' + this.dataName,true) :
19940 if(!node || node.parentNode == el){
19943 var p = node.parentNode;
19944 while(p && p != el){
19945 if(p.parentNode == el){
19954 onClick : function(e){
19955 var item = this.findItemFromChild(e.getTarget());
19957 var index = this.indexOf(item);
19958 if(this.onItemClick(item, index, e) !== false){
19959 this.fireEvent("click", this, index, item, e);
19962 this.clearSelections();
19967 onContextMenu : function(e){
19968 var item = this.findItemFromChild(e.getTarget());
19970 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19975 onDblClick : function(e){
19976 var item = this.findItemFromChild(e.getTarget());
19978 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19982 onItemClick : function(item, index, e)
19984 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19987 if (this.toggleSelect) {
19988 var m = this.isSelected(item) ? 'unselect' : 'select';
19991 _t[m](item, true, false);
19994 if(this.multiSelect || this.singleSelect){
19995 if(this.multiSelect && e.shiftKey && this.lastSelection){
19996 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19998 this.select(item, this.multiSelect && e.ctrlKey);
19999 this.lastSelection = item;
20002 if(!this.tickable){
20003 e.preventDefault();
20011 * Get the number of selected nodes.
20014 getSelectionCount : function(){
20015 return this.selections.length;
20019 * Get the currently selected nodes.
20020 * @return {Array} An array of HTMLElements
20022 getSelectedNodes : function(){
20023 return this.selections;
20027 * Get the indexes of the selected nodes.
20030 getSelectedIndexes : function(){
20031 var indexes = [], s = this.selections;
20032 for(var i = 0, len = s.length; i < len; i++){
20033 indexes.push(s[i].nodeIndex);
20039 * Clear all selections
20040 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20042 clearSelections : function(suppressEvent){
20043 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20044 this.cmp.elements = this.selections;
20045 this.cmp.removeClass(this.selectedClass);
20046 this.selections = [];
20047 if(!suppressEvent){
20048 this.fireEvent("selectionchange", this, this.selections);
20054 * Returns true if the passed node is selected
20055 * @param {HTMLElement/Number} node The node or node index
20056 * @return {Boolean}
20058 isSelected : function(node){
20059 var s = this.selections;
20063 node = this.getNode(node);
20064 return s.indexOf(node) !== -1;
20069 * @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
20070 * @param {Boolean} keepExisting (optional) true to keep existing selections
20071 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20073 select : function(nodeInfo, keepExisting, suppressEvent){
20074 if(nodeInfo instanceof Array){
20076 this.clearSelections(true);
20078 for(var i = 0, len = nodeInfo.length; i < len; i++){
20079 this.select(nodeInfo[i], true, true);
20083 var node = this.getNode(nodeInfo);
20084 if(!node || this.isSelected(node)){
20085 return; // already selected.
20088 this.clearSelections(true);
20091 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20092 Roo.fly(node).addClass(this.selectedClass);
20093 this.selections.push(node);
20094 if(!suppressEvent){
20095 this.fireEvent("selectionchange", this, this.selections);
20103 * @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
20104 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20105 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20107 unselect : function(nodeInfo, keepExisting, suppressEvent)
20109 if(nodeInfo instanceof Array){
20110 Roo.each(this.selections, function(s) {
20111 this.unselect(s, nodeInfo);
20115 var node = this.getNode(nodeInfo);
20116 if(!node || !this.isSelected(node)){
20117 //Roo.log("not selected");
20118 return; // not selected.
20122 Roo.each(this.selections, function(s) {
20124 Roo.fly(node).removeClass(this.selectedClass);
20131 this.selections= ns;
20132 this.fireEvent("selectionchange", this, this.selections);
20136 * Gets a template node.
20137 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20138 * @return {HTMLElement} The node or null if it wasn't found
20140 getNode : function(nodeInfo){
20141 if(typeof nodeInfo == "string"){
20142 return document.getElementById(nodeInfo);
20143 }else if(typeof nodeInfo == "number"){
20144 return this.nodes[nodeInfo];
20150 * Gets a range template nodes.
20151 * @param {Number} startIndex
20152 * @param {Number} endIndex
20153 * @return {Array} An array of nodes
20155 getNodes : function(start, end){
20156 var ns = this.nodes;
20157 start = start || 0;
20158 end = typeof end == "undefined" ? ns.length - 1 : end;
20161 for(var i = start; i <= end; i++){
20165 for(var i = start; i >= end; i--){
20173 * Finds the index of the passed node
20174 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20175 * @return {Number} The index of the node or -1
20177 indexOf : function(node){
20178 node = this.getNode(node);
20179 if(typeof node.nodeIndex == "number"){
20180 return node.nodeIndex;
20182 var ns = this.nodes;
20183 for(var i = 0, len = ns.length; i < len; i++){
20194 * based on jquery fullcalendar
20198 Roo.bootstrap = Roo.bootstrap || {};
20200 * @class Roo.bootstrap.Calendar
20201 * @extends Roo.bootstrap.Component
20202 * Bootstrap Calendar class
20203 * @cfg {Boolean} loadMask (true|false) default false
20204 * @cfg {Object} header generate the user specific header of the calendar, default false
20207 * Create a new Container
20208 * @param {Object} config The config object
20213 Roo.bootstrap.Calendar = function(config){
20214 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20218 * Fires when a date is selected
20219 * @param {DatePicker} this
20220 * @param {Date} date The selected date
20224 * @event monthchange
20225 * Fires when the displayed month changes
20226 * @param {DatePicker} this
20227 * @param {Date} date The selected month
20229 'monthchange': true,
20231 * @event evententer
20232 * Fires when mouse over an event
20233 * @param {Calendar} this
20234 * @param {event} Event
20236 'evententer': true,
20238 * @event eventleave
20239 * Fires when the mouse leaves an
20240 * @param {Calendar} this
20243 'eventleave': true,
20245 * @event eventclick
20246 * Fires when the mouse click an
20247 * @param {Calendar} this
20256 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20259 * @cfg {Roo.data.Store} store
20260 * The data source for the calendar
20264 * @cfg {Number} startDay
20265 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20273 getAutoCreate : function(){
20276 var fc_button = function(name, corner, style, content ) {
20277 return Roo.apply({},{
20279 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20281 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20284 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20295 style : 'width:100%',
20302 cls : 'fc-header-left',
20304 fc_button('prev', 'left', 'arrow', '‹' ),
20305 fc_button('next', 'right', 'arrow', '›' ),
20306 { tag: 'span', cls: 'fc-header-space' },
20307 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20315 cls : 'fc-header-center',
20319 cls: 'fc-header-title',
20322 html : 'month / year'
20330 cls : 'fc-header-right',
20332 /* fc_button('month', 'left', '', 'month' ),
20333 fc_button('week', '', '', 'week' ),
20334 fc_button('day', 'right', '', 'day' )
20346 header = this.header;
20349 var cal_heads = function() {
20351 // fixme - handle this.
20353 for (var i =0; i < Date.dayNames.length; i++) {
20354 var d = Date.dayNames[i];
20357 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20358 html : d.substring(0,3)
20362 ret[0].cls += ' fc-first';
20363 ret[6].cls += ' fc-last';
20366 var cal_cell = function(n) {
20369 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20374 cls: 'fc-day-number',
20378 cls: 'fc-day-content',
20382 style: 'position: relative;' // height: 17px;
20394 var cal_rows = function() {
20397 for (var r = 0; r < 6; r++) {
20404 for (var i =0; i < Date.dayNames.length; i++) {
20405 var d = Date.dayNames[i];
20406 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20409 row.cn[0].cls+=' fc-first';
20410 row.cn[0].cn[0].style = 'min-height:90px';
20411 row.cn[6].cls+=' fc-last';
20415 ret[0].cls += ' fc-first';
20416 ret[4].cls += ' fc-prev-last';
20417 ret[5].cls += ' fc-last';
20424 cls: 'fc-border-separate',
20425 style : 'width:100%',
20433 cls : 'fc-first fc-last',
20451 cls : 'fc-content',
20452 style : "position: relative;",
20455 cls : 'fc-view fc-view-month fc-grid',
20456 style : 'position: relative',
20457 unselectable : 'on',
20460 cls : 'fc-event-container',
20461 style : 'position:absolute;z-index:8;top:0;left:0;'
20479 initEvents : function()
20482 throw "can not find store for calendar";
20488 style: "text-align:center",
20492 style: "background-color:white;width:50%;margin:250 auto",
20496 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20507 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20509 var size = this.el.select('.fc-content', true).first().getSize();
20510 this.maskEl.setSize(size.width, size.height);
20511 this.maskEl.enableDisplayMode("block");
20512 if(!this.loadMask){
20513 this.maskEl.hide();
20516 this.store = Roo.factory(this.store, Roo.data);
20517 this.store.on('load', this.onLoad, this);
20518 this.store.on('beforeload', this.onBeforeLoad, this);
20522 this.cells = this.el.select('.fc-day',true);
20523 //Roo.log(this.cells);
20524 this.textNodes = this.el.query('.fc-day-number');
20525 this.cells.addClassOnOver('fc-state-hover');
20527 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20528 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20529 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20530 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20532 this.on('monthchange', this.onMonthChange, this);
20534 this.update(new Date().clearTime());
20537 resize : function() {
20538 var sz = this.el.getSize();
20540 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20541 this.el.select('.fc-day-content div',true).setHeight(34);
20546 showPrevMonth : function(e){
20547 this.update(this.activeDate.add("mo", -1));
20549 showToday : function(e){
20550 this.update(new Date().clearTime());
20553 showNextMonth : function(e){
20554 this.update(this.activeDate.add("mo", 1));
20558 showPrevYear : function(){
20559 this.update(this.activeDate.add("y", -1));
20563 showNextYear : function(){
20564 this.update(this.activeDate.add("y", 1));
20569 update : function(date)
20571 var vd = this.activeDate;
20572 this.activeDate = date;
20573 // if(vd && this.el){
20574 // var t = date.getTime();
20575 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20576 // Roo.log('using add remove');
20578 // this.fireEvent('monthchange', this, date);
20580 // this.cells.removeClass("fc-state-highlight");
20581 // this.cells.each(function(c){
20582 // if(c.dateValue == t){
20583 // c.addClass("fc-state-highlight");
20584 // setTimeout(function(){
20585 // try{c.dom.firstChild.focus();}catch(e){}
20595 var days = date.getDaysInMonth();
20597 var firstOfMonth = date.getFirstDateOfMonth();
20598 var startingPos = firstOfMonth.getDay()-this.startDay;
20600 if(startingPos < this.startDay){
20604 var pm = date.add(Date.MONTH, -1);
20605 var prevStart = pm.getDaysInMonth()-startingPos;
20607 this.cells = this.el.select('.fc-day',true);
20608 this.textNodes = this.el.query('.fc-day-number');
20609 this.cells.addClassOnOver('fc-state-hover');
20611 var cells = this.cells.elements;
20612 var textEls = this.textNodes;
20614 Roo.each(cells, function(cell){
20615 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20618 days += startingPos;
20620 // convert everything to numbers so it's fast
20621 var day = 86400000;
20622 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20625 //Roo.log(prevStart);
20627 var today = new Date().clearTime().getTime();
20628 var sel = date.clearTime().getTime();
20629 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20630 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20631 var ddMatch = this.disabledDatesRE;
20632 var ddText = this.disabledDatesText;
20633 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20634 var ddaysText = this.disabledDaysText;
20635 var format = this.format;
20637 var setCellClass = function(cal, cell){
20641 //Roo.log('set Cell Class');
20643 var t = d.getTime();
20647 cell.dateValue = t;
20649 cell.className += " fc-today";
20650 cell.className += " fc-state-highlight";
20651 cell.title = cal.todayText;
20654 // disable highlight in other month..
20655 //cell.className += " fc-state-highlight";
20660 cell.className = " fc-state-disabled";
20661 cell.title = cal.minText;
20665 cell.className = " fc-state-disabled";
20666 cell.title = cal.maxText;
20670 if(ddays.indexOf(d.getDay()) != -1){
20671 cell.title = ddaysText;
20672 cell.className = " fc-state-disabled";
20675 if(ddMatch && format){
20676 var fvalue = d.dateFormat(format);
20677 if(ddMatch.test(fvalue)){
20678 cell.title = ddText.replace("%0", fvalue);
20679 cell.className = " fc-state-disabled";
20683 if (!cell.initialClassName) {
20684 cell.initialClassName = cell.dom.className;
20687 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20692 for(; i < startingPos; i++) {
20693 textEls[i].innerHTML = (++prevStart);
20694 d.setDate(d.getDate()+1);
20696 cells[i].className = "fc-past fc-other-month";
20697 setCellClass(this, cells[i]);
20702 for(; i < days; i++){
20703 intDay = i - startingPos + 1;
20704 textEls[i].innerHTML = (intDay);
20705 d.setDate(d.getDate()+1);
20707 cells[i].className = ''; // "x-date-active";
20708 setCellClass(this, cells[i]);
20712 for(; i < 42; i++) {
20713 textEls[i].innerHTML = (++extraDays);
20714 d.setDate(d.getDate()+1);
20716 cells[i].className = "fc-future fc-other-month";
20717 setCellClass(this, cells[i]);
20720 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20722 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20724 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20725 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20727 if(totalRows != 6){
20728 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20729 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20732 this.fireEvent('monthchange', this, date);
20736 if(!this.internalRender){
20737 var main = this.el.dom.firstChild;
20738 var w = main.offsetWidth;
20739 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20740 Roo.fly(main).setWidth(w);
20741 this.internalRender = true;
20742 // opera does not respect the auto grow header center column
20743 // then, after it gets a width opera refuses to recalculate
20744 // without a second pass
20745 if(Roo.isOpera && !this.secondPass){
20746 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20747 this.secondPass = true;
20748 this.update.defer(10, this, [date]);
20755 findCell : function(dt) {
20756 dt = dt.clearTime().getTime();
20758 this.cells.each(function(c){
20759 //Roo.log("check " +c.dateValue + '?=' + dt);
20760 if(c.dateValue == dt){
20770 findCells : function(ev) {
20771 var s = ev.start.clone().clearTime().getTime();
20773 var e= ev.end.clone().clearTime().getTime();
20776 this.cells.each(function(c){
20777 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20779 if(c.dateValue > e){
20782 if(c.dateValue < s){
20791 // findBestRow: function(cells)
20795 // for (var i =0 ; i < cells.length;i++) {
20796 // ret = Math.max(cells[i].rows || 0,ret);
20803 addItem : function(ev)
20805 // look for vertical location slot in
20806 var cells = this.findCells(ev);
20808 // ev.row = this.findBestRow(cells);
20810 // work out the location.
20814 for(var i =0; i < cells.length; i++) {
20816 cells[i].row = cells[0].row;
20819 cells[i].row = cells[i].row + 1;
20829 if (crow.start.getY() == cells[i].getY()) {
20831 crow.end = cells[i];
20848 cells[0].events.push(ev);
20850 this.calevents.push(ev);
20853 clearEvents: function() {
20855 if(!this.calevents){
20859 Roo.each(this.cells.elements, function(c){
20865 Roo.each(this.calevents, function(e) {
20866 Roo.each(e.els, function(el) {
20867 el.un('mouseenter' ,this.onEventEnter, this);
20868 el.un('mouseleave' ,this.onEventLeave, this);
20873 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20879 renderEvents: function()
20883 this.cells.each(function(c) {
20892 if(c.row != c.events.length){
20893 r = 4 - (4 - (c.row - c.events.length));
20896 c.events = ev.slice(0, r);
20897 c.more = ev.slice(r);
20899 if(c.more.length && c.more.length == 1){
20900 c.events.push(c.more.pop());
20903 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20907 this.cells.each(function(c) {
20909 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20912 for (var e = 0; e < c.events.length; e++){
20913 var ev = c.events[e];
20914 var rows = ev.rows;
20916 for(var i = 0; i < rows.length; i++) {
20918 // how many rows should it span..
20921 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20922 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20924 unselectable : "on",
20927 cls: 'fc-event-inner',
20931 // cls: 'fc-event-time',
20932 // html : cells.length > 1 ? '' : ev.time
20936 cls: 'fc-event-title',
20937 html : String.format('{0}', ev.title)
20944 cls: 'ui-resizable-handle ui-resizable-e',
20945 html : '  '
20952 cfg.cls += ' fc-event-start';
20954 if ((i+1) == rows.length) {
20955 cfg.cls += ' fc-event-end';
20958 var ctr = _this.el.select('.fc-event-container',true).first();
20959 var cg = ctr.createChild(cfg);
20961 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20962 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20964 var r = (c.more.length) ? 1 : 0;
20965 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20966 cg.setWidth(ebox.right - sbox.x -2);
20968 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20969 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20970 cg.on('click', _this.onEventClick, _this, ev);
20981 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20982 style : 'position: absolute',
20983 unselectable : "on",
20986 cls: 'fc-event-inner',
20990 cls: 'fc-event-title',
20998 cls: 'ui-resizable-handle ui-resizable-e',
20999 html : '  '
21005 var ctr = _this.el.select('.fc-event-container',true).first();
21006 var cg = ctr.createChild(cfg);
21008 var sbox = c.select('.fc-day-content',true).first().getBox();
21009 var ebox = c.select('.fc-day-content',true).first().getBox();
21011 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21012 cg.setWidth(ebox.right - sbox.x -2);
21014 cg.on('click', _this.onMoreEventClick, _this, c.more);
21024 onEventEnter: function (e, el,event,d) {
21025 this.fireEvent('evententer', this, el, event);
21028 onEventLeave: function (e, el,event,d) {
21029 this.fireEvent('eventleave', this, el, event);
21032 onEventClick: function (e, el,event,d) {
21033 this.fireEvent('eventclick', this, el, event);
21036 onMonthChange: function () {
21040 onMoreEventClick: function(e, el, more)
21044 this.calpopover.placement = 'right';
21045 this.calpopover.setTitle('More');
21047 this.calpopover.setContent('');
21049 var ctr = this.calpopover.el.select('.popover-content', true).first();
21051 Roo.each(more, function(m){
21053 cls : 'fc-event-hori fc-event-draggable',
21056 var cg = ctr.createChild(cfg);
21058 cg.on('click', _this.onEventClick, _this, m);
21061 this.calpopover.show(el);
21066 onLoad: function ()
21068 this.calevents = [];
21071 if(this.store.getCount() > 0){
21072 this.store.data.each(function(d){
21075 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21076 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21077 time : d.data.start_time,
21078 title : d.data.title,
21079 description : d.data.description,
21080 venue : d.data.venue
21085 this.renderEvents();
21087 if(this.calevents.length && this.loadMask){
21088 this.maskEl.hide();
21092 onBeforeLoad: function()
21094 this.clearEvents();
21096 this.maskEl.show();
21110 * @class Roo.bootstrap.Popover
21111 * @extends Roo.bootstrap.Component
21113 * @children Roo.bootstrap.Component
21114 * Bootstrap Popover class
21115 * @cfg {String} html contents of the popover (or false to use children..)
21116 * @cfg {String} title of popover (or false to hide)
21117 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21118 * @cfg {String} trigger click || hover (or false to trigger manually)
21119 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21120 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21121 * - if false and it has a 'parent' then it will be automatically added to that element
21122 * - if string - Roo.get will be called
21123 * @cfg {Number} delay - delay before showing
21126 * Create a new Popover
21127 * @param {Object} config The config object
21130 Roo.bootstrap.Popover = function(config){
21131 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21137 * After the popover show
21139 * @param {Roo.bootstrap.Popover} this
21144 * After the popover hide
21146 * @param {Roo.bootstrap.Popover} this
21152 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21157 placement : 'right',
21158 trigger : 'hover', // hover
21164 can_build_overlaid : false,
21166 maskEl : false, // the mask element
21169 alignEl : false, // when show is called with an element - this get's stored.
21171 getChildContainer : function()
21173 return this.contentEl;
21176 getPopoverHeader : function()
21178 this.title = true; // flag not to hide it..
21179 this.headerEl.addClass('p-0');
21180 return this.headerEl
21184 getAutoCreate : function(){
21187 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21188 style: 'display:block',
21194 cls : 'popover-inner ',
21198 cls: 'popover-title popover-header',
21199 html : this.title === false ? '' : this.title
21202 cls : 'popover-content popover-body ' + (this.cls || ''),
21203 html : this.html || ''
21214 * @param {string} the title
21216 setTitle: function(str)
21220 this.headerEl.dom.innerHTML = str;
21225 * @param {string} the body content
21227 setContent: function(str)
21230 if (this.contentEl) {
21231 this.contentEl.dom.innerHTML = str;
21235 // as it get's added to the bottom of the page.
21236 onRender : function(ct, position)
21238 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21243 var cfg = Roo.apply({}, this.getAutoCreate());
21247 cfg.cls += ' ' + this.cls;
21250 cfg.style = this.style;
21252 //Roo.log("adding to ");
21253 this.el = Roo.get(document.body).createChild(cfg, position);
21254 // Roo.log(this.el);
21257 this.contentEl = this.el.select('.popover-content',true).first();
21258 this.headerEl = this.el.select('.popover-title',true).first();
21261 if(typeof(this.items) != 'undefined'){
21262 var items = this.items;
21265 for(var i =0;i < items.length;i++) {
21266 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21270 this.items = nitems;
21272 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21273 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21280 resizeMask : function()
21282 this.maskEl.setSize(
21283 Roo.lib.Dom.getViewWidth(true),
21284 Roo.lib.Dom.getViewHeight(true)
21288 initEvents : function()
21292 Roo.bootstrap.Popover.register(this);
21295 this.arrowEl = this.el.select('.arrow',true).first();
21296 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21297 this.el.enableDisplayMode('block');
21301 if (this.over === false && !this.parent()) {
21304 if (this.triggers === false) {
21309 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21310 var triggers = this.trigger ? this.trigger.split(' ') : [];
21311 Roo.each(triggers, function(trigger) {
21313 if (trigger == 'click') {
21314 on_el.on('click', this.toggle, this);
21315 } else if (trigger != 'manual') {
21316 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21317 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21319 on_el.on(eventIn ,this.enter, this);
21320 on_el.on(eventOut, this.leave, this);
21330 toggle : function () {
21331 this.hoverState == 'in' ? this.leave() : this.enter();
21334 enter : function () {
21336 clearTimeout(this.timeout);
21338 this.hoverState = 'in';
21340 if (!this.delay || !this.delay.show) {
21345 this.timeout = setTimeout(function () {
21346 if (_t.hoverState == 'in') {
21349 }, this.delay.show)
21352 leave : function() {
21353 clearTimeout(this.timeout);
21355 this.hoverState = 'out';
21357 if (!this.delay || !this.delay.hide) {
21362 this.timeout = setTimeout(function () {
21363 if (_t.hoverState == 'out') {
21366 }, this.delay.hide)
21370 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21371 * @param {string} (left|right|top|bottom) position
21373 show : function (on_el, placement)
21375 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21376 on_el = on_el || false; // default to false
21379 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21380 on_el = this.parent().el;
21381 } else if (this.over) {
21382 on_el = Roo.get(this.over);
21387 this.alignEl = Roo.get( on_el );
21390 this.render(document.body);
21396 if (this.title === false) {
21397 this.headerEl.hide();
21402 this.el.dom.style.display = 'block';
21405 if (this.alignEl) {
21406 this.updatePosition(this.placement, true);
21409 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21410 var es = this.el.getSize();
21411 var x = Roo.lib.Dom.getViewWidth()/2;
21412 var y = Roo.lib.Dom.getViewHeight()/2;
21413 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21418 //var arrow = this.el.select('.arrow',true).first();
21419 //arrow.set(align[2],
21421 this.el.addClass('in');
21425 this.hoverState = 'in';
21428 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21429 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21430 this.maskEl.dom.style.display = 'block';
21431 this.maskEl.addClass('show');
21433 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21435 this.fireEvent('show', this);
21439 * fire this manually after loading a grid in the table for example
21440 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21441 * @param {Boolean} try and move it if we cant get right position.
21443 updatePosition : function(placement, try_move)
21445 // allow for calling with no parameters
21446 placement = placement ? placement : this.placement;
21447 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21449 this.el.removeClass([
21450 'fade','top','bottom', 'left', 'right','in',
21451 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21453 this.el.addClass(placement + ' bs-popover-' + placement);
21455 if (!this.alignEl ) {
21459 switch (placement) {
21461 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21462 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21463 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21464 //normal display... or moved up/down.
21465 this.el.setXY(offset);
21466 var xy = this.alignEl.getAnchorXY('tr', false);
21468 this.arrowEl.setXY(xy);
21471 // continue through...
21472 return this.updatePosition('left', false);
21476 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21477 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21478 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21479 //normal display... or moved up/down.
21480 this.el.setXY(offset);
21481 var xy = this.alignEl.getAnchorXY('tl', false);
21482 xy[0]-=10;xy[1]+=5; // << fix me
21483 this.arrowEl.setXY(xy);
21487 return this.updatePosition('right', false);
21490 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21491 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21492 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21493 //normal display... or moved up/down.
21494 this.el.setXY(offset);
21495 var xy = this.alignEl.getAnchorXY('t', false);
21496 xy[1]-=10; // << fix me
21497 this.arrowEl.setXY(xy);
21501 return this.updatePosition('bottom', false);
21504 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21505 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21506 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21507 //normal display... or moved up/down.
21508 this.el.setXY(offset);
21509 var xy = this.alignEl.getAnchorXY('b', false);
21510 xy[1]+=2; // << fix me
21511 this.arrowEl.setXY(xy);
21515 return this.updatePosition('top', false);
21526 this.el.setXY([0,0]);
21527 this.el.removeClass('in');
21529 this.hoverState = null;
21530 this.maskEl.hide(); // always..
21531 this.fireEvent('hide', this);
21537 Roo.apply(Roo.bootstrap.Popover, {
21540 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21541 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21542 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21543 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21548 clickHander : false,
21552 onMouseDown : function(e)
21554 if (this.popups.length && !e.getTarget(".roo-popover")) {
21555 /// what is nothing is showing..
21564 register : function(popup)
21566 if (!Roo.bootstrap.Popover.clickHandler) {
21567 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21569 // hide other popups.
21570 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21571 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21572 this.hideAll(); //<< why?
21573 //this.popups.push(popup);
21575 hideAll : function()
21577 this.popups.forEach(function(p) {
21581 onShow : function() {
21582 Roo.bootstrap.Popover.popups.push(this);
21584 onHide : function() {
21585 Roo.bootstrap.Popover.popups.remove(this);
21591 * Card header - holder for the card header elements.
21596 * @class Roo.bootstrap.PopoverNav
21597 * @extends Roo.bootstrap.NavGroup
21598 * Bootstrap Popover header navigation class
21600 * Create a new Popover Header Navigation
21601 * @param {Object} config The config object
21604 Roo.bootstrap.PopoverNav = function(config){
21605 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21608 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21611 container_method : 'getPopoverHeader'
21629 * @class Roo.bootstrap.Progress
21630 * @extends Roo.bootstrap.Component
21631 * @children Roo.bootstrap.ProgressBar
21632 * Bootstrap Progress class
21633 * @cfg {Boolean} striped striped of the progress bar
21634 * @cfg {Boolean} active animated of the progress bar
21638 * Create a new Progress
21639 * @param {Object} config The config object
21642 Roo.bootstrap.Progress = function(config){
21643 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21646 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21651 getAutoCreate : function(){
21659 cfg.cls += ' progress-striped';
21663 cfg.cls += ' active';
21682 * @class Roo.bootstrap.ProgressBar
21683 * @extends Roo.bootstrap.Component
21684 * Bootstrap ProgressBar class
21685 * @cfg {Number} aria_valuenow aria-value now
21686 * @cfg {Number} aria_valuemin aria-value min
21687 * @cfg {Number} aria_valuemax aria-value max
21688 * @cfg {String} label label for the progress bar
21689 * @cfg {String} panel (success | info | warning | danger )
21690 * @cfg {String} role role of the progress bar
21691 * @cfg {String} sr_only text
21695 * Create a new ProgressBar
21696 * @param {Object} config The config object
21699 Roo.bootstrap.ProgressBar = function(config){
21700 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21703 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21707 aria_valuemax : 100,
21713 getAutoCreate : function()
21718 cls: 'progress-bar',
21719 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21731 cfg.role = this.role;
21734 if(this.aria_valuenow){
21735 cfg['aria-valuenow'] = this.aria_valuenow;
21738 if(this.aria_valuemin){
21739 cfg['aria-valuemin'] = this.aria_valuemin;
21742 if(this.aria_valuemax){
21743 cfg['aria-valuemax'] = this.aria_valuemax;
21746 if(this.label && !this.sr_only){
21747 cfg.html = this.label;
21751 cfg.cls += ' progress-bar-' + this.panel;
21757 update : function(aria_valuenow)
21759 this.aria_valuenow = aria_valuenow;
21761 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21769 * @class Roo.bootstrap.TabGroup
21770 * @extends Roo.bootstrap.Column
21771 * @children Roo.bootstrap.TabPanel
21772 * Bootstrap Column class
21773 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21774 * @cfg {Boolean} carousel true to make the group behave like a carousel
21775 * @cfg {Boolean} bullets show bullets for the panels
21776 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21777 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21778 * @cfg {Boolean} showarrow (true|false) show arrow default true
21781 * Create a new TabGroup
21782 * @param {Object} config The config object
21785 Roo.bootstrap.TabGroup = function(config){
21786 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21788 this.navId = Roo.id();
21791 Roo.bootstrap.TabGroup.register(this);
21795 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21798 transition : false,
21803 slideOnTouch : false,
21806 getAutoCreate : function()
21808 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21810 cfg.cls += ' tab-content';
21812 if (this.carousel) {
21813 cfg.cls += ' carousel slide';
21816 cls : 'carousel-inner',
21820 if(this.bullets && !Roo.isTouch){
21823 cls : 'carousel-bullets',
21827 if(this.bullets_cls){
21828 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21835 cfg.cn[0].cn.push(bullets);
21838 if(this.showarrow){
21839 cfg.cn[0].cn.push({
21841 class : 'carousel-arrow',
21845 class : 'carousel-prev',
21849 class : 'fa fa-chevron-left'
21855 class : 'carousel-next',
21859 class : 'fa fa-chevron-right'
21872 initEvents: function()
21874 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21875 // this.el.on("touchstart", this.onTouchStart, this);
21878 if(this.autoslide){
21881 this.slideFn = window.setInterval(function() {
21882 _this.showPanelNext();
21886 if(this.showarrow){
21887 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21888 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21894 // onTouchStart : function(e, el, o)
21896 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21900 // this.showPanelNext();
21904 getChildContainer : function()
21906 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21910 * register a Navigation item
21911 * @param {Roo.bootstrap.NavItem} the navitem to add
21913 register : function(item)
21915 this.tabs.push( item);
21916 item.navId = this.navId; // not really needed..
21921 getActivePanel : function()
21924 Roo.each(this.tabs, function(t) {
21934 getPanelByName : function(n)
21937 Roo.each(this.tabs, function(t) {
21938 if (t.tabId == n) {
21946 indexOfPanel : function(p)
21949 Roo.each(this.tabs, function(t,i) {
21950 if (t.tabId == p.tabId) {
21959 * show a specific panel
21960 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21961 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21963 showPanel : function (pan)
21965 if(this.transition || typeof(pan) == 'undefined'){
21966 Roo.log("waiting for the transitionend");
21970 if (typeof(pan) == 'number') {
21971 pan = this.tabs[pan];
21974 if (typeof(pan) == 'string') {
21975 pan = this.getPanelByName(pan);
21978 var cur = this.getActivePanel();
21981 Roo.log('pan or acitve pan is undefined');
21985 if (pan.tabId == this.getActivePanel().tabId) {
21989 if (false === cur.fireEvent('beforedeactivate')) {
21993 if(this.bullets > 0 && !Roo.isTouch){
21994 this.setActiveBullet(this.indexOfPanel(pan));
21997 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21999 //class="carousel-item carousel-item-next carousel-item-left"
22001 this.transition = true;
22002 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22003 var lr = dir == 'next' ? 'left' : 'right';
22004 pan.el.addClass(dir); // or prev
22005 pan.el.addClass('carousel-item-' + dir); // or prev
22006 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22007 cur.el.addClass(lr); // or right
22008 pan.el.addClass(lr);
22009 cur.el.addClass('carousel-item-' +lr); // or right
22010 pan.el.addClass('carousel-item-' +lr);
22014 cur.el.on('transitionend', function() {
22015 Roo.log("trans end?");
22017 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22018 pan.setActive(true);
22020 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22021 cur.setActive(false);
22023 _this.transition = false;
22025 }, this, { single: true } );
22030 cur.setActive(false);
22031 pan.setActive(true);
22036 showPanelNext : function()
22038 var i = this.indexOfPanel(this.getActivePanel());
22040 if (i >= this.tabs.length - 1 && !this.autoslide) {
22044 if (i >= this.tabs.length - 1 && this.autoslide) {
22048 this.showPanel(this.tabs[i+1]);
22051 showPanelPrev : function()
22053 var i = this.indexOfPanel(this.getActivePanel());
22055 if (i < 1 && !this.autoslide) {
22059 if (i < 1 && this.autoslide) {
22060 i = this.tabs.length;
22063 this.showPanel(this.tabs[i-1]);
22067 addBullet: function()
22069 if(!this.bullets || Roo.isTouch){
22072 var ctr = this.el.select('.carousel-bullets',true).first();
22073 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22074 var bullet = ctr.createChild({
22075 cls : 'bullet bullet-' + i
22076 },ctr.dom.lastChild);
22081 bullet.on('click', (function(e, el, o, ii, t){
22083 e.preventDefault();
22085 this.showPanel(ii);
22087 if(this.autoslide && this.slideFn){
22088 clearInterval(this.slideFn);
22089 this.slideFn = window.setInterval(function() {
22090 _this.showPanelNext();
22094 }).createDelegate(this, [i, bullet], true));
22099 setActiveBullet : function(i)
22105 Roo.each(this.el.select('.bullet', true).elements, function(el){
22106 el.removeClass('selected');
22109 var bullet = this.el.select('.bullet-' + i, true).first();
22115 bullet.addClass('selected');
22126 Roo.apply(Roo.bootstrap.TabGroup, {
22130 * register a Navigation Group
22131 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22133 register : function(navgrp)
22135 this.groups[navgrp.navId] = navgrp;
22139 * fetch a Navigation Group based on the navigation ID
22140 * if one does not exist , it will get created.
22141 * @param {string} the navgroup to add
22142 * @returns {Roo.bootstrap.NavGroup} the navgroup
22144 get: function(navId) {
22145 if (typeof(this.groups[navId]) == 'undefined') {
22146 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22148 return this.groups[navId] ;
22163 * @class Roo.bootstrap.TabPanel
22164 * @extends Roo.bootstrap.Component
22165 * @children Roo.bootstrap.Component
22166 * Bootstrap TabPanel class
22167 * @cfg {Boolean} active panel active
22168 * @cfg {String} html panel content
22169 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22170 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22171 * @cfg {String} href click to link..
22172 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22176 * Create a new TabPanel
22177 * @param {Object} config The config object
22180 Roo.bootstrap.TabPanel = function(config){
22181 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22185 * Fires when the active status changes
22186 * @param {Roo.bootstrap.TabPanel} this
22187 * @param {Boolean} state the new state
22192 * @event beforedeactivate
22193 * Fires before a tab is de-activated - can be used to do validation on a form.
22194 * @param {Roo.bootstrap.TabPanel} this
22195 * @return {Boolean} false if there is an error
22198 'beforedeactivate': true
22201 this.tabId = this.tabId || Roo.id();
22205 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22212 touchSlide : false,
22213 getAutoCreate : function(){
22218 // item is needed for carousel - not sure if it has any effect otherwise
22219 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22220 html: this.html || ''
22224 cfg.cls += ' active';
22228 cfg.tabId = this.tabId;
22236 initEvents: function()
22238 var p = this.parent();
22240 this.navId = this.navId || p.navId;
22242 if (typeof(this.navId) != 'undefined') {
22243 // not really needed.. but just in case.. parent should be a NavGroup.
22244 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22248 var i = tg.tabs.length - 1;
22250 if(this.active && tg.bullets > 0 && i < tg.bullets){
22251 tg.setActiveBullet(i);
22255 this.el.on('click', this.onClick, this);
22257 if(Roo.isTouch && this.touchSlide){
22258 this.el.on("touchstart", this.onTouchStart, this);
22259 this.el.on("touchmove", this.onTouchMove, this);
22260 this.el.on("touchend", this.onTouchEnd, this);
22265 onRender : function(ct, position)
22267 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22270 setActive : function(state)
22272 Roo.log("panel - set active " + this.tabId + "=" + state);
22274 this.active = state;
22276 this.el.removeClass('active');
22278 } else if (!this.el.hasClass('active')) {
22279 this.el.addClass('active');
22282 this.fireEvent('changed', this, state);
22285 onClick : function(e)
22287 e.preventDefault();
22289 if(!this.href.length){
22293 window.location.href = this.href;
22302 onTouchStart : function(e)
22304 this.swiping = false;
22306 this.startX = e.browserEvent.touches[0].clientX;
22307 this.startY = e.browserEvent.touches[0].clientY;
22310 onTouchMove : function(e)
22312 this.swiping = true;
22314 this.endX = e.browserEvent.touches[0].clientX;
22315 this.endY = e.browserEvent.touches[0].clientY;
22318 onTouchEnd : function(e)
22325 var tabGroup = this.parent();
22327 if(this.endX > this.startX){ // swiping right
22328 tabGroup.showPanelPrev();
22332 if(this.startX > this.endX){ // swiping left
22333 tabGroup.showPanelNext();
22352 * @class Roo.bootstrap.DateField
22353 * @extends Roo.bootstrap.Input
22354 * Bootstrap DateField class
22355 * @cfg {Number} weekStart default 0
22356 * @cfg {String} viewMode default empty, (months|years)
22357 * @cfg {String} minViewMode default empty, (months|years)
22358 * @cfg {Number} startDate default -Infinity
22359 * @cfg {Number} endDate default Infinity
22360 * @cfg {Boolean} todayHighlight default false
22361 * @cfg {Boolean} todayBtn default false
22362 * @cfg {Boolean} calendarWeeks default false
22363 * @cfg {Object} daysOfWeekDisabled default empty
22364 * @cfg {Boolean} singleMode default false (true | false)
22366 * @cfg {Boolean} keyboardNavigation default true
22367 * @cfg {String} language default en
22370 * Create a new DateField
22371 * @param {Object} config The config object
22374 Roo.bootstrap.DateField = function(config){
22375 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22379 * Fires when this field show.
22380 * @param {Roo.bootstrap.DateField} this
22381 * @param {Mixed} date The date value
22386 * Fires when this field hide.
22387 * @param {Roo.bootstrap.DateField} this
22388 * @param {Mixed} date The date value
22393 * Fires when select a date.
22394 * @param {Roo.bootstrap.DateField} this
22395 * @param {Mixed} date The date value
22399 * @event beforeselect
22400 * Fires when before select a date.
22401 * @param {Roo.bootstrap.DateField} this
22402 * @param {Mixed} date The date value
22404 beforeselect : true
22408 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22411 * @cfg {String} format
22412 * The default date format string which can be overriden for localization support. The format must be
22413 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22417 * @cfg {String} altFormats
22418 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22419 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22421 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22429 todayHighlight : false,
22435 keyboardNavigation: true,
22437 calendarWeeks: false,
22439 startDate: -Infinity,
22443 daysOfWeekDisabled: [],
22447 singleMode : false,
22449 UTCDate: function()
22451 return new Date(Date.UTC.apply(Date, arguments));
22454 UTCToday: function()
22456 var today = new Date();
22457 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22460 getDate: function() {
22461 var d = this.getUTCDate();
22462 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22465 getUTCDate: function() {
22469 setDate: function(d) {
22470 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22473 setUTCDate: function(d) {
22475 this.setValue(this.formatDate(this.date));
22478 onRender: function(ct, position)
22481 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22483 this.language = this.language || 'en';
22484 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22485 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22487 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22488 this.format = this.format || 'm/d/y';
22489 this.isInline = false;
22490 this.isInput = true;
22491 this.component = this.el.select('.add-on', true).first() || false;
22492 this.component = (this.component && this.component.length === 0) ? false : this.component;
22493 this.hasInput = this.component && this.inputEl().length;
22495 if (typeof(this.minViewMode === 'string')) {
22496 switch (this.minViewMode) {
22498 this.minViewMode = 1;
22501 this.minViewMode = 2;
22504 this.minViewMode = 0;
22509 if (typeof(this.viewMode === 'string')) {
22510 switch (this.viewMode) {
22523 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22525 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22527 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22529 this.picker().on('mousedown', this.onMousedown, this);
22530 this.picker().on('click', this.onClick, this);
22532 this.picker().addClass('datepicker-dropdown');
22534 this.startViewMode = this.viewMode;
22536 if(this.singleMode){
22537 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22538 v.setVisibilityMode(Roo.Element.DISPLAY);
22542 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22543 v.setStyle('width', '189px');
22547 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22548 if(!this.calendarWeeks){
22553 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22554 v.attr('colspan', function(i, val){
22555 return parseInt(val) + 1;
22560 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22562 this.setStartDate(this.startDate);
22563 this.setEndDate(this.endDate);
22565 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22572 if(this.isInline) {
22577 picker : function()
22579 return this.pickerEl;
22580 // return this.el.select('.datepicker', true).first();
22583 fillDow: function()
22585 var dowCnt = this.weekStart;
22594 if(this.calendarWeeks){
22602 while (dowCnt < this.weekStart + 7) {
22606 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22610 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22613 fillMonths: function()
22616 var months = this.picker().select('>.datepicker-months td', true).first();
22618 months.dom.innerHTML = '';
22624 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22627 months.createChild(month);
22634 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;
22636 if (this.date < this.startDate) {
22637 this.viewDate = new Date(this.startDate);
22638 } else if (this.date > this.endDate) {
22639 this.viewDate = new Date(this.endDate);
22641 this.viewDate = new Date(this.date);
22649 var d = new Date(this.viewDate),
22650 year = d.getUTCFullYear(),
22651 month = d.getUTCMonth(),
22652 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22653 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22654 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22655 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22656 currentDate = this.date && this.date.valueOf(),
22657 today = this.UTCToday();
22659 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22661 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22663 // this.picker.select('>tfoot th.today').
22664 // .text(dates[this.language].today)
22665 // .toggle(this.todayBtn !== false);
22667 this.updateNavArrows();
22670 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22672 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22674 prevMonth.setUTCDate(day);
22676 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22678 var nextMonth = new Date(prevMonth);
22680 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22682 nextMonth = nextMonth.valueOf();
22684 var fillMonths = false;
22686 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22688 while(prevMonth.valueOf() <= nextMonth) {
22691 if (prevMonth.getUTCDay() === this.weekStart) {
22693 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22701 if(this.calendarWeeks){
22702 // ISO 8601: First week contains first thursday.
22703 // ISO also states week starts on Monday, but we can be more abstract here.
22705 // Start of current week: based on weekstart/current date
22706 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22707 // Thursday of this week
22708 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22709 // First Thursday of year, year from thursday
22710 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22711 // Calendar week: ms between thursdays, div ms per day, div 7 days
22712 calWeek = (th - yth) / 864e5 / 7 + 1;
22714 fillMonths.cn.push({
22722 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22724 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22727 if (this.todayHighlight &&
22728 prevMonth.getUTCFullYear() == today.getFullYear() &&
22729 prevMonth.getUTCMonth() == today.getMonth() &&
22730 prevMonth.getUTCDate() == today.getDate()) {
22731 clsName += ' today';
22734 if (currentDate && prevMonth.valueOf() === currentDate) {
22735 clsName += ' active';
22738 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22739 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22740 clsName += ' disabled';
22743 fillMonths.cn.push({
22745 cls: 'day ' + clsName,
22746 html: prevMonth.getDate()
22749 prevMonth.setDate(prevMonth.getDate()+1);
22752 var currentYear = this.date && this.date.getUTCFullYear();
22753 var currentMonth = this.date && this.date.getUTCMonth();
22755 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22757 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22758 v.removeClass('active');
22760 if(currentYear === year && k === currentMonth){
22761 v.addClass('active');
22764 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22765 v.addClass('disabled');
22771 year = parseInt(year/10, 10) * 10;
22773 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22775 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22778 for (var i = -1; i < 11; i++) {
22779 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22781 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22789 showMode: function(dir)
22792 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22795 Roo.each(this.picker().select('>div',true).elements, function(v){
22796 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22799 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22804 if(this.isInline) {
22808 this.picker().removeClass(['bottom', 'top']);
22810 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22812 * place to the top of element!
22816 this.picker().addClass('top');
22817 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22822 this.picker().addClass('bottom');
22824 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22827 parseDate : function(value)
22829 if(!value || value instanceof Date){
22832 var v = Date.parseDate(value, this.format);
22833 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22834 v = Date.parseDate(value, 'Y-m-d');
22836 if(!v && this.altFormats){
22837 if(!this.altFormatsArray){
22838 this.altFormatsArray = this.altFormats.split("|");
22840 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22841 v = Date.parseDate(value, this.altFormatsArray[i]);
22847 formatDate : function(date, fmt)
22849 return (!date || !(date instanceof Date)) ?
22850 date : date.dateFormat(fmt || this.format);
22853 onFocus : function()
22855 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22859 onBlur : function()
22861 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22863 var d = this.inputEl().getValue();
22870 showPopup : function()
22872 this.picker().show();
22876 this.fireEvent('showpopup', this, this.date);
22879 hidePopup : function()
22881 if(this.isInline) {
22884 this.picker().hide();
22885 this.viewMode = this.startViewMode;
22888 this.fireEvent('hidepopup', this, this.date);
22892 onMousedown: function(e)
22894 e.stopPropagation();
22895 e.preventDefault();
22900 Roo.bootstrap.DateField.superclass.keyup.call(this);
22904 setValue: function(v)
22906 if(this.fireEvent('beforeselect', this, v) !== false){
22907 var d = new Date(this.parseDate(v) ).clearTime();
22909 if(isNaN(d.getTime())){
22910 this.date = this.viewDate = '';
22911 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22915 v = this.formatDate(d);
22917 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22919 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22923 this.fireEvent('select', this, this.date);
22927 getValue: function()
22929 return this.formatDate(this.date);
22932 fireKey: function(e)
22934 if (!this.picker().isVisible()){
22935 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22941 var dateChanged = false,
22943 newDate, newViewDate;
22948 e.preventDefault();
22952 if (!this.keyboardNavigation) {
22955 dir = e.keyCode == 37 ? -1 : 1;
22958 newDate = this.moveYear(this.date, dir);
22959 newViewDate = this.moveYear(this.viewDate, dir);
22960 } else if (e.shiftKey){
22961 newDate = this.moveMonth(this.date, dir);
22962 newViewDate = this.moveMonth(this.viewDate, dir);
22964 newDate = new Date(this.date);
22965 newDate.setUTCDate(this.date.getUTCDate() + dir);
22966 newViewDate = new Date(this.viewDate);
22967 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22969 if (this.dateWithinRange(newDate)){
22970 this.date = newDate;
22971 this.viewDate = newViewDate;
22972 this.setValue(this.formatDate(this.date));
22974 e.preventDefault();
22975 dateChanged = true;
22980 if (!this.keyboardNavigation) {
22983 dir = e.keyCode == 38 ? -1 : 1;
22985 newDate = this.moveYear(this.date, dir);
22986 newViewDate = this.moveYear(this.viewDate, dir);
22987 } else if (e.shiftKey){
22988 newDate = this.moveMonth(this.date, dir);
22989 newViewDate = this.moveMonth(this.viewDate, dir);
22991 newDate = new Date(this.date);
22992 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22993 newViewDate = new Date(this.viewDate);
22994 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22996 if (this.dateWithinRange(newDate)){
22997 this.date = newDate;
22998 this.viewDate = newViewDate;
22999 this.setValue(this.formatDate(this.date));
23001 e.preventDefault();
23002 dateChanged = true;
23006 this.setValue(this.formatDate(this.date));
23008 e.preventDefault();
23011 this.setValue(this.formatDate(this.date));
23025 onClick: function(e)
23027 e.stopPropagation();
23028 e.preventDefault();
23030 var target = e.getTarget();
23032 if(target.nodeName.toLowerCase() === 'i'){
23033 target = Roo.get(target).dom.parentNode;
23036 var nodeName = target.nodeName;
23037 var className = target.className;
23038 var html = target.innerHTML;
23039 //Roo.log(nodeName);
23041 switch(nodeName.toLowerCase()) {
23043 switch(className) {
23049 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23050 switch(this.viewMode){
23052 this.viewDate = this.moveMonth(this.viewDate, dir);
23056 this.viewDate = this.moveYear(this.viewDate, dir);
23062 var date = new Date();
23063 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23065 this.setValue(this.formatDate(this.date));
23072 if (className.indexOf('disabled') < 0) {
23073 if (!this.viewDate) {
23074 this.viewDate = new Date();
23076 this.viewDate.setUTCDate(1);
23077 if (className.indexOf('month') > -1) {
23078 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23080 var year = parseInt(html, 10) || 0;
23081 this.viewDate.setUTCFullYear(year);
23085 if(this.singleMode){
23086 this.setValue(this.formatDate(this.viewDate));
23097 //Roo.log(className);
23098 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23099 var day = parseInt(html, 10) || 1;
23100 var year = (this.viewDate || new Date()).getUTCFullYear(),
23101 month = (this.viewDate || new Date()).getUTCMonth();
23103 if (className.indexOf('old') > -1) {
23110 } else if (className.indexOf('new') > -1) {
23118 //Roo.log([year,month,day]);
23119 this.date = this.UTCDate(year, month, day,0,0,0,0);
23120 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23122 //Roo.log(this.formatDate(this.date));
23123 this.setValue(this.formatDate(this.date));
23130 setStartDate: function(startDate)
23132 this.startDate = startDate || -Infinity;
23133 if (this.startDate !== -Infinity) {
23134 this.startDate = this.parseDate(this.startDate);
23137 this.updateNavArrows();
23140 setEndDate: function(endDate)
23142 this.endDate = endDate || Infinity;
23143 if (this.endDate !== Infinity) {
23144 this.endDate = this.parseDate(this.endDate);
23147 this.updateNavArrows();
23150 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23152 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23153 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23154 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23156 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23157 return parseInt(d, 10);
23160 this.updateNavArrows();
23163 updateNavArrows: function()
23165 if(this.singleMode){
23169 var d = new Date(this.viewDate),
23170 year = d.getUTCFullYear(),
23171 month = d.getUTCMonth();
23173 Roo.each(this.picker().select('.prev', true).elements, function(v){
23175 switch (this.viewMode) {
23178 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23184 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23191 Roo.each(this.picker().select('.next', true).elements, function(v){
23193 switch (this.viewMode) {
23196 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23202 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23210 moveMonth: function(date, dir)
23215 var new_date = new Date(date.valueOf()),
23216 day = new_date.getUTCDate(),
23217 month = new_date.getUTCMonth(),
23218 mag = Math.abs(dir),
23220 dir = dir > 0 ? 1 : -1;
23223 // If going back one month, make sure month is not current month
23224 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23226 return new_date.getUTCMonth() == month;
23228 // If going forward one month, make sure month is as expected
23229 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23231 return new_date.getUTCMonth() != new_month;
23233 new_month = month + dir;
23234 new_date.setUTCMonth(new_month);
23235 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23236 if (new_month < 0 || new_month > 11) {
23237 new_month = (new_month + 12) % 12;
23240 // For magnitudes >1, move one month at a time...
23241 for (var i=0; i<mag; i++) {
23242 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23243 new_date = this.moveMonth(new_date, dir);
23245 // ...then reset the day, keeping it in the new month
23246 new_month = new_date.getUTCMonth();
23247 new_date.setUTCDate(day);
23249 return new_month != new_date.getUTCMonth();
23252 // Common date-resetting loop -- if date is beyond end of month, make it
23255 new_date.setUTCDate(--day);
23256 new_date.setUTCMonth(new_month);
23261 moveYear: function(date, dir)
23263 return this.moveMonth(date, dir*12);
23266 dateWithinRange: function(date)
23268 return date >= this.startDate && date <= this.endDate;
23274 this.picker().remove();
23277 validateValue : function(value)
23279 if(this.getVisibilityEl().hasClass('hidden')){
23283 if(value.length < 1) {
23284 if(this.allowBlank){
23290 if(value.length < this.minLength){
23293 if(value.length > this.maxLength){
23297 var vt = Roo.form.VTypes;
23298 if(!vt[this.vtype](value, this)){
23302 if(typeof this.validator == "function"){
23303 var msg = this.validator(value);
23309 if(this.regex && !this.regex.test(value)){
23313 if(typeof(this.parseDate(value)) == 'undefined'){
23317 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23321 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23331 this.date = this.viewDate = '';
23333 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23338 Roo.apply(Roo.bootstrap.DateField, {
23349 html: '<i class="fa fa-arrow-left"/>'
23359 html: '<i class="fa fa-arrow-right"/>'
23401 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23402 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23403 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23404 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23405 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23418 navFnc: 'FullYear',
23423 navFnc: 'FullYear',
23428 Roo.apply(Roo.bootstrap.DateField, {
23432 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23436 cls: 'datepicker-days',
23440 cls: 'table-condensed',
23442 Roo.bootstrap.DateField.head,
23446 Roo.bootstrap.DateField.footer
23453 cls: 'datepicker-months',
23457 cls: 'table-condensed',
23459 Roo.bootstrap.DateField.head,
23460 Roo.bootstrap.DateField.content,
23461 Roo.bootstrap.DateField.footer
23468 cls: 'datepicker-years',
23472 cls: 'table-condensed',
23474 Roo.bootstrap.DateField.head,
23475 Roo.bootstrap.DateField.content,
23476 Roo.bootstrap.DateField.footer
23495 * @class Roo.bootstrap.TimeField
23496 * @extends Roo.bootstrap.Input
23497 * Bootstrap DateField class
23501 * Create a new TimeField
23502 * @param {Object} config The config object
23505 Roo.bootstrap.TimeField = function(config){
23506 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23510 * Fires when this field show.
23511 * @param {Roo.bootstrap.DateField} thisthis
23512 * @param {Mixed} date The date value
23517 * Fires when this field hide.
23518 * @param {Roo.bootstrap.DateField} this
23519 * @param {Mixed} date The date value
23524 * Fires when select a date.
23525 * @param {Roo.bootstrap.DateField} this
23526 * @param {Mixed} date The date value
23532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23535 * @cfg {String} format
23536 * The default time format string which can be overriden for localization support. The format must be
23537 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23541 getAutoCreate : function()
23543 this.after = '<i class="fa far fa-clock"></i>';
23544 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23548 onRender: function(ct, position)
23551 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23553 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23555 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23557 this.pop = this.picker().select('>.datepicker-time',true).first();
23558 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23560 this.picker().on('mousedown', this.onMousedown, this);
23561 this.picker().on('click', this.onClick, this);
23563 this.picker().addClass('datepicker-dropdown');
23568 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23569 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23570 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23571 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23572 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23573 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23577 fireKey: function(e){
23578 if (!this.picker().isVisible()){
23579 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23585 e.preventDefault();
23593 this.onTogglePeriod();
23596 this.onIncrementMinutes();
23599 this.onDecrementMinutes();
23608 onClick: function(e) {
23609 e.stopPropagation();
23610 e.preventDefault();
23613 picker : function()
23615 return this.pickerEl;
23618 fillTime: function()
23620 var time = this.pop.select('tbody', true).first();
23622 time.dom.innerHTML = '';
23637 cls: 'hours-up fa fas fa-chevron-up'
23657 cls: 'minutes-up fa fas fa-chevron-up'
23678 cls: 'timepicker-hour',
23693 cls: 'timepicker-minute',
23708 cls: 'btn btn-primary period',
23730 cls: 'hours-down fa fas fa-chevron-down'
23750 cls: 'minutes-down fa fas fa-chevron-down'
23768 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23775 var hours = this.time.getHours();
23776 var minutes = this.time.getMinutes();
23789 hours = hours - 12;
23793 hours = '0' + hours;
23797 minutes = '0' + minutes;
23800 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23801 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23802 this.pop.select('button', true).first().dom.innerHTML = period;
23808 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23810 var cls = ['bottom'];
23812 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23819 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23823 //this.picker().setXY(20000,20000);
23824 this.picker().addClass(cls.join('-'));
23828 Roo.each(cls, function(c){
23833 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23834 //_this.picker().setTop(_this.inputEl().getHeight());
23838 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23840 //_this.picker().setTop(0 - _this.picker().getHeight());
23845 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23849 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23857 onFocus : function()
23859 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23863 onBlur : function()
23865 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23871 this.picker().show();
23876 this.fireEvent('show', this, this.date);
23881 this.picker().hide();
23884 this.fireEvent('hide', this, this.date);
23887 setTime : function()
23890 this.setValue(this.time.format(this.format));
23892 this.fireEvent('select', this, this.date);
23897 onMousedown: function(e){
23898 e.stopPropagation();
23899 e.preventDefault();
23902 onIncrementHours: function()
23904 Roo.log('onIncrementHours');
23905 this.time = this.time.add(Date.HOUR, 1);
23910 onDecrementHours: function()
23912 Roo.log('onDecrementHours');
23913 this.time = this.time.add(Date.HOUR, -1);
23917 onIncrementMinutes: function()
23919 Roo.log('onIncrementMinutes');
23920 this.time = this.time.add(Date.MINUTE, 1);
23924 onDecrementMinutes: function()
23926 Roo.log('onDecrementMinutes');
23927 this.time = this.time.add(Date.MINUTE, -1);
23931 onTogglePeriod: function()
23933 Roo.log('onTogglePeriod');
23934 this.time = this.time.add(Date.HOUR, 12);
23942 Roo.apply(Roo.bootstrap.TimeField, {
23946 cls: 'datepicker dropdown-menu',
23950 cls: 'datepicker-time',
23954 cls: 'table-condensed',
23983 cls: 'btn btn-info ok',
24011 * @class Roo.bootstrap.MonthField
24012 * @extends Roo.bootstrap.Input
24013 * Bootstrap MonthField class
24015 * @cfg {String} language default en
24018 * Create a new MonthField
24019 * @param {Object} config The config object
24022 Roo.bootstrap.MonthField = function(config){
24023 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24028 * Fires when this field show.
24029 * @param {Roo.bootstrap.MonthField} this
24030 * @param {Mixed} date The date value
24035 * Fires when this field hide.
24036 * @param {Roo.bootstrap.MonthField} this
24037 * @param {Mixed} date The date value
24042 * Fires when select a date.
24043 * @param {Roo.bootstrap.MonthField} this
24044 * @param {String} oldvalue The old value
24045 * @param {String} newvalue The new value
24051 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24053 onRender: function(ct, position)
24056 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24058 this.language = this.language || 'en';
24059 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24060 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24062 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24063 this.isInline = false;
24064 this.isInput = true;
24065 this.component = this.el.select('.add-on', true).first() || false;
24066 this.component = (this.component && this.component.length === 0) ? false : this.component;
24067 this.hasInput = this.component && this.inputEL().length;
24069 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24071 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24073 this.picker().on('mousedown', this.onMousedown, this);
24074 this.picker().on('click', this.onClick, this);
24076 this.picker().addClass('datepicker-dropdown');
24078 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24079 v.setStyle('width', '189px');
24086 if(this.isInline) {
24092 setValue: function(v, suppressEvent)
24094 var o = this.getValue();
24096 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24100 if(suppressEvent !== true){
24101 this.fireEvent('select', this, o, v);
24106 getValue: function()
24111 onClick: function(e)
24113 e.stopPropagation();
24114 e.preventDefault();
24116 var target = e.getTarget();
24118 if(target.nodeName.toLowerCase() === 'i'){
24119 target = Roo.get(target).dom.parentNode;
24122 var nodeName = target.nodeName;
24123 var className = target.className;
24124 var html = target.innerHTML;
24126 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24130 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24132 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24138 picker : function()
24140 return this.pickerEl;
24143 fillMonths: function()
24146 var months = this.picker().select('>.datepicker-months td', true).first();
24148 months.dom.innerHTML = '';
24154 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24157 months.createChild(month);
24166 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24167 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24170 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24171 e.removeClass('active');
24173 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24174 e.addClass('active');
24181 if(this.isInline) {
24185 this.picker().removeClass(['bottom', 'top']);
24187 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24189 * place to the top of element!
24193 this.picker().addClass('top');
24194 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24199 this.picker().addClass('bottom');
24201 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24204 onFocus : function()
24206 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24210 onBlur : function()
24212 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24214 var d = this.inputEl().getValue();
24223 this.picker().show();
24224 this.picker().select('>.datepicker-months', true).first().show();
24228 this.fireEvent('show', this, this.date);
24233 if(this.isInline) {
24236 this.picker().hide();
24237 this.fireEvent('hide', this, this.date);
24241 onMousedown: function(e)
24243 e.stopPropagation();
24244 e.preventDefault();
24249 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24253 fireKey: function(e)
24255 if (!this.picker().isVisible()){
24256 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24267 e.preventDefault();
24271 dir = e.keyCode == 37 ? -1 : 1;
24273 this.vIndex = this.vIndex + dir;
24275 if(this.vIndex < 0){
24279 if(this.vIndex > 11){
24283 if(isNaN(this.vIndex)){
24287 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24293 dir = e.keyCode == 38 ? -1 : 1;
24295 this.vIndex = this.vIndex + dir * 4;
24297 if(this.vIndex < 0){
24301 if(this.vIndex > 11){
24305 if(isNaN(this.vIndex)){
24309 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24314 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24315 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24319 e.preventDefault();
24322 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24323 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24339 this.picker().remove();
24344 Roo.apply(Roo.bootstrap.MonthField, {
24363 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24364 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24369 Roo.apply(Roo.bootstrap.MonthField, {
24373 cls: 'datepicker dropdown-menu roo-dynamic',
24377 cls: 'datepicker-months',
24381 cls: 'table-condensed',
24383 Roo.bootstrap.DateField.content
24403 * @class Roo.bootstrap.CheckBox
24404 * @extends Roo.bootstrap.Input
24405 * Bootstrap CheckBox class
24407 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24408 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24409 * @cfg {String} boxLabel The text that appears beside the checkbox
24410 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24411 * @cfg {Boolean} checked initnal the element
24412 * @cfg {Boolean} inline inline the element (default false)
24413 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24414 * @cfg {String} tooltip label tooltip
24417 * Create a new CheckBox
24418 * @param {Object} config The config object
24421 Roo.bootstrap.CheckBox = function(config){
24422 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24427 * Fires when the element is checked or unchecked.
24428 * @param {Roo.bootstrap.CheckBox} this This input
24429 * @param {Boolean} checked The new checked value
24434 * Fires when the element is click.
24435 * @param {Roo.bootstrap.CheckBox} this This input
24442 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24444 inputType: 'checkbox',
24453 // checkbox success does not make any sense really..
24458 getAutoCreate : function()
24460 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24466 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24469 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24475 type : this.inputType,
24476 value : this.inputValue,
24477 cls : 'roo-' + this.inputType, //'form-box',
24478 placeholder : this.placeholder || ''
24482 if(this.inputType != 'radio'){
24486 cls : 'roo-hidden-value',
24487 value : this.checked ? this.inputValue : this.valueOff
24492 if (this.weight) { // Validity check?
24493 cfg.cls += " " + this.inputType + "-" + this.weight;
24496 if (this.disabled) {
24497 input.disabled=true;
24501 input.checked = this.checked;
24506 input.name = this.name;
24508 if(this.inputType != 'radio'){
24509 hidden.name = this.name;
24510 input.name = '_hidden_' + this.name;
24515 input.cls += ' input-' + this.size;
24520 ['xs','sm','md','lg'].map(function(size){
24521 if (settings[size]) {
24522 cfg.cls += ' col-' + size + '-' + settings[size];
24526 var inputblock = input;
24528 if (this.before || this.after) {
24531 cls : 'input-group',
24536 inputblock.cn.push({
24538 cls : 'input-group-addon',
24543 inputblock.cn.push(input);
24545 if(this.inputType != 'radio'){
24546 inputblock.cn.push(hidden);
24550 inputblock.cn.push({
24552 cls : 'input-group-addon',
24558 var boxLabelCfg = false;
24564 //'for': id, // box label is handled by onclick - so no for...
24566 html: this.boxLabel
24569 boxLabelCfg.tooltip = this.tooltip;
24575 if (align ==='left' && this.fieldLabel.length) {
24576 // Roo.log("left and has label");
24581 cls : 'control-label',
24582 html : this.fieldLabel
24593 cfg.cn[1].cn.push(boxLabelCfg);
24596 if(this.labelWidth > 12){
24597 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24600 if(this.labelWidth < 13 && this.labelmd == 0){
24601 this.labelmd = this.labelWidth;
24604 if(this.labellg > 0){
24605 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24606 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24609 if(this.labelmd > 0){
24610 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24611 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24614 if(this.labelsm > 0){
24615 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24616 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24619 if(this.labelxs > 0){
24620 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24621 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24624 } else if ( this.fieldLabel.length) {
24625 // Roo.log(" label");
24629 tag: this.boxLabel ? 'span' : 'label',
24631 cls: 'control-label box-input-label',
24632 //cls : 'input-group-addon',
24633 html : this.fieldLabel
24640 cfg.cn.push(boxLabelCfg);
24645 // Roo.log(" no label && no align");
24646 cfg.cn = [ inputblock ] ;
24648 cfg.cn.push(boxLabelCfg);
24656 if(this.inputType != 'radio'){
24657 cfg.cn.push(hidden);
24665 * return the real input element.
24667 inputEl: function ()
24669 return this.el.select('input.roo-' + this.inputType,true).first();
24671 hiddenEl: function ()
24673 return this.el.select('input.roo-hidden-value',true).first();
24676 labelEl: function()
24678 return this.el.select('label.control-label',true).first();
24680 /* depricated... */
24684 return this.labelEl();
24687 boxLabelEl: function()
24689 return this.el.select('label.box-label',true).first();
24692 initEvents : function()
24694 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24696 this.inputEl().on('click', this.onClick, this);
24698 if (this.boxLabel) {
24699 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24702 this.startValue = this.getValue();
24705 Roo.bootstrap.CheckBox.register(this);
24709 onClick : function(e)
24711 if(this.fireEvent('click', this, e) !== false){
24712 this.setChecked(!this.checked);
24717 setChecked : function(state,suppressEvent)
24719 this.startValue = this.getValue();
24721 if(this.inputType == 'radio'){
24723 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24724 e.dom.checked = false;
24727 this.inputEl().dom.checked = true;
24729 this.inputEl().dom.value = this.inputValue;
24731 if(suppressEvent !== true){
24732 this.fireEvent('check', this, true);
24740 this.checked = state;
24742 this.inputEl().dom.checked = state;
24745 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24747 if(suppressEvent !== true){
24748 this.fireEvent('check', this, state);
24754 getValue : function()
24756 if(this.inputType == 'radio'){
24757 return this.getGroupValue();
24760 return this.hiddenEl().dom.value;
24764 getGroupValue : function()
24766 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24770 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24773 setValue : function(v,suppressEvent)
24775 if(this.inputType == 'radio'){
24776 this.setGroupValue(v, suppressEvent);
24780 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24785 setGroupValue : function(v, suppressEvent)
24787 this.startValue = this.getValue();
24789 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24790 e.dom.checked = false;
24792 if(e.dom.value == v){
24793 e.dom.checked = true;
24797 if(suppressEvent !== true){
24798 this.fireEvent('check', this, true);
24806 validate : function()
24808 if(this.getVisibilityEl().hasClass('hidden')){
24814 (this.inputType == 'radio' && this.validateRadio()) ||
24815 (this.inputType == 'checkbox' && this.validateCheckbox())
24821 this.markInvalid();
24825 validateRadio : function()
24827 if(this.getVisibilityEl().hasClass('hidden')){
24831 if(this.allowBlank){
24837 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24838 if(!e.dom.checked){
24850 validateCheckbox : function()
24853 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24854 //return (this.getValue() == this.inputValue) ? true : false;
24857 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24865 for(var i in group){
24866 if(group[i].el.isVisible(true)){
24874 for(var i in group){
24879 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24886 * Mark this field as valid
24888 markValid : function()
24892 this.fireEvent('valid', this);
24894 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24897 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24904 if(this.inputType == 'radio'){
24905 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24906 var fg = e.findParent('.form-group', false, true);
24907 if (Roo.bootstrap.version == 3) {
24908 fg.removeClass([_this.invalidClass, _this.validClass]);
24909 fg.addClass(_this.validClass);
24911 fg.removeClass(['is-valid', 'is-invalid']);
24912 fg.addClass('is-valid');
24920 var fg = this.el.findParent('.form-group', false, true);
24921 if (Roo.bootstrap.version == 3) {
24922 fg.removeClass([this.invalidClass, this.validClass]);
24923 fg.addClass(this.validClass);
24925 fg.removeClass(['is-valid', 'is-invalid']);
24926 fg.addClass('is-valid');
24931 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24937 for(var i in group){
24938 var fg = group[i].el.findParent('.form-group', false, true);
24939 if (Roo.bootstrap.version == 3) {
24940 fg.removeClass([this.invalidClass, this.validClass]);
24941 fg.addClass(this.validClass);
24943 fg.removeClass(['is-valid', 'is-invalid']);
24944 fg.addClass('is-valid');
24950 * Mark this field as invalid
24951 * @param {String} msg The validation message
24953 markInvalid : function(msg)
24955 if(this.allowBlank){
24961 this.fireEvent('invalid', this, msg);
24963 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24966 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24970 label.markInvalid();
24973 if(this.inputType == 'radio'){
24975 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24976 var fg = e.findParent('.form-group', false, true);
24977 if (Roo.bootstrap.version == 3) {
24978 fg.removeClass([_this.invalidClass, _this.validClass]);
24979 fg.addClass(_this.invalidClass);
24981 fg.removeClass(['is-invalid', 'is-valid']);
24982 fg.addClass('is-invalid');
24990 var fg = this.el.findParent('.form-group', false, true);
24991 if (Roo.bootstrap.version == 3) {
24992 fg.removeClass([_this.invalidClass, _this.validClass]);
24993 fg.addClass(_this.invalidClass);
24995 fg.removeClass(['is-invalid', 'is-valid']);
24996 fg.addClass('is-invalid');
25001 var group = Roo.bootstrap.CheckBox.get(this.groupId);
25007 for(var i in group){
25008 var fg = group[i].el.findParent('.form-group', false, true);
25009 if (Roo.bootstrap.version == 3) {
25010 fg.removeClass([_this.invalidClass, _this.validClass]);
25011 fg.addClass(_this.invalidClass);
25013 fg.removeClass(['is-invalid', 'is-valid']);
25014 fg.addClass('is-invalid');
25020 clearInvalid : function()
25022 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25024 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25026 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25028 if (label && label.iconEl) {
25029 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25030 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25034 disable : function()
25036 if(this.inputType != 'radio'){
25037 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25044 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25045 _this.getActionEl().addClass(this.disabledClass);
25046 e.dom.disabled = true;
25050 this.disabled = true;
25051 this.fireEvent("disable", this);
25055 enable : function()
25057 if(this.inputType != 'radio'){
25058 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25065 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25066 _this.getActionEl().removeClass(this.disabledClass);
25067 e.dom.disabled = false;
25071 this.disabled = false;
25072 this.fireEvent("enable", this);
25076 setBoxLabel : function(v)
25081 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25087 Roo.apply(Roo.bootstrap.CheckBox, {
25092 * register a CheckBox Group
25093 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25095 register : function(checkbox)
25097 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25098 this.groups[checkbox.groupId] = {};
25101 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25105 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25109 * fetch a CheckBox Group based on the group ID
25110 * @param {string} the group ID
25111 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25113 get: function(groupId) {
25114 if (typeof(this.groups[groupId]) == 'undefined') {
25118 return this.groups[groupId] ;
25131 * @class Roo.bootstrap.Radio
25132 * @extends Roo.bootstrap.Component
25133 * Bootstrap Radio class
25134 * @cfg {String} boxLabel - the label associated
25135 * @cfg {String} value - the value of radio
25138 * Create a new Radio
25139 * @param {Object} config The config object
25141 Roo.bootstrap.Radio = function(config){
25142 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25146 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25152 getAutoCreate : function()
25156 cls : 'form-group radio',
25161 html : this.boxLabel
25169 initEvents : function()
25171 this.parent().register(this);
25173 this.el.on('click', this.onClick, this);
25177 onClick : function(e)
25179 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25180 this.setChecked(true);
25184 setChecked : function(state, suppressEvent)
25186 this.parent().setValue(this.value, suppressEvent);
25190 setBoxLabel : function(v)
25195 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25210 * @class Roo.bootstrap.SecurePass
25211 * @extends Roo.bootstrap.Input
25212 * Bootstrap SecurePass class
25216 * Create a new SecurePass
25217 * @param {Object} config The config object
25220 Roo.bootstrap.SecurePass = function (config) {
25221 // these go here, so the translation tool can replace them..
25223 PwdEmpty: "Please type a password, and then retype it to confirm.",
25224 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25225 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25226 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25227 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25228 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25229 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25230 TooWeak: "Your password is Too Weak."
25232 this.meterLabel = "Password strength:";
25233 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25234 this.meterClass = [
25235 "roo-password-meter-tooweak",
25236 "roo-password-meter-weak",
25237 "roo-password-meter-medium",
25238 "roo-password-meter-strong",
25239 "roo-password-meter-grey"
25244 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25247 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25249 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25251 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25252 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25253 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25254 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25255 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25256 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25257 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25267 * @cfg {String/Object} Label for the strength meter (defaults to
25268 * 'Password strength:')
25273 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25274 * ['Weak', 'Medium', 'Strong'])
25277 pwdStrengths: false,
25290 initEvents: function ()
25292 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25294 if (this.el.is('input[type=password]') && Roo.isSafari) {
25295 this.el.on('keydown', this.SafariOnKeyDown, this);
25298 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25301 onRender: function (ct, position)
25303 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25304 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25305 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25307 this.trigger.createChild({
25312 cls: 'roo-password-meter-grey col-xs-12',
25315 //width: this.meterWidth + 'px'
25319 cls: 'roo-password-meter-text'
25325 if (this.hideTrigger) {
25326 this.trigger.setDisplayed(false);
25328 this.setSize(this.width || '', this.height || '');
25331 onDestroy: function ()
25333 if (this.trigger) {
25334 this.trigger.removeAllListeners();
25335 this.trigger.remove();
25338 this.wrap.remove();
25340 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25343 checkStrength: function ()
25345 var pwd = this.inputEl().getValue();
25346 if (pwd == this._lastPwd) {
25351 if (this.ClientSideStrongPassword(pwd)) {
25353 } else if (this.ClientSideMediumPassword(pwd)) {
25355 } else if (this.ClientSideWeakPassword(pwd)) {
25361 Roo.log('strength1: ' + strength);
25363 //var pm = this.trigger.child('div/div/div').dom;
25364 var pm = this.trigger.child('div/div');
25365 pm.removeClass(this.meterClass);
25366 pm.addClass(this.meterClass[strength]);
25369 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25371 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25373 this._lastPwd = pwd;
25377 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25379 this._lastPwd = '';
25381 var pm = this.trigger.child('div/div');
25382 pm.removeClass(this.meterClass);
25383 pm.addClass('roo-password-meter-grey');
25386 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25389 this.inputEl().dom.type='password';
25392 validateValue: function (value)
25394 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25397 if (value.length == 0) {
25398 if (this.allowBlank) {
25399 this.clearInvalid();
25403 this.markInvalid(this.errors.PwdEmpty);
25404 this.errorMsg = this.errors.PwdEmpty;
25412 if (!value.match(/[\x21-\x7e]+/)) {
25413 this.markInvalid(this.errors.PwdBadChar);
25414 this.errorMsg = this.errors.PwdBadChar;
25417 if (value.length < 6) {
25418 this.markInvalid(this.errors.PwdShort);
25419 this.errorMsg = this.errors.PwdShort;
25422 if (value.length > 16) {
25423 this.markInvalid(this.errors.PwdLong);
25424 this.errorMsg = this.errors.PwdLong;
25428 if (this.ClientSideStrongPassword(value)) {
25430 } else if (this.ClientSideMediumPassword(value)) {
25432 } else if (this.ClientSideWeakPassword(value)) {
25439 if (strength < 2) {
25440 //this.markInvalid(this.errors.TooWeak);
25441 this.errorMsg = this.errors.TooWeak;
25446 console.log('strength2: ' + strength);
25448 //var pm = this.trigger.child('div/div/div').dom;
25450 var pm = this.trigger.child('div/div');
25451 pm.removeClass(this.meterClass);
25452 pm.addClass(this.meterClass[strength]);
25454 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25456 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25458 this.errorMsg = '';
25462 CharacterSetChecks: function (type)
25465 this.fResult = false;
25468 isctype: function (character, type)
25471 case this.kCapitalLetter:
25472 if (character >= 'A' && character <= 'Z') {
25477 case this.kSmallLetter:
25478 if (character >= 'a' && character <= 'z') {
25484 if (character >= '0' && character <= '9') {
25489 case this.kPunctuation:
25490 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25501 IsLongEnough: function (pwd, size)
25503 return !(pwd == null || isNaN(size) || pwd.length < size);
25506 SpansEnoughCharacterSets: function (word, nb)
25508 if (!this.IsLongEnough(word, nb))
25513 var characterSetChecks = new Array(
25514 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25515 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25518 for (var index = 0; index < word.length; ++index) {
25519 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25520 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25521 characterSetChecks[nCharSet].fResult = true;
25528 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25529 if (characterSetChecks[nCharSet].fResult) {
25534 if (nCharSets < nb) {
25540 ClientSideStrongPassword: function (pwd)
25542 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25545 ClientSideMediumPassword: function (pwd)
25547 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25550 ClientSideWeakPassword: function (pwd)
25552 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25555 })//<script type="text/javascript">
25558 * Based Ext JS Library 1.1.1
25559 * Copyright(c) 2006-2007, Ext JS, LLC.
25565 * @class Roo.HtmlEditorCore
25566 * @extends Roo.Component
25567 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25569 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25572 Roo.HtmlEditorCore = function(config){
25575 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25580 * @event initialize
25581 * Fires when the editor is fully initialized (including the iframe)
25582 * @param {Roo.HtmlEditorCore} this
25587 * Fires when the editor is first receives the focus. Any insertion must wait
25588 * until after this event.
25589 * @param {Roo.HtmlEditorCore} this
25593 * @event beforesync
25594 * Fires before the textarea is updated with content from the editor iframe. Return false
25595 * to cancel the sync.
25596 * @param {Roo.HtmlEditorCore} this
25597 * @param {String} html
25601 * @event beforepush
25602 * Fires before the iframe editor is updated with content from the textarea. Return false
25603 * to cancel the push.
25604 * @param {Roo.HtmlEditorCore} this
25605 * @param {String} html
25610 * Fires when the textarea is updated with content from the editor iframe.
25611 * @param {Roo.HtmlEditorCore} this
25612 * @param {String} html
25617 * Fires when the iframe editor is updated with content from the textarea.
25618 * @param {Roo.HtmlEditorCore} this
25619 * @param {String} html
25624 * @event editorevent
25625 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25626 * @param {Roo.HtmlEditorCore} this
25632 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25634 // defaults : white / black...
25635 this.applyBlacklists();
25642 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25646 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25652 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25657 * @cfg {Number} height (in pixels)
25661 * @cfg {Number} width (in pixels)
25666 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25669 stylesheets: false,
25672 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25674 allowComments: false,
25678 // private properties
25679 validationEvent : false,
25681 initialized : false,
25683 sourceEditMode : false,
25684 onFocus : Roo.emptyFn,
25686 hideMode:'offsets',
25690 // blacklist + whitelisted elements..
25697 * Protected method that will not generally be called directly. It
25698 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25699 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25701 getDocMarkup : function(){
25705 // inherit styels from page...??
25706 if (this.stylesheets === false) {
25708 Roo.get(document.head).select('style').each(function(node) {
25709 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25712 Roo.get(document.head).select('link').each(function(node) {
25713 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25716 } else if (!this.stylesheets.length) {
25718 st = '<style type="text/css">' +
25719 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25722 for (var i in this.stylesheets) {
25723 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25728 st += '<style type="text/css">' +
25729 'IMG { cursor: pointer } ' +
25732 var cls = 'roo-htmleditor-body';
25734 if(this.bodyCls.length){
25735 cls += ' ' + this.bodyCls;
25738 return '<html><head>' + st +
25739 //<style type="text/css">' +
25740 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25742 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25746 onRender : function(ct, position)
25749 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25750 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25753 this.el.dom.style.border = '0 none';
25754 this.el.dom.setAttribute('tabIndex', -1);
25755 this.el.addClass('x-hidden hide');
25759 if(Roo.isIE){ // fix IE 1px bogus margin
25760 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25764 this.frameId = Roo.id();
25768 var iframe = this.owner.wrap.createChild({
25770 cls: 'form-control', // bootstrap..
25772 name: this.frameId,
25773 frameBorder : 'no',
25774 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25779 this.iframe = iframe.dom;
25781 this.assignDocWin();
25783 this.doc.designMode = 'on';
25786 this.doc.write(this.getDocMarkup());
25790 var task = { // must defer to wait for browser to be ready
25792 //console.log("run task?" + this.doc.readyState);
25793 this.assignDocWin();
25794 if(this.doc.body || this.doc.readyState == 'complete'){
25796 this.doc.designMode="on";
25800 Roo.TaskMgr.stop(task);
25801 this.initEditor.defer(10, this);
25808 Roo.TaskMgr.start(task);
25813 onResize : function(w, h)
25815 Roo.log('resize: ' +w + ',' + h );
25816 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25820 if(typeof w == 'number'){
25822 this.iframe.style.width = w + 'px';
25824 if(typeof h == 'number'){
25826 this.iframe.style.height = h + 'px';
25828 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25835 * Toggles the editor between standard and source edit mode.
25836 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25838 toggleSourceEdit : function(sourceEditMode){
25840 this.sourceEditMode = sourceEditMode === true;
25842 if(this.sourceEditMode){
25844 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25847 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25848 //this.iframe.className = '';
25851 //this.setSize(this.owner.wrap.getSize());
25852 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25859 * Protected method that will not generally be called directly. If you need/want
25860 * custom HTML cleanup, this is the method you should override.
25861 * @param {String} html The HTML to be cleaned
25862 * return {String} The cleaned HTML
25864 cleanHtml : function(html){
25865 html = String(html);
25866 if(html.length > 5){
25867 if(Roo.isSafari){ // strip safari nonsense
25868 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25871 if(html == ' '){
25878 * HTML Editor -> Textarea
25879 * Protected method that will not generally be called directly. Syncs the contents
25880 * of the editor iframe with the textarea.
25882 syncValue : function(){
25883 if(this.initialized){
25884 var bd = (this.doc.body || this.doc.documentElement);
25885 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25886 var html = bd.innerHTML;
25888 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25889 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25891 html = '<div style="'+m[0]+'">' + html + '</div>';
25894 html = this.cleanHtml(html);
25895 // fix up the special chars.. normaly like back quotes in word...
25896 // however we do not want to do this with chinese..
25897 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25899 var cc = match.charCodeAt();
25901 // Get the character value, handling surrogate pairs
25902 if (match.length == 2) {
25903 // It's a surrogate pair, calculate the Unicode code point
25904 var high = match.charCodeAt(0) - 0xD800;
25905 var low = match.charCodeAt(1) - 0xDC00;
25906 cc = (high * 0x400) + low + 0x10000;
25908 (cc >= 0x4E00 && cc < 0xA000 ) ||
25909 (cc >= 0x3400 && cc < 0x4E00 ) ||
25910 (cc >= 0xf900 && cc < 0xfb00 )
25915 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25916 return "&#" + cc + ";";
25923 if(this.owner.fireEvent('beforesync', this, html) !== false){
25924 this.el.dom.value = html;
25925 this.owner.fireEvent('sync', this, html);
25931 * Protected method that will not generally be called directly. Pushes the value of the textarea
25932 * into the iframe editor.
25934 pushValue : function(){
25935 if(this.initialized){
25936 var v = this.el.dom.value.trim();
25938 // if(v.length < 1){
25942 if(this.owner.fireEvent('beforepush', this, v) !== false){
25943 var d = (this.doc.body || this.doc.documentElement);
25945 this.cleanUpPaste();
25946 this.el.dom.value = d.innerHTML;
25947 this.owner.fireEvent('push', this, v);
25953 deferFocus : function(){
25954 this.focus.defer(10, this);
25958 focus : function(){
25959 if(this.win && !this.sourceEditMode){
25966 assignDocWin: function()
25968 var iframe = this.iframe;
25971 this.doc = iframe.contentWindow.document;
25972 this.win = iframe.contentWindow;
25974 // if (!Roo.get(this.frameId)) {
25977 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25978 // this.win = Roo.get(this.frameId).dom.contentWindow;
25980 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25984 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25985 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25990 initEditor : function(){
25991 //console.log("INIT EDITOR");
25992 this.assignDocWin();
25996 this.doc.designMode="on";
25998 this.doc.write(this.getDocMarkup());
26001 var dbody = (this.doc.body || this.doc.documentElement);
26002 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26003 // this copies styles from the containing element into thsi one..
26004 // not sure why we need all of this..
26005 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26007 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26008 //ss['background-attachment'] = 'fixed'; // w3c
26009 dbody.bgProperties = 'fixed'; // ie
26010 //Roo.DomHelper.applyStyles(dbody, ss);
26011 Roo.EventManager.on(this.doc, {
26012 //'mousedown': this.onEditorEvent,
26013 'mouseup': this.onEditorEvent,
26014 'dblclick': this.onEditorEvent,
26015 'click': this.onEditorEvent,
26016 'keyup': this.onEditorEvent,
26021 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26023 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26024 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26026 this.initialized = true;
26028 this.owner.fireEvent('initialize', this);
26033 onDestroy : function(){
26039 //for (var i =0; i < this.toolbars.length;i++) {
26040 // // fixme - ask toolbars for heights?
26041 // this.toolbars[i].onDestroy();
26044 //this.wrap.dom.innerHTML = '';
26045 //this.wrap.remove();
26050 onFirstFocus : function(){
26052 this.assignDocWin();
26055 this.activated = true;
26058 if(Roo.isGecko){ // prevent silly gecko errors
26060 var s = this.win.getSelection();
26061 if(!s.focusNode || s.focusNode.nodeType != 3){
26062 var r = s.getRangeAt(0);
26063 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26068 this.execCmd('useCSS', true);
26069 this.execCmd('styleWithCSS', false);
26072 this.owner.fireEvent('activate', this);
26076 adjustFont: function(btn){
26077 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26078 //if(Roo.isSafari){ // safari
26081 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26082 if(Roo.isSafari){ // safari
26083 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26084 v = (v < 10) ? 10 : v;
26085 v = (v > 48) ? 48 : v;
26086 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26091 v = Math.max(1, v+adjust);
26093 this.execCmd('FontSize', v );
26096 onEditorEvent : function(e)
26098 this.owner.fireEvent('editorevent', this, e);
26099 // this.updateToolbar();
26100 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26103 insertTag : function(tg)
26105 // could be a bit smarter... -> wrap the current selected tRoo..
26106 if (tg.toLowerCase() == 'span' ||
26107 tg.toLowerCase() == 'code' ||
26108 tg.toLowerCase() == 'sup' ||
26109 tg.toLowerCase() == 'sub'
26112 range = this.createRange(this.getSelection());
26113 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26114 wrappingNode.appendChild(range.extractContents());
26115 range.insertNode(wrappingNode);
26122 this.execCmd("formatblock", tg);
26126 insertText : function(txt)
26130 var range = this.createRange();
26131 range.deleteContents();
26132 //alert(Sender.getAttribute('label'));
26134 range.insertNode(this.doc.createTextNode(txt));
26140 * Executes a Midas editor command on the editor document and performs necessary focus and
26141 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26142 * @param {String} cmd The Midas command
26143 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26145 relayCmd : function(cmd, value){
26147 this.execCmd(cmd, value);
26148 this.owner.fireEvent('editorevent', this);
26149 //this.updateToolbar();
26150 this.owner.deferFocus();
26154 * Executes a Midas editor command directly on the editor document.
26155 * For visual commands, you should use {@link #relayCmd} instead.
26156 * <b>This should only be called after the editor is initialized.</b>
26157 * @param {String} cmd The Midas command
26158 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26160 execCmd : function(cmd, value){
26161 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26168 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26170 * @param {String} text | dom node..
26172 insertAtCursor : function(text)
26175 if(!this.activated){
26181 var r = this.doc.selection.createRange();
26192 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26196 // from jquery ui (MIT licenced)
26198 var win = this.win;
26200 if (win.getSelection && win.getSelection().getRangeAt) {
26201 range = win.getSelection().getRangeAt(0);
26202 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26203 range.insertNode(node);
26204 } else if (win.document.selection && win.document.selection.createRange) {
26205 // no firefox support
26206 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26207 win.document.selection.createRange().pasteHTML(txt);
26209 // no firefox support
26210 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26211 this.execCmd('InsertHTML', txt);
26220 mozKeyPress : function(e){
26222 var c = e.getCharCode(), cmd;
26225 c = String.fromCharCode(c).toLowerCase();
26239 this.cleanUpPaste.defer(100, this);
26247 e.preventDefault();
26255 fixKeys : function(){ // load time branching for fastest keydown performance
26257 return function(e){
26258 var k = e.getKey(), r;
26261 r = this.doc.selection.createRange();
26264 r.pasteHTML('    ');
26271 r = this.doc.selection.createRange();
26273 var target = r.parentElement();
26274 if(!target || target.tagName.toLowerCase() != 'li'){
26276 r.pasteHTML('<br />');
26282 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26283 this.cleanUpPaste.defer(100, this);
26289 }else if(Roo.isOpera){
26290 return function(e){
26291 var k = e.getKey();
26295 this.execCmd('InsertHTML','    ');
26298 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26299 this.cleanUpPaste.defer(100, this);
26304 }else if(Roo.isSafari){
26305 return function(e){
26306 var k = e.getKey();
26310 this.execCmd('InsertText','\t');
26314 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26315 this.cleanUpPaste.defer(100, this);
26323 getAllAncestors: function()
26325 var p = this.getSelectedNode();
26328 a.push(p); // push blank onto stack..
26329 p = this.getParentElement();
26333 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26337 a.push(this.doc.body);
26341 lastSelNode : false,
26344 getSelection : function()
26346 this.assignDocWin();
26347 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26350 getSelectedNode: function()
26352 // this may only work on Gecko!!!
26354 // should we cache this!!!!
26359 var range = this.createRange(this.getSelection()).cloneRange();
26362 var parent = range.parentElement();
26364 var testRange = range.duplicate();
26365 testRange.moveToElementText(parent);
26366 if (testRange.inRange(range)) {
26369 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26372 parent = parent.parentElement;
26377 // is ancestor a text element.
26378 var ac = range.commonAncestorContainer;
26379 if (ac.nodeType == 3) {
26380 ac = ac.parentNode;
26383 var ar = ac.childNodes;
26386 var other_nodes = [];
26387 var has_other_nodes = false;
26388 for (var i=0;i<ar.length;i++) {
26389 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26392 // fullly contained node.
26394 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26399 // probably selected..
26400 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26401 other_nodes.push(ar[i]);
26405 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26410 has_other_nodes = true;
26412 if (!nodes.length && other_nodes.length) {
26413 nodes= other_nodes;
26415 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26421 createRange: function(sel)
26423 // this has strange effects when using with
26424 // top toolbar - not sure if it's a great idea.
26425 //this.editor.contentWindow.focus();
26426 if (typeof sel != "undefined") {
26428 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26430 return this.doc.createRange();
26433 return this.doc.createRange();
26436 getParentElement: function()
26439 this.assignDocWin();
26440 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26442 var range = this.createRange(sel);
26445 var p = range.commonAncestorContainer;
26446 while (p.nodeType == 3) { // text node
26457 * Range intersection.. the hard stuff...
26461 * [ -- selected range --- ]
26465 * if end is before start or hits it. fail.
26466 * if start is after end or hits it fail.
26468 * if either hits (but other is outside. - then it's not
26474 // @see http://www.thismuchiknow.co.uk/?p=64.
26475 rangeIntersectsNode : function(range, node)
26477 var nodeRange = node.ownerDocument.createRange();
26479 nodeRange.selectNode(node);
26481 nodeRange.selectNodeContents(node);
26484 var rangeStartRange = range.cloneRange();
26485 rangeStartRange.collapse(true);
26487 var rangeEndRange = range.cloneRange();
26488 rangeEndRange.collapse(false);
26490 var nodeStartRange = nodeRange.cloneRange();
26491 nodeStartRange.collapse(true);
26493 var nodeEndRange = nodeRange.cloneRange();
26494 nodeEndRange.collapse(false);
26496 return rangeStartRange.compareBoundaryPoints(
26497 Range.START_TO_START, nodeEndRange) == -1 &&
26498 rangeEndRange.compareBoundaryPoints(
26499 Range.START_TO_START, nodeStartRange) == 1;
26503 rangeCompareNode : function(range, node)
26505 var nodeRange = node.ownerDocument.createRange();
26507 nodeRange.selectNode(node);
26509 nodeRange.selectNodeContents(node);
26513 range.collapse(true);
26515 nodeRange.collapse(true);
26517 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26518 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26520 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26522 var nodeIsBefore = ss == 1;
26523 var nodeIsAfter = ee == -1;
26525 if (nodeIsBefore && nodeIsAfter) {
26528 if (!nodeIsBefore && nodeIsAfter) {
26529 return 1; //right trailed.
26532 if (nodeIsBefore && !nodeIsAfter) {
26533 return 2; // left trailed.
26539 // private? - in a new class?
26540 cleanUpPaste : function()
26542 // cleans up the whole document..
26543 Roo.log('cleanuppaste');
26545 this.cleanUpChildren(this.doc.body);
26546 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26547 if (clean != this.doc.body.innerHTML) {
26548 this.doc.body.innerHTML = clean;
26553 cleanWordChars : function(input) {// change the chars to hex code
26554 var he = Roo.HtmlEditorCore;
26556 var output = input;
26557 Roo.each(he.swapCodes, function(sw) {
26558 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26560 output = output.replace(swapper, sw[1]);
26567 cleanUpChildren : function (n)
26569 if (!n.childNodes.length) {
26572 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26573 this.cleanUpChild(n.childNodes[i]);
26580 cleanUpChild : function (node)
26583 //console.log(node);
26584 if (node.nodeName == "#text") {
26585 // clean up silly Windows -- stuff?
26588 if (node.nodeName == "#comment") {
26589 if (!this.allowComments) {
26590 node.parentNode.removeChild(node);
26592 // clean up silly Windows -- stuff?
26595 var lcname = node.tagName.toLowerCase();
26596 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26597 // whitelist of tags..
26599 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26601 node.parentNode.removeChild(node);
26606 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26608 // spans with no attributes - just remove them..
26609 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26610 remove_keep_children = true;
26613 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26614 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26616 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26617 // remove_keep_children = true;
26620 if (remove_keep_children) {
26621 this.cleanUpChildren(node);
26622 // inserts everything just before this node...
26623 while (node.childNodes.length) {
26624 var cn = node.childNodes[0];
26625 node.removeChild(cn);
26626 node.parentNode.insertBefore(cn, node);
26628 node.parentNode.removeChild(node);
26632 if (!node.attributes || !node.attributes.length) {
26637 this.cleanUpChildren(node);
26641 function cleanAttr(n,v)
26644 if (v.match(/^\./) || v.match(/^\//)) {
26647 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26650 if (v.match(/^#/)) {
26653 if (v.match(/^\{/)) { // allow template editing.
26656 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26657 node.removeAttribute(n);
26661 var cwhite = this.cwhite;
26662 var cblack = this.cblack;
26664 function cleanStyle(n,v)
26666 if (v.match(/expression/)) { //XSS?? should we even bother..
26667 node.removeAttribute(n);
26671 var parts = v.split(/;/);
26674 Roo.each(parts, function(p) {
26675 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26679 var l = p.split(':').shift().replace(/\s+/g,'');
26680 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26682 if ( cwhite.length && cblack.indexOf(l) > -1) {
26683 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26684 //node.removeAttribute(n);
26688 // only allow 'c whitelisted system attributes'
26689 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26690 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26691 //node.removeAttribute(n);
26701 if (clean.length) {
26702 node.setAttribute(n, clean.join(';'));
26704 node.removeAttribute(n);
26710 for (var i = node.attributes.length-1; i > -1 ; i--) {
26711 var a = node.attributes[i];
26714 if (a.name.toLowerCase().substr(0,2)=='on') {
26715 node.removeAttribute(a.name);
26718 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26719 node.removeAttribute(a.name);
26722 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26723 cleanAttr(a.name,a.value); // fixme..
26726 if (a.name == 'style') {
26727 cleanStyle(a.name,a.value);
26730 /// clean up MS crap..
26731 // tecnically this should be a list of valid class'es..
26734 if (a.name == 'class') {
26735 if (a.value.match(/^Mso/)) {
26736 node.removeAttribute('class');
26739 if (a.value.match(/^body$/)) {
26740 node.removeAttribute('class');
26751 this.cleanUpChildren(node);
26757 * Clean up MS wordisms...
26759 cleanWord : function(node)
26762 this.cleanWord(this.doc.body);
26767 node.nodeName == 'SPAN' &&
26768 !node.hasAttributes() &&
26769 node.childNodes.length == 1 &&
26770 node.firstChild.nodeName == "#text"
26772 var textNode = node.firstChild;
26773 node.removeChild(textNode);
26774 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26775 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26777 node.parentNode.insertBefore(textNode, node);
26778 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26779 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26781 node.parentNode.removeChild(node);
26784 if (node.nodeName == "#text") {
26785 // clean up silly Windows -- stuff?
26788 if (node.nodeName == "#comment") {
26789 node.parentNode.removeChild(node);
26790 // clean up silly Windows -- stuff?
26794 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26795 node.parentNode.removeChild(node);
26798 //Roo.log(node.tagName);
26799 // remove - but keep children..
26800 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26801 //Roo.log('-- removed');
26802 while (node.childNodes.length) {
26803 var cn = node.childNodes[0];
26804 node.removeChild(cn);
26805 node.parentNode.insertBefore(cn, node);
26806 // move node to parent - and clean it..
26807 this.cleanWord(cn);
26809 node.parentNode.removeChild(node);
26810 /// no need to iterate chidlren = it's got none..
26811 //this.iterateChildren(node, this.cleanWord);
26815 if (node.className.length) {
26817 var cn = node.className.split(/\W+/);
26819 Roo.each(cn, function(cls) {
26820 if (cls.match(/Mso[a-zA-Z]+/)) {
26825 node.className = cna.length ? cna.join(' ') : '';
26827 node.removeAttribute("class");
26831 if (node.hasAttribute("lang")) {
26832 node.removeAttribute("lang");
26835 if (node.hasAttribute("style")) {
26837 var styles = node.getAttribute("style").split(";");
26839 Roo.each(styles, function(s) {
26840 if (!s.match(/:/)) {
26843 var kv = s.split(":");
26844 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26847 // what ever is left... we allow.
26850 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26851 if (!nstyle.length) {
26852 node.removeAttribute('style');
26855 this.iterateChildren(node, this.cleanWord);
26861 * iterateChildren of a Node, calling fn each time, using this as the scole..
26862 * @param {DomNode} node node to iterate children of.
26863 * @param {Function} fn method of this class to call on each item.
26865 iterateChildren : function(node, fn)
26867 if (!node.childNodes.length) {
26870 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26871 fn.call(this, node.childNodes[i])
26877 * cleanTableWidths.
26879 * Quite often pasting from word etc.. results in tables with column and widths.
26880 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26883 cleanTableWidths : function(node)
26888 this.cleanTableWidths(this.doc.body);
26893 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26896 Roo.log(node.tagName);
26897 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26898 this.iterateChildren(node, this.cleanTableWidths);
26901 if (node.hasAttribute('width')) {
26902 node.removeAttribute('width');
26906 if (node.hasAttribute("style")) {
26909 var styles = node.getAttribute("style").split(";");
26911 Roo.each(styles, function(s) {
26912 if (!s.match(/:/)) {
26915 var kv = s.split(":");
26916 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26919 // what ever is left... we allow.
26922 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26923 if (!nstyle.length) {
26924 node.removeAttribute('style');
26928 this.iterateChildren(node, this.cleanTableWidths);
26936 domToHTML : function(currentElement, depth, nopadtext) {
26938 depth = depth || 0;
26939 nopadtext = nopadtext || false;
26941 if (!currentElement) {
26942 return this.domToHTML(this.doc.body);
26945 //Roo.log(currentElement);
26947 var allText = false;
26948 var nodeName = currentElement.nodeName;
26949 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26951 if (nodeName == '#text') {
26953 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26958 if (nodeName != 'BODY') {
26961 // Prints the node tagName, such as <A>, <IMG>, etc
26964 for(i = 0; i < currentElement.attributes.length;i++) {
26966 var aname = currentElement.attributes.item(i).name;
26967 if (!currentElement.attributes.item(i).value.length) {
26970 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26973 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26982 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26985 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26990 // Traverse the tree
26992 var currentElementChild = currentElement.childNodes.item(i);
26993 var allText = true;
26994 var innerHTML = '';
26996 while (currentElementChild) {
26997 // Formatting code (indent the tree so it looks nice on the screen)
26998 var nopad = nopadtext;
26999 if (lastnode == 'SPAN') {
27003 if (currentElementChild.nodeName == '#text') {
27004 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27005 toadd = nopadtext ? toadd : toadd.trim();
27006 if (!nopad && toadd.length > 80) {
27007 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27009 innerHTML += toadd;
27012 currentElementChild = currentElement.childNodes.item(i);
27018 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27020 // Recursively traverse the tree structure of the child node
27021 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27022 lastnode = currentElementChild.nodeName;
27024 currentElementChild=currentElement.childNodes.item(i);
27030 // The remaining code is mostly for formatting the tree
27031 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27036 ret+= "</"+tagName+">";
27042 applyBlacklists : function()
27044 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27045 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27049 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27050 if (b.indexOf(tag) > -1) {
27053 this.white.push(tag);
27057 Roo.each(w, function(tag) {
27058 if (b.indexOf(tag) > -1) {
27061 if (this.white.indexOf(tag) > -1) {
27064 this.white.push(tag);
27069 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27070 if (w.indexOf(tag) > -1) {
27073 this.black.push(tag);
27077 Roo.each(b, function(tag) {
27078 if (w.indexOf(tag) > -1) {
27081 if (this.black.indexOf(tag) > -1) {
27084 this.black.push(tag);
27089 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27090 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27094 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27095 if (b.indexOf(tag) > -1) {
27098 this.cwhite.push(tag);
27102 Roo.each(w, function(tag) {
27103 if (b.indexOf(tag) > -1) {
27106 if (this.cwhite.indexOf(tag) > -1) {
27109 this.cwhite.push(tag);
27114 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27115 if (w.indexOf(tag) > -1) {
27118 this.cblack.push(tag);
27122 Roo.each(b, function(tag) {
27123 if (w.indexOf(tag) > -1) {
27126 if (this.cblack.indexOf(tag) > -1) {
27129 this.cblack.push(tag);
27134 setStylesheets : function(stylesheets)
27136 if(typeof(stylesheets) == 'string'){
27137 Roo.get(this.iframe.contentDocument.head).createChild({
27139 rel : 'stylesheet',
27148 Roo.each(stylesheets, function(s) {
27153 Roo.get(_this.iframe.contentDocument.head).createChild({
27155 rel : 'stylesheet',
27164 removeStylesheets : function()
27168 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27173 setStyle : function(style)
27175 Roo.get(this.iframe.contentDocument.head).createChild({
27184 // hide stuff that is not compatible
27198 * @event specialkey
27202 * @cfg {String} fieldClass @hide
27205 * @cfg {String} focusClass @hide
27208 * @cfg {String} autoCreate @hide
27211 * @cfg {String} inputType @hide
27214 * @cfg {String} invalidClass @hide
27217 * @cfg {String} invalidText @hide
27220 * @cfg {String} msgFx @hide
27223 * @cfg {String} validateOnBlur @hide
27227 Roo.HtmlEditorCore.white = [
27228 'area', 'br', 'img', 'input', 'hr', 'wbr',
27230 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27231 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27232 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27233 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27234 'table', 'ul', 'xmp',
27236 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27239 'dir', 'menu', 'ol', 'ul', 'dl',
27245 Roo.HtmlEditorCore.black = [
27246 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27248 'base', 'basefont', 'bgsound', 'blink', 'body',
27249 'frame', 'frameset', 'head', 'html', 'ilayer',
27250 'iframe', 'layer', 'link', 'meta', 'object',
27251 'script', 'style' ,'title', 'xml' // clean later..
27253 Roo.HtmlEditorCore.clean = [
27254 'script', 'style', 'title', 'xml'
27256 Roo.HtmlEditorCore.remove = [
27261 Roo.HtmlEditorCore.ablack = [
27265 Roo.HtmlEditorCore.aclean = [
27266 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27270 Roo.HtmlEditorCore.pwhite= [
27271 'http', 'https', 'mailto'
27274 // white listed style attributes.
27275 Roo.HtmlEditorCore.cwhite= [
27276 // 'text-align', /// default is to allow most things..
27282 // black listed style attributes.
27283 Roo.HtmlEditorCore.cblack= [
27284 // 'font-size' -- this can be set by the project
27288 Roo.HtmlEditorCore.swapCodes =[
27289 [ 8211, "–" ],
27290 [ 8212, "—" ],
27307 * @class Roo.bootstrap.HtmlEditor
27308 * @extends Roo.bootstrap.TextArea
27309 * Bootstrap HtmlEditor class
27312 * Create a new HtmlEditor
27313 * @param {Object} config The config object
27316 Roo.bootstrap.HtmlEditor = function(config){
27317 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27318 if (!this.toolbars) {
27319 this.toolbars = [];
27322 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27325 * @event initialize
27326 * Fires when the editor is fully initialized (including the iframe)
27327 * @param {HtmlEditor} this
27332 * Fires when the editor is first receives the focus. Any insertion must wait
27333 * until after this event.
27334 * @param {HtmlEditor} this
27338 * @event beforesync
27339 * Fires before the textarea is updated with content from the editor iframe. Return false
27340 * to cancel the sync.
27341 * @param {HtmlEditor} this
27342 * @param {String} html
27346 * @event beforepush
27347 * Fires before the iframe editor is updated with content from the textarea. Return false
27348 * to cancel the push.
27349 * @param {HtmlEditor} this
27350 * @param {String} html
27355 * Fires when the textarea is updated with content from the editor iframe.
27356 * @param {HtmlEditor} this
27357 * @param {String} html
27362 * Fires when the iframe editor is updated with content from the textarea.
27363 * @param {HtmlEditor} this
27364 * @param {String} html
27368 * @event editmodechange
27369 * Fires when the editor switches edit modes
27370 * @param {HtmlEditor} this
27371 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27373 editmodechange: true,
27375 * @event editorevent
27376 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27377 * @param {HtmlEditor} this
27381 * @event firstfocus
27382 * Fires when on first focus - needed by toolbars..
27383 * @param {HtmlEditor} this
27388 * Auto save the htmlEditor value as a file into Events
27389 * @param {HtmlEditor} this
27393 * @event savedpreview
27394 * preview the saved version of htmlEditor
27395 * @param {HtmlEditor} this
27402 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27406 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27411 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27416 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27421 * @cfg {Number} height (in pixels)
27425 * @cfg {Number} width (in pixels)
27430 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27433 stylesheets: false,
27438 // private properties
27439 validationEvent : false,
27441 initialized : false,
27444 onFocus : Roo.emptyFn,
27446 hideMode:'offsets',
27448 tbContainer : false,
27452 toolbarContainer :function() {
27453 return this.wrap.select('.x-html-editor-tb',true).first();
27457 * Protected method that will not generally be called directly. It
27458 * is called when the editor creates its toolbar. Override this method if you need to
27459 * add custom toolbar buttons.
27460 * @param {HtmlEditor} editor
27462 createToolbar : function(){
27463 Roo.log('renewing');
27464 Roo.log("create toolbars");
27466 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27467 this.toolbars[0].render(this.toolbarContainer());
27471 // if (!editor.toolbars || !editor.toolbars.length) {
27472 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27475 // for (var i =0 ; i < editor.toolbars.length;i++) {
27476 // editor.toolbars[i] = Roo.factory(
27477 // typeof(editor.toolbars[i]) == 'string' ?
27478 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27479 // Roo.bootstrap.HtmlEditor);
27480 // editor.toolbars[i].init(editor);
27486 onRender : function(ct, position)
27488 // Roo.log("Call onRender: " + this.xtype);
27490 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27492 this.wrap = this.inputEl().wrap({
27493 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27496 this.editorcore.onRender(ct, position);
27498 if (this.resizable) {
27499 this.resizeEl = new Roo.Resizable(this.wrap, {
27503 minHeight : this.height,
27504 height: this.height,
27505 handles : this.resizable,
27508 resize : function(r, w, h) {
27509 _t.onResize(w,h); // -something
27515 this.createToolbar(this);
27518 if(!this.width && this.resizable){
27519 this.setSize(this.wrap.getSize());
27521 if (this.resizeEl) {
27522 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27523 // should trigger onReize..
27529 onResize : function(w, h)
27531 Roo.log('resize: ' +w + ',' + h );
27532 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27536 if(this.inputEl() ){
27537 if(typeof w == 'number'){
27538 var aw = w - this.wrap.getFrameWidth('lr');
27539 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27542 if(typeof h == 'number'){
27543 var tbh = -11; // fixme it needs to tool bar size!
27544 for (var i =0; i < this.toolbars.length;i++) {
27545 // fixme - ask toolbars for heights?
27546 tbh += this.toolbars[i].el.getHeight();
27547 //if (this.toolbars[i].footer) {
27548 // tbh += this.toolbars[i].footer.el.getHeight();
27556 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27557 ah -= 5; // knock a few pixes off for look..
27558 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27562 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27563 this.editorcore.onResize(ew,eh);
27568 * Toggles the editor between standard and source edit mode.
27569 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27571 toggleSourceEdit : function(sourceEditMode)
27573 this.editorcore.toggleSourceEdit(sourceEditMode);
27575 if(this.editorcore.sourceEditMode){
27576 Roo.log('editor - showing textarea');
27579 // Roo.log(this.syncValue());
27581 this.inputEl().removeClass(['hide', 'x-hidden']);
27582 this.inputEl().dom.removeAttribute('tabIndex');
27583 this.inputEl().focus();
27585 Roo.log('editor - hiding textarea');
27587 // Roo.log(this.pushValue());
27590 this.inputEl().addClass(['hide', 'x-hidden']);
27591 this.inputEl().dom.setAttribute('tabIndex', -1);
27592 //this.deferFocus();
27595 if(this.resizable){
27596 this.setSize(this.wrap.getSize());
27599 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27602 // private (for BoxComponent)
27603 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27605 // private (for BoxComponent)
27606 getResizeEl : function(){
27610 // private (for BoxComponent)
27611 getPositionEl : function(){
27616 initEvents : function(){
27617 this.originalValue = this.getValue();
27621 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27624 // markInvalid : Roo.emptyFn,
27626 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27629 // clearInvalid : Roo.emptyFn,
27631 setValue : function(v){
27632 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27633 this.editorcore.pushValue();
27638 deferFocus : function(){
27639 this.focus.defer(10, this);
27643 focus : function(){
27644 this.editorcore.focus();
27650 onDestroy : function(){
27656 for (var i =0; i < this.toolbars.length;i++) {
27657 // fixme - ask toolbars for heights?
27658 this.toolbars[i].onDestroy();
27661 this.wrap.dom.innerHTML = '';
27662 this.wrap.remove();
27667 onFirstFocus : function(){
27668 //Roo.log("onFirstFocus");
27669 this.editorcore.onFirstFocus();
27670 for (var i =0; i < this.toolbars.length;i++) {
27671 this.toolbars[i].onFirstFocus();
27677 syncValue : function()
27679 this.editorcore.syncValue();
27682 pushValue : function()
27684 this.editorcore.pushValue();
27688 // hide stuff that is not compatible
27702 * @event specialkey
27706 * @cfg {String} fieldClass @hide
27709 * @cfg {String} focusClass @hide
27712 * @cfg {String} autoCreate @hide
27715 * @cfg {String} inputType @hide
27719 * @cfg {String} invalidText @hide
27722 * @cfg {String} msgFx @hide
27725 * @cfg {String} validateOnBlur @hide
27734 Roo.namespace('Roo.bootstrap.htmleditor');
27736 * @class Roo.bootstrap.HtmlEditorToolbar1
27742 new Roo.bootstrap.HtmlEditor({
27745 new Roo.bootstrap.HtmlEditorToolbar1({
27746 disable : { fonts: 1 , format: 1, ..., ... , ...],
27752 * @cfg {Object} disable List of elements to disable..
27753 * @cfg {Array} btns List of additional buttons.
27757 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27760 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27763 Roo.apply(this, config);
27765 // default disabled, based on 'good practice'..
27766 this.disable = this.disable || {};
27767 Roo.applyIf(this.disable, {
27770 specialElements : true
27772 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27774 this.editor = config.editor;
27775 this.editorcore = config.editor.editorcore;
27777 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27779 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27780 // dont call parent... till later.
27782 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27787 editorcore : false,
27792 "h1","h2","h3","h4","h5","h6",
27794 "abbr", "acronym", "address", "cite", "samp", "var",
27798 onRender : function(ct, position)
27800 // Roo.log("Call onRender: " + this.xtype);
27802 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27804 this.el.dom.style.marginBottom = '0';
27806 var editorcore = this.editorcore;
27807 var editor= this.editor;
27810 var btn = function(id,cmd , toggle, handler, html){
27812 var event = toggle ? 'toggle' : 'click';
27817 xns: Roo.bootstrap,
27821 enableToggle:toggle !== false,
27823 pressed : toggle ? false : null,
27826 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27827 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27833 // var cb_box = function...
27838 xns: Roo.bootstrap,
27843 xns: Roo.bootstrap,
27847 Roo.each(this.formats, function(f) {
27848 style.menu.items.push({
27850 xns: Roo.bootstrap,
27851 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27856 editorcore.insertTag(this.tagname);
27863 children.push(style);
27865 btn('bold',false,true);
27866 btn('italic',false,true);
27867 btn('align-left', 'justifyleft',true);
27868 btn('align-center', 'justifycenter',true);
27869 btn('align-right' , 'justifyright',true);
27870 btn('link', false, false, function(btn) {
27871 //Roo.log("create link?");
27872 var url = prompt(this.createLinkText, this.defaultLinkValue);
27873 if(url && url != 'http:/'+'/'){
27874 this.editorcore.relayCmd('createlink', url);
27877 btn('list','insertunorderedlist',true);
27878 btn('pencil', false,true, function(btn){
27880 this.toggleSourceEdit(btn.pressed);
27883 if (this.editor.btns.length > 0) {
27884 for (var i = 0; i<this.editor.btns.length; i++) {
27885 children.push(this.editor.btns[i]);
27893 xns: Roo.bootstrap,
27898 xns: Roo.bootstrap,
27903 cog.menu.items.push({
27905 xns: Roo.bootstrap,
27906 html : Clean styles,
27911 editorcore.insertTag(this.tagname);
27920 this.xtype = 'NavSimplebar';
27922 for(var i=0;i< children.length;i++) {
27924 this.buttons.add(this.addxtypeChild(children[i]));
27928 editor.on('editorevent', this.updateToolbar, this);
27930 onBtnClick : function(id)
27932 this.editorcore.relayCmd(id);
27933 this.editorcore.focus();
27937 * Protected method that will not generally be called directly. It triggers
27938 * a toolbar update by reading the markup state of the current selection in the editor.
27940 updateToolbar: function(){
27942 if(!this.editorcore.activated){
27943 this.editor.onFirstFocus(); // is this neeed?
27947 var btns = this.buttons;
27948 var doc = this.editorcore.doc;
27949 btns.get('bold').setActive(doc.queryCommandState('bold'));
27950 btns.get('italic').setActive(doc.queryCommandState('italic'));
27951 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27953 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27954 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27955 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27957 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27958 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27961 var ans = this.editorcore.getAllAncestors();
27962 if (this.formatCombo) {
27965 var store = this.formatCombo.store;
27966 this.formatCombo.setValue("");
27967 for (var i =0; i < ans.length;i++) {
27968 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27970 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27978 // hides menus... - so this cant be on a menu...
27979 Roo.bootstrap.MenuMgr.hideAll();
27981 Roo.bootstrap.menu.Manager.hideAll();
27982 //this.editorsyncValue();
27984 onFirstFocus: function() {
27985 this.buttons.each(function(item){
27989 toggleSourceEdit : function(sourceEditMode){
27992 if(sourceEditMode){
27993 Roo.log("disabling buttons");
27994 this.buttons.each( function(item){
27995 if(item.cmd != 'pencil'){
28001 Roo.log("enabling buttons");
28002 if(this.editorcore.initialized){
28003 this.buttons.each( function(item){
28009 Roo.log("calling toggole on editor");
28010 // tell the editor that it's been pressed..
28011 this.editor.toggleSourceEdit(sourceEditMode);
28025 * @class Roo.bootstrap.Markdown
28026 * @extends Roo.bootstrap.TextArea
28027 * Bootstrap Showdown editable area
28028 * @cfg {string} content
28031 * Create a new Showdown
28034 Roo.bootstrap.Markdown = function(config){
28035 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28039 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28043 initEvents : function()
28046 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28047 this.markdownEl = this.el.createChild({
28048 cls : 'roo-markdown-area'
28050 this.inputEl().addClass('d-none');
28051 if (this.getValue() == '') {
28052 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28055 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28057 this.markdownEl.on('click', this.toggleTextEdit, this);
28058 this.on('blur', this.toggleTextEdit, this);
28059 this.on('specialkey', this.resizeTextArea, this);
28062 toggleTextEdit : function()
28064 var sh = this.markdownEl.getHeight();
28065 this.inputEl().addClass('d-none');
28066 this.markdownEl.addClass('d-none');
28067 if (!this.editing) {
28069 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28070 this.inputEl().removeClass('d-none');
28071 this.inputEl().focus();
28072 this.editing = true;
28075 // show showdown...
28076 this.updateMarkdown();
28077 this.markdownEl.removeClass('d-none');
28078 this.editing = false;
28081 updateMarkdown : function()
28083 if (this.getValue() == '') {
28084 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28088 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28091 resizeTextArea: function () {
28094 Roo.log([sh, this.getValue().split("\n").length * 30]);
28095 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28097 setValue : function(val)
28099 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28100 if (!this.editing) {
28101 this.updateMarkdown();
28107 if (!this.editing) {
28108 this.toggleTextEdit();
28116 * Ext JS Library 1.1.1
28117 * Copyright(c) 2006-2007, Ext JS, LLC.
28119 * Originally Released Under LGPL - original licence link has changed is not relivant.
28122 * <script type="text/javascript">
28126 * @class Roo.bootstrap.PagingToolbar
28127 * @extends Roo.bootstrap.NavSimplebar
28128 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28130 * Create a new PagingToolbar
28131 * @param {Object} config The config object
28132 * @param {Roo.data.Store} store
28134 Roo.bootstrap.PagingToolbar = function(config)
28136 // old args format still supported... - xtype is prefered..
28137 // created from xtype...
28139 this.ds = config.dataSource;
28141 if (config.store && !this.ds) {
28142 this.store= Roo.factory(config.store, Roo.data);
28143 this.ds = this.store;
28144 this.ds.xmodule = this.xmodule || false;
28147 this.toolbarItems = [];
28148 if (config.items) {
28149 this.toolbarItems = config.items;
28152 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28157 this.bind(this.ds);
28160 if (Roo.bootstrap.version == 4) {
28161 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28163 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28168 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28170 * @cfg {Roo.bootstrap.Button} buttons[]
28171 * Buttons for the toolbar
28174 * @cfg {Roo.data.Store} store
28175 * The underlying data store providing the paged data
28178 * @cfg {String/HTMLElement/Element} container
28179 * container The id or element that will contain the toolbar
28182 * @cfg {Boolean} displayInfo
28183 * True to display the displayMsg (defaults to false)
28186 * @cfg {Number} pageSize
28187 * The number of records to display per page (defaults to 20)
28191 * @cfg {String} displayMsg
28192 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28194 displayMsg : 'Displaying {0} - {1} of {2}',
28196 * @cfg {String} emptyMsg
28197 * The message to display when no records are found (defaults to "No data to display")
28199 emptyMsg : 'No data to display',
28201 * Customizable piece of the default paging text (defaults to "Page")
28204 beforePageText : "Page",
28206 * Customizable piece of the default paging text (defaults to "of %0")
28209 afterPageText : "of {0}",
28211 * Customizable piece of the default paging text (defaults to "First Page")
28214 firstText : "First Page",
28216 * Customizable piece of the default paging text (defaults to "Previous Page")
28219 prevText : "Previous Page",
28221 * Customizable piece of the default paging text (defaults to "Next Page")
28224 nextText : "Next Page",
28226 * Customizable piece of the default paging text (defaults to "Last Page")
28229 lastText : "Last Page",
28231 * Customizable piece of the default paging text (defaults to "Refresh")
28234 refreshText : "Refresh",
28238 onRender : function(ct, position)
28240 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28241 this.navgroup.parentId = this.id;
28242 this.navgroup.onRender(this.el, null);
28243 // add the buttons to the navgroup
28245 if(this.displayInfo){
28246 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28247 this.displayEl = this.el.select('.x-paging-info', true).first();
28248 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28249 // this.displayEl = navel.el.select('span',true).first();
28255 Roo.each(_this.buttons, function(e){ // this might need to use render????
28256 Roo.factory(e).render(_this.el);
28260 Roo.each(_this.toolbarItems, function(e) {
28261 _this.navgroup.addItem(e);
28265 this.first = this.navgroup.addItem({
28266 tooltip: this.firstText,
28267 cls: "prev btn-outline-secondary",
28268 html : ' <i class="fa fa-step-backward"></i>',
28270 preventDefault: true,
28271 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28274 this.prev = this.navgroup.addItem({
28275 tooltip: this.prevText,
28276 cls: "prev btn-outline-secondary",
28277 html : ' <i class="fa fa-backward"></i>',
28279 preventDefault: true,
28280 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28282 //this.addSeparator();
28285 var field = this.navgroup.addItem( {
28287 cls : 'x-paging-position btn-outline-secondary',
28289 html : this.beforePageText +
28290 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28291 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28294 this.field = field.el.select('input', true).first();
28295 this.field.on("keydown", this.onPagingKeydown, this);
28296 this.field.on("focus", function(){this.dom.select();});
28299 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28300 //this.field.setHeight(18);
28301 //this.addSeparator();
28302 this.next = this.navgroup.addItem({
28303 tooltip: this.nextText,
28304 cls: "next btn-outline-secondary",
28305 html : ' <i class="fa fa-forward"></i>',
28307 preventDefault: true,
28308 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28310 this.last = this.navgroup.addItem({
28311 tooltip: this.lastText,
28312 html : ' <i class="fa fa-step-forward"></i>',
28313 cls: "next btn-outline-secondary",
28315 preventDefault: true,
28316 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28318 //this.addSeparator();
28319 this.loading = this.navgroup.addItem({
28320 tooltip: this.refreshText,
28321 cls: "btn-outline-secondary",
28322 html : ' <i class="fa fa-refresh"></i>',
28323 preventDefault: true,
28324 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28330 updateInfo : function(){
28331 if(this.displayEl){
28332 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28333 var msg = count == 0 ?
28337 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28339 this.displayEl.update(msg);
28344 onLoad : function(ds, r, o)
28346 this.cursor = o.params && o.params.start ? o.params.start : 0;
28348 var d = this.getPageData(),
28353 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28354 this.field.dom.value = ap;
28355 this.first.setDisabled(ap == 1);
28356 this.prev.setDisabled(ap == 1);
28357 this.next.setDisabled(ap == ps);
28358 this.last.setDisabled(ap == ps);
28359 this.loading.enable();
28364 getPageData : function(){
28365 var total = this.ds.getTotalCount();
28368 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28369 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28374 onLoadError : function(){
28375 this.loading.enable();
28379 onPagingKeydown : function(e){
28380 var k = e.getKey();
28381 var d = this.getPageData();
28383 var v = this.field.dom.value, pageNum;
28384 if(!v || isNaN(pageNum = parseInt(v, 10))){
28385 this.field.dom.value = d.activePage;
28388 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28389 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28392 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))
28394 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28395 this.field.dom.value = pageNum;
28396 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28399 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28401 var v = this.field.dom.value, pageNum;
28402 var increment = (e.shiftKey) ? 10 : 1;
28403 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28406 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28407 this.field.dom.value = d.activePage;
28410 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28412 this.field.dom.value = parseInt(v, 10) + increment;
28413 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28414 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28421 beforeLoad : function(){
28423 this.loading.disable();
28428 onClick : function(which){
28437 ds.load({params:{start: 0, limit: this.pageSize}});
28440 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28443 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28446 var total = ds.getTotalCount();
28447 var extra = total % this.pageSize;
28448 var lastStart = extra ? (total - extra) : total-this.pageSize;
28449 ds.load({params:{start: lastStart, limit: this.pageSize}});
28452 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28458 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28459 * @param {Roo.data.Store} store The data store to unbind
28461 unbind : function(ds){
28462 ds.un("beforeload", this.beforeLoad, this);
28463 ds.un("load", this.onLoad, this);
28464 ds.un("loadexception", this.onLoadError, this);
28465 ds.un("remove", this.updateInfo, this);
28466 ds.un("add", this.updateInfo, this);
28467 this.ds = undefined;
28471 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28472 * @param {Roo.data.Store} store The data store to bind
28474 bind : function(ds){
28475 ds.on("beforeload", this.beforeLoad, this);
28476 ds.on("load", this.onLoad, this);
28477 ds.on("loadexception", this.onLoadError, this);
28478 ds.on("remove", this.updateInfo, this);
28479 ds.on("add", this.updateInfo, this);
28490 * @class Roo.bootstrap.MessageBar
28491 * @extends Roo.bootstrap.Component
28492 * Bootstrap MessageBar class
28493 * @cfg {String} html contents of the MessageBar
28494 * @cfg {String} weight (info | success | warning | danger) default info
28495 * @cfg {String} beforeClass insert the bar before the given class
28496 * @cfg {Boolean} closable (true | false) default false
28497 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28500 * Create a new Element
28501 * @param {Object} config The config object
28504 Roo.bootstrap.MessageBar = function(config){
28505 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28508 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28514 beforeClass: 'bootstrap-sticky-wrap',
28516 getAutoCreate : function(){
28520 cls: 'alert alert-dismissable alert-' + this.weight,
28525 html: this.html || ''
28531 cfg.cls += ' alert-messages-fixed';
28545 onRender : function(ct, position)
28547 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28550 var cfg = Roo.apply({}, this.getAutoCreate());
28554 cfg.cls += ' ' + this.cls;
28557 cfg.style = this.style;
28559 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28561 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28564 this.el.select('>button.close').on('click', this.hide, this);
28570 if (!this.rendered) {
28576 this.fireEvent('show', this);
28582 if (!this.rendered) {
28588 this.fireEvent('hide', this);
28591 update : function()
28593 // var e = this.el.dom.firstChild;
28595 // if(this.closable){
28596 // e = e.nextSibling;
28599 // e.data = this.html || '';
28601 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28617 * @class Roo.bootstrap.Graph
28618 * @extends Roo.bootstrap.Component
28619 * Bootstrap Graph class
28623 @cfg {String} graphtype bar | vbar | pie
28624 @cfg {number} g_x coodinator | centre x (pie)
28625 @cfg {number} g_y coodinator | centre y (pie)
28626 @cfg {number} g_r radius (pie)
28627 @cfg {number} g_height height of the chart (respected by all elements in the set)
28628 @cfg {number} g_width width of the chart (respected by all elements in the set)
28629 @cfg {Object} title The title of the chart
28632 -opts (object) options for the chart
28634 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28635 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28637 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.
28638 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28640 o stretch (boolean)
28642 -opts (object) options for the pie
28645 o startAngle (number)
28646 o endAngle (number)
28650 * Create a new Input
28651 * @param {Object} config The config object
28654 Roo.bootstrap.Graph = function(config){
28655 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28661 * The img click event for the img.
28662 * @param {Roo.EventObject} e
28668 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28679 //g_colors: this.colors,
28686 getAutoCreate : function(){
28697 onRender : function(ct,position){
28700 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28702 if (typeof(Raphael) == 'undefined') {
28703 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28707 this.raphael = Raphael(this.el.dom);
28709 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28710 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28711 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28712 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28714 r.text(160, 10, "Single Series Chart").attr(txtattr);
28715 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28716 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28717 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28719 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28720 r.barchart(330, 10, 300, 220, data1);
28721 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28722 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28725 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28726 // r.barchart(30, 30, 560, 250, xdata, {
28727 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28728 // axis : "0 0 1 1",
28729 // axisxlabels : xdata
28730 // //yvalues : cols,
28733 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28735 // this.load(null,xdata,{
28736 // axis : "0 0 1 1",
28737 // axisxlabels : xdata
28742 load : function(graphtype,xdata,opts)
28744 this.raphael.clear();
28746 graphtype = this.graphtype;
28751 var r = this.raphael,
28752 fin = function () {
28753 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28755 fout = function () {
28756 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28758 pfin = function() {
28759 this.sector.stop();
28760 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28763 this.label[0].stop();
28764 this.label[0].attr({ r: 7.5 });
28765 this.label[1].attr({ "font-weight": 800 });
28768 pfout = function() {
28769 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28772 this.label[0].animate({ r: 5 }, 500, "bounce");
28773 this.label[1].attr({ "font-weight": 400 });
28779 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28782 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28785 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28786 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28788 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28795 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28800 setTitle: function(o)
28805 initEvents: function() {
28808 this.el.on('click', this.onClick, this);
28812 onClick : function(e)
28814 Roo.log('img onclick');
28815 this.fireEvent('click', this, e);
28827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28830 * @class Roo.bootstrap.dash.NumberBox
28831 * @extends Roo.bootstrap.Component
28832 * Bootstrap NumberBox class
28833 * @cfg {String} headline Box headline
28834 * @cfg {String} content Box content
28835 * @cfg {String} icon Box icon
28836 * @cfg {String} footer Footer text
28837 * @cfg {String} fhref Footer href
28840 * Create a new NumberBox
28841 * @param {Object} config The config object
28845 Roo.bootstrap.dash.NumberBox = function(config){
28846 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28850 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28859 getAutoCreate : function(){
28863 cls : 'small-box ',
28871 cls : 'roo-headline',
28872 html : this.headline
28876 cls : 'roo-content',
28877 html : this.content
28891 cls : 'ion ' + this.icon
28900 cls : 'small-box-footer',
28901 href : this.fhref || '#',
28905 cfg.cn.push(footer);
28912 onRender : function(ct,position){
28913 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28920 setHeadline: function (value)
28922 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28925 setFooter: function (value, href)
28927 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28930 this.el.select('a.small-box-footer',true).first().attr('href', href);
28935 setContent: function (value)
28937 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28940 initEvents: function()
28954 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28957 * @class Roo.bootstrap.dash.TabBox
28958 * @extends Roo.bootstrap.Component
28959 * Bootstrap TabBox class
28960 * @cfg {String} title Title of the TabBox
28961 * @cfg {String} icon Icon of the TabBox
28962 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28963 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28966 * Create a new TabBox
28967 * @param {Object} config The config object
28971 Roo.bootstrap.dash.TabBox = function(config){
28972 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28977 * When a pane is added
28978 * @param {Roo.bootstrap.dash.TabPane} pane
28982 * @event activatepane
28983 * When a pane is activated
28984 * @param {Roo.bootstrap.dash.TabPane} pane
28986 "activatepane" : true
28994 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28999 tabScrollable : false,
29001 getChildContainer : function()
29003 return this.el.select('.tab-content', true).first();
29006 getAutoCreate : function(){
29010 cls: 'pull-left header',
29018 cls: 'fa ' + this.icon
29024 cls: 'nav nav-tabs pull-right',
29030 if(this.tabScrollable){
29037 cls: 'nav nav-tabs pull-right',
29048 cls: 'nav-tabs-custom',
29053 cls: 'tab-content no-padding',
29061 initEvents : function()
29063 //Roo.log('add add pane handler');
29064 this.on('addpane', this.onAddPane, this);
29067 * Updates the box title
29068 * @param {String} html to set the title to.
29070 setTitle : function(value)
29072 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29074 onAddPane : function(pane)
29076 this.panes.push(pane);
29077 //Roo.log('addpane');
29079 // tabs are rendere left to right..
29080 if(!this.showtabs){
29084 var ctr = this.el.select('.nav-tabs', true).first();
29087 var existing = ctr.select('.nav-tab',true);
29088 var qty = existing.getCount();;
29091 var tab = ctr.createChild({
29093 cls : 'nav-tab' + (qty ? '' : ' active'),
29101 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29104 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29106 pane.el.addClass('active');
29111 onTabClick : function(ev,un,ob,pane)
29113 //Roo.log('tab - prev default');
29114 ev.preventDefault();
29117 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29118 pane.tab.addClass('active');
29119 //Roo.log(pane.title);
29120 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29121 // technically we should have a deactivate event.. but maybe add later.
29122 // and it should not de-activate the selected tab...
29123 this.fireEvent('activatepane', pane);
29124 pane.el.addClass('active');
29125 pane.fireEvent('activate');
29130 getActivePane : function()
29133 Roo.each(this.panes, function(p) {
29134 if(p.el.hasClass('active')){
29155 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29157 * @class Roo.bootstrap.TabPane
29158 * @extends Roo.bootstrap.Component
29159 * Bootstrap TabPane class
29160 * @cfg {Boolean} active (false | true) Default false
29161 * @cfg {String} title title of panel
29165 * Create a new TabPane
29166 * @param {Object} config The config object
29169 Roo.bootstrap.dash.TabPane = function(config){
29170 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29176 * When a pane is activated
29177 * @param {Roo.bootstrap.dash.TabPane} pane
29184 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29189 // the tabBox that this is attached to.
29192 getAutoCreate : function()
29200 cfg.cls += ' active';
29205 initEvents : function()
29207 //Roo.log('trigger add pane handler');
29208 this.parent().fireEvent('addpane', this)
29212 * Updates the tab title
29213 * @param {String} html to set the title to.
29215 setTitle: function(str)
29221 this.tab.select('a', true).first().dom.innerHTML = str;
29240 * @class Roo.bootstrap.Tooltip
29241 * Bootstrap Tooltip class
29242 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29243 * to determine which dom element triggers the tooltip.
29245 * It needs to add support for additional attributes like tooltip-position
29248 * Create a new Toolti
29249 * @param {Object} config The config object
29252 Roo.bootstrap.Tooltip = function(config){
29253 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29255 this.alignment = Roo.bootstrap.Tooltip.alignment;
29257 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29258 this.alignment = config.alignment;
29263 Roo.apply(Roo.bootstrap.Tooltip, {
29265 * @function init initialize tooltip monitoring.
29269 currentTip : false,
29270 currentRegion : false,
29276 Roo.get(document).on('mouseover', this.enter ,this);
29277 Roo.get(document).on('mouseout', this.leave, this);
29280 this.currentTip = new Roo.bootstrap.Tooltip();
29283 enter : function(ev)
29285 var dom = ev.getTarget();
29287 //Roo.log(['enter',dom]);
29288 var el = Roo.fly(dom);
29289 if (this.currentEl) {
29291 //Roo.log(this.currentEl);
29292 //Roo.log(this.currentEl.contains(dom));
29293 if (this.currentEl == el) {
29296 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29302 if (this.currentTip.el) {
29303 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29307 if(!el || el.dom == document){
29313 if (!el.attr('tooltip')) {
29314 pel = el.findParent("[tooltip]");
29316 bindEl = Roo.get(pel);
29322 // you can not look for children, as if el is the body.. then everythign is the child..
29323 if (!pel && !el.attr('tooltip')) { //
29324 if (!el.select("[tooltip]").elements.length) {
29327 // is the mouse over this child...?
29328 bindEl = el.select("[tooltip]").first();
29329 var xy = ev.getXY();
29330 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29331 //Roo.log("not in region.");
29334 //Roo.log("child element over..");
29337 this.currentEl = el;
29338 this.currentTip.bind(bindEl);
29339 this.currentRegion = Roo.lib.Region.getRegion(dom);
29340 this.currentTip.enter();
29343 leave : function(ev)
29345 var dom = ev.getTarget();
29346 //Roo.log(['leave',dom]);
29347 if (!this.currentEl) {
29352 if (dom != this.currentEl.dom) {
29355 var xy = ev.getXY();
29356 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29359 // only activate leave if mouse cursor is outside... bounding box..
29364 if (this.currentTip) {
29365 this.currentTip.leave();
29367 //Roo.log('clear currentEl');
29368 this.currentEl = false;
29373 'left' : ['r-l', [-2,0], 'right'],
29374 'right' : ['l-r', [2,0], 'left'],
29375 'bottom' : ['t-b', [0,2], 'top'],
29376 'top' : [ 'b-t', [0,-2], 'bottom']
29382 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29387 delay : null, // can be { show : 300 , hide: 500}
29391 hoverState : null, //???
29393 placement : 'bottom',
29397 getAutoCreate : function(){
29404 cls : 'tooltip-arrow arrow'
29407 cls : 'tooltip-inner'
29414 bind : function(el)
29419 initEvents : function()
29421 this.arrowEl = this.el.select('.arrow', true).first();
29422 this.innerEl = this.el.select('.tooltip-inner', true).first();
29425 enter : function () {
29427 if (this.timeout != null) {
29428 clearTimeout(this.timeout);
29431 this.hoverState = 'in';
29432 //Roo.log("enter - show");
29433 if (!this.delay || !this.delay.show) {
29438 this.timeout = setTimeout(function () {
29439 if (_t.hoverState == 'in') {
29442 }, this.delay.show);
29446 clearTimeout(this.timeout);
29448 this.hoverState = 'out';
29449 if (!this.delay || !this.delay.hide) {
29455 this.timeout = setTimeout(function () {
29456 //Roo.log("leave - timeout");
29458 if (_t.hoverState == 'out') {
29460 Roo.bootstrap.Tooltip.currentEl = false;
29465 show : function (msg)
29468 this.render(document.body);
29471 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29473 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29475 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29477 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29478 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29480 var placement = typeof this.placement == 'function' ?
29481 this.placement.call(this, this.el, on_el) :
29484 var autoToken = /\s?auto?\s?/i;
29485 var autoPlace = autoToken.test(placement);
29487 placement = placement.replace(autoToken, '') || 'top';
29491 //this.el.setXY([0,0]);
29493 //this.el.dom.style.display='block';
29495 //this.el.appendTo(on_el);
29497 var p = this.getPosition();
29498 var box = this.el.getBox();
29504 var align = this.alignment[placement];
29506 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29508 if(placement == 'top' || placement == 'bottom'){
29510 placement = 'right';
29513 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29514 placement = 'left';
29517 var scroll = Roo.select('body', true).first().getScroll();
29519 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29523 align = this.alignment[placement];
29525 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29529 var elems = document.getElementsByTagName('div');
29530 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29531 for (var i = 0; i < elems.length; i++) {
29532 var zindex = Number.parseInt(
29533 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29536 if (zindex > highest) {
29543 this.el.dom.style.zIndex = highest;
29545 this.el.alignTo(this.bindEl, align[0],align[1]);
29546 //var arrow = this.el.select('.arrow',true).first();
29547 //arrow.set(align[2],
29549 this.el.addClass(placement);
29550 this.el.addClass("bs-tooltip-"+ placement);
29552 this.el.addClass('in fade show');
29554 this.hoverState = null;
29556 if (this.el.hasClass('fade')) {
29571 //this.el.setXY([0,0]);
29572 this.el.removeClass(['show', 'in']);
29588 * @class Roo.bootstrap.LocationPicker
29589 * @extends Roo.bootstrap.Component
29590 * Bootstrap LocationPicker class
29591 * @cfg {Number} latitude Position when init default 0
29592 * @cfg {Number} longitude Position when init default 0
29593 * @cfg {Number} zoom default 15
29594 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29595 * @cfg {Boolean} mapTypeControl default false
29596 * @cfg {Boolean} disableDoubleClickZoom default false
29597 * @cfg {Boolean} scrollwheel default true
29598 * @cfg {Boolean} streetViewControl default false
29599 * @cfg {Number} radius default 0
29600 * @cfg {String} locationName
29601 * @cfg {Boolean} draggable default true
29602 * @cfg {Boolean} enableAutocomplete default false
29603 * @cfg {Boolean} enableReverseGeocode default true
29604 * @cfg {String} markerTitle
29607 * Create a new LocationPicker
29608 * @param {Object} config The config object
29612 Roo.bootstrap.LocationPicker = function(config){
29614 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29619 * Fires when the picker initialized.
29620 * @param {Roo.bootstrap.LocationPicker} this
29621 * @param {Google Location} location
29625 * @event positionchanged
29626 * Fires when the picker position changed.
29627 * @param {Roo.bootstrap.LocationPicker} this
29628 * @param {Google Location} location
29630 positionchanged : true,
29633 * Fires when the map resize.
29634 * @param {Roo.bootstrap.LocationPicker} this
29639 * Fires when the map show.
29640 * @param {Roo.bootstrap.LocationPicker} this
29645 * Fires when the map hide.
29646 * @param {Roo.bootstrap.LocationPicker} this
29651 * Fires when click the map.
29652 * @param {Roo.bootstrap.LocationPicker} this
29653 * @param {Map event} e
29657 * @event mapRightClick
29658 * Fires when right click the map.
29659 * @param {Roo.bootstrap.LocationPicker} this
29660 * @param {Map event} e
29662 mapRightClick : true,
29664 * @event markerClick
29665 * Fires when click the marker.
29666 * @param {Roo.bootstrap.LocationPicker} this
29667 * @param {Map event} e
29669 markerClick : true,
29671 * @event markerRightClick
29672 * Fires when right click the marker.
29673 * @param {Roo.bootstrap.LocationPicker} this
29674 * @param {Map event} e
29676 markerRightClick : true,
29678 * @event OverlayViewDraw
29679 * Fires when OverlayView Draw
29680 * @param {Roo.bootstrap.LocationPicker} this
29682 OverlayViewDraw : true,
29684 * @event OverlayViewOnAdd
29685 * Fires when OverlayView Draw
29686 * @param {Roo.bootstrap.LocationPicker} this
29688 OverlayViewOnAdd : true,
29690 * @event OverlayViewOnRemove
29691 * Fires when OverlayView Draw
29692 * @param {Roo.bootstrap.LocationPicker} this
29694 OverlayViewOnRemove : true,
29696 * @event OverlayViewShow
29697 * Fires when OverlayView Draw
29698 * @param {Roo.bootstrap.LocationPicker} this
29699 * @param {Pixel} cpx
29701 OverlayViewShow : true,
29703 * @event OverlayViewHide
29704 * Fires when OverlayView Draw
29705 * @param {Roo.bootstrap.LocationPicker} this
29707 OverlayViewHide : true,
29709 * @event loadexception
29710 * Fires when load google lib failed.
29711 * @param {Roo.bootstrap.LocationPicker} this
29713 loadexception : true
29718 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29720 gMapContext: false,
29726 mapTypeControl: false,
29727 disableDoubleClickZoom: false,
29729 streetViewControl: false,
29733 enableAutocomplete: false,
29734 enableReverseGeocode: true,
29737 getAutoCreate: function()
29742 cls: 'roo-location-picker'
29748 initEvents: function(ct, position)
29750 if(!this.el.getWidth() || this.isApplied()){
29754 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29759 initial: function()
29761 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29762 this.fireEvent('loadexception', this);
29766 if(!this.mapTypeId){
29767 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29770 this.gMapContext = this.GMapContext();
29772 this.initOverlayView();
29774 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29778 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29779 _this.setPosition(_this.gMapContext.marker.position);
29782 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29783 _this.fireEvent('mapClick', this, event);
29787 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29788 _this.fireEvent('mapRightClick', this, event);
29792 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29793 _this.fireEvent('markerClick', this, event);
29797 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29798 _this.fireEvent('markerRightClick', this, event);
29802 this.setPosition(this.gMapContext.location);
29804 this.fireEvent('initial', this, this.gMapContext.location);
29807 initOverlayView: function()
29811 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29815 _this.fireEvent('OverlayViewDraw', _this);
29820 _this.fireEvent('OverlayViewOnAdd', _this);
29823 onRemove: function()
29825 _this.fireEvent('OverlayViewOnRemove', _this);
29828 show: function(cpx)
29830 _this.fireEvent('OverlayViewShow', _this, cpx);
29835 _this.fireEvent('OverlayViewHide', _this);
29841 fromLatLngToContainerPixel: function(event)
29843 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29846 isApplied: function()
29848 return this.getGmapContext() == false ? false : true;
29851 getGmapContext: function()
29853 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29856 GMapContext: function()
29858 var position = new google.maps.LatLng(this.latitude, this.longitude);
29860 var _map = new google.maps.Map(this.el.dom, {
29863 mapTypeId: this.mapTypeId,
29864 mapTypeControl: this.mapTypeControl,
29865 disableDoubleClickZoom: this.disableDoubleClickZoom,
29866 scrollwheel: this.scrollwheel,
29867 streetViewControl: this.streetViewControl,
29868 locationName: this.locationName,
29869 draggable: this.draggable,
29870 enableAutocomplete: this.enableAutocomplete,
29871 enableReverseGeocode: this.enableReverseGeocode
29874 var _marker = new google.maps.Marker({
29875 position: position,
29877 title: this.markerTitle,
29878 draggable: this.draggable
29885 location: position,
29886 radius: this.radius,
29887 locationName: this.locationName,
29888 addressComponents: {
29889 formatted_address: null,
29890 addressLine1: null,
29891 addressLine2: null,
29893 streetNumber: null,
29897 stateOrProvince: null
29900 domContainer: this.el.dom,
29901 geodecoder: new google.maps.Geocoder()
29905 drawCircle: function(center, radius, options)
29907 if (this.gMapContext.circle != null) {
29908 this.gMapContext.circle.setMap(null);
29912 options = Roo.apply({}, options, {
29913 strokeColor: "#0000FF",
29914 strokeOpacity: .35,
29916 fillColor: "#0000FF",
29920 options.map = this.gMapContext.map;
29921 options.radius = radius;
29922 options.center = center;
29923 this.gMapContext.circle = new google.maps.Circle(options);
29924 return this.gMapContext.circle;
29930 setPosition: function(location)
29932 this.gMapContext.location = location;
29933 this.gMapContext.marker.setPosition(location);
29934 this.gMapContext.map.panTo(location);
29935 this.drawCircle(location, this.gMapContext.radius, {});
29939 if (this.gMapContext.settings.enableReverseGeocode) {
29940 this.gMapContext.geodecoder.geocode({
29941 latLng: this.gMapContext.location
29942 }, function(results, status) {
29944 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29945 _this.gMapContext.locationName = results[0].formatted_address;
29946 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29948 _this.fireEvent('positionchanged', this, location);
29955 this.fireEvent('positionchanged', this, location);
29960 google.maps.event.trigger(this.gMapContext.map, "resize");
29962 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29964 this.fireEvent('resize', this);
29967 setPositionByLatLng: function(latitude, longitude)
29969 this.setPosition(new google.maps.LatLng(latitude, longitude));
29972 getCurrentPosition: function()
29975 latitude: this.gMapContext.location.lat(),
29976 longitude: this.gMapContext.location.lng()
29980 getAddressName: function()
29982 return this.gMapContext.locationName;
29985 getAddressComponents: function()
29987 return this.gMapContext.addressComponents;
29990 address_component_from_google_geocode: function(address_components)
29994 for (var i = 0; i < address_components.length; i++) {
29995 var component = address_components[i];
29996 if (component.types.indexOf("postal_code") >= 0) {
29997 result.postalCode = component.short_name;
29998 } else if (component.types.indexOf("street_number") >= 0) {
29999 result.streetNumber = component.short_name;
30000 } else if (component.types.indexOf("route") >= 0) {
30001 result.streetName = component.short_name;
30002 } else if (component.types.indexOf("neighborhood") >= 0) {
30003 result.city = component.short_name;
30004 } else if (component.types.indexOf("locality") >= 0) {
30005 result.city = component.short_name;
30006 } else if (component.types.indexOf("sublocality") >= 0) {
30007 result.district = component.short_name;
30008 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30009 result.stateOrProvince = component.short_name;
30010 } else if (component.types.indexOf("country") >= 0) {
30011 result.country = component.short_name;
30015 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30016 result.addressLine2 = "";
30020 setZoomLevel: function(zoom)
30022 this.gMapContext.map.setZoom(zoom);
30035 this.fireEvent('show', this);
30046 this.fireEvent('hide', this);
30051 Roo.apply(Roo.bootstrap.LocationPicker, {
30053 OverlayView : function(map, options)
30055 options = options || {};
30062 * @class Roo.bootstrap.Alert
30063 * @extends Roo.bootstrap.Component
30064 * Bootstrap Alert class - shows an alert area box
30066 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30067 Enter a valid email address
30070 * @cfg {String} title The title of alert
30071 * @cfg {String} html The content of alert
30072 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30073 * @cfg {String} fa font-awesomeicon
30074 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30075 * @cfg {Boolean} close true to show a x closer
30079 * Create a new alert
30080 * @param {Object} config The config object
30084 Roo.bootstrap.Alert = function(config){
30085 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30089 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30095 faicon: false, // BC
30099 getAutoCreate : function()
30111 style : this.close ? '' : 'display:none'
30115 cls : 'roo-alert-icon'
30120 cls : 'roo-alert-title',
30125 cls : 'roo-alert-text',
30132 cfg.cn[0].cls += ' fa ' + this.faicon;
30135 cfg.cn[0].cls += ' fa ' + this.fa;
30139 cfg.cls += ' alert-' + this.weight;
30145 initEvents: function()
30147 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30148 this.titleEl = this.el.select('.roo-alert-title',true).first();
30149 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30150 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30151 if (this.seconds > 0) {
30152 this.hide.defer(this.seconds, this);
30156 * Set the Title Message HTML
30157 * @param {String} html
30159 setTitle : function(str)
30161 this.titleEl.dom.innerHTML = str;
30165 * Set the Body Message HTML
30166 * @param {String} html
30168 setHtml : function(str)
30170 this.htmlEl.dom.innerHTML = str;
30173 * Set the Weight of the alert
30174 * @param {String} (success|info|warning|danger) weight
30177 setWeight : function(weight)
30180 this.el.removeClass('alert-' + this.weight);
30183 this.weight = weight;
30185 this.el.addClass('alert-' + this.weight);
30188 * Set the Icon of the alert
30189 * @param {String} see fontawsome names (name without the 'fa-' bit)
30191 setIcon : function(icon)
30194 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30197 this.faicon = icon;
30199 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30224 * @class Roo.bootstrap.UploadCropbox
30225 * @extends Roo.bootstrap.Component
30226 * Bootstrap UploadCropbox class
30227 * @cfg {String} emptyText show when image has been loaded
30228 * @cfg {String} rotateNotify show when image too small to rotate
30229 * @cfg {Number} errorTimeout default 3000
30230 * @cfg {Number} minWidth default 300
30231 * @cfg {Number} minHeight default 300
30232 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30233 * @cfg {Boolean} isDocument (true|false) default false
30234 * @cfg {String} url action url
30235 * @cfg {String} paramName default 'imageUpload'
30236 * @cfg {String} method default POST
30237 * @cfg {Boolean} loadMask (true|false) default true
30238 * @cfg {Boolean} loadingText default 'Loading...'
30241 * Create a new UploadCropbox
30242 * @param {Object} config The config object
30245 Roo.bootstrap.UploadCropbox = function(config){
30246 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30250 * @event beforeselectfile
30251 * Fire before select file
30252 * @param {Roo.bootstrap.UploadCropbox} this
30254 "beforeselectfile" : true,
30257 * Fire after initEvent
30258 * @param {Roo.bootstrap.UploadCropbox} this
30263 * Fire after initEvent
30264 * @param {Roo.bootstrap.UploadCropbox} this
30265 * @param {String} data
30270 * Fire when preparing the file data
30271 * @param {Roo.bootstrap.UploadCropbox} this
30272 * @param {Object} file
30277 * Fire when get exception
30278 * @param {Roo.bootstrap.UploadCropbox} this
30279 * @param {XMLHttpRequest} xhr
30281 "exception" : true,
30283 * @event beforeloadcanvas
30284 * Fire before load the canvas
30285 * @param {Roo.bootstrap.UploadCropbox} this
30286 * @param {String} src
30288 "beforeloadcanvas" : true,
30291 * Fire when trash image
30292 * @param {Roo.bootstrap.UploadCropbox} this
30297 * Fire when download the image
30298 * @param {Roo.bootstrap.UploadCropbox} this
30302 * @event footerbuttonclick
30303 * Fire when footerbuttonclick
30304 * @param {Roo.bootstrap.UploadCropbox} this
30305 * @param {String} type
30307 "footerbuttonclick" : true,
30311 * @param {Roo.bootstrap.UploadCropbox} this
30316 * Fire when rotate the image
30317 * @param {Roo.bootstrap.UploadCropbox} this
30318 * @param {String} pos
30323 * Fire when inspect the file
30324 * @param {Roo.bootstrap.UploadCropbox} this
30325 * @param {Object} file
30330 * Fire when xhr upload the file
30331 * @param {Roo.bootstrap.UploadCropbox} this
30332 * @param {Object} data
30337 * Fire when arrange the file data
30338 * @param {Roo.bootstrap.UploadCropbox} this
30339 * @param {Object} formData
30344 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30347 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30349 emptyText : 'Click to upload image',
30350 rotateNotify : 'Image is too small to rotate',
30351 errorTimeout : 3000,
30365 cropType : 'image/jpeg',
30367 canvasLoaded : false,
30368 isDocument : false,
30370 paramName : 'imageUpload',
30372 loadingText : 'Loading...',
30375 getAutoCreate : function()
30379 cls : 'roo-upload-cropbox',
30383 cls : 'roo-upload-cropbox-selector',
30388 cls : 'roo-upload-cropbox-body',
30389 style : 'cursor:pointer',
30393 cls : 'roo-upload-cropbox-preview'
30397 cls : 'roo-upload-cropbox-thumb'
30401 cls : 'roo-upload-cropbox-empty-notify',
30402 html : this.emptyText
30406 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30407 html : this.rotateNotify
30413 cls : 'roo-upload-cropbox-footer',
30416 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30426 onRender : function(ct, position)
30428 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30430 if (this.buttons.length) {
30432 Roo.each(this.buttons, function(bb) {
30434 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30436 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30442 this.maskEl = this.el;
30446 initEvents : function()
30448 this.urlAPI = (window.createObjectURL && window) ||
30449 (window.URL && URL.revokeObjectURL && URL) ||
30450 (window.webkitURL && webkitURL);
30452 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30453 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30455 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30456 this.selectorEl.hide();
30458 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30459 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30461 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30462 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30463 this.thumbEl.hide();
30465 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30466 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30468 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30469 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30470 this.errorEl.hide();
30472 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30473 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30474 this.footerEl.hide();
30476 this.setThumbBoxSize();
30482 this.fireEvent('initial', this);
30489 window.addEventListener("resize", function() { _this.resize(); } );
30491 this.bodyEl.on('click', this.beforeSelectFile, this);
30494 this.bodyEl.on('touchstart', this.onTouchStart, this);
30495 this.bodyEl.on('touchmove', this.onTouchMove, this);
30496 this.bodyEl.on('touchend', this.onTouchEnd, this);
30500 this.bodyEl.on('mousedown', this.onMouseDown, this);
30501 this.bodyEl.on('mousemove', this.onMouseMove, this);
30502 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30503 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30504 Roo.get(document).on('mouseup', this.onMouseUp, this);
30507 this.selectorEl.on('change', this.onFileSelected, this);
30513 this.baseScale = 1;
30515 this.baseRotate = 1;
30516 this.dragable = false;
30517 this.pinching = false;
30520 this.cropData = false;
30521 this.notifyEl.dom.innerHTML = this.emptyText;
30523 this.selectorEl.dom.value = '';
30527 resize : function()
30529 if(this.fireEvent('resize', this) != false){
30530 this.setThumbBoxPosition();
30531 this.setCanvasPosition();
30535 onFooterButtonClick : function(e, el, o, type)
30538 case 'rotate-left' :
30539 this.onRotateLeft(e);
30541 case 'rotate-right' :
30542 this.onRotateRight(e);
30545 this.beforeSelectFile(e);
30560 this.fireEvent('footerbuttonclick', this, type);
30563 beforeSelectFile : function(e)
30565 e.preventDefault();
30567 if(this.fireEvent('beforeselectfile', this) != false){
30568 this.selectorEl.dom.click();
30572 onFileSelected : function(e)
30574 e.preventDefault();
30576 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30580 var file = this.selectorEl.dom.files[0];
30582 if(this.fireEvent('inspect', this, file) != false){
30583 this.prepare(file);
30588 trash : function(e)
30590 this.fireEvent('trash', this);
30593 download : function(e)
30595 this.fireEvent('download', this);
30598 loadCanvas : function(src)
30600 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30604 this.imageEl = document.createElement('img');
30608 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30610 this.imageEl.src = src;
30614 onLoadCanvas : function()
30616 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30617 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30619 this.bodyEl.un('click', this.beforeSelectFile, this);
30621 this.notifyEl.hide();
30622 this.thumbEl.show();
30623 this.footerEl.show();
30625 this.baseRotateLevel();
30627 if(this.isDocument){
30628 this.setThumbBoxSize();
30631 this.setThumbBoxPosition();
30633 this.baseScaleLevel();
30639 this.canvasLoaded = true;
30642 this.maskEl.unmask();
30647 setCanvasPosition : function()
30649 if(!this.canvasEl){
30653 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30654 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30656 this.previewEl.setLeft(pw);
30657 this.previewEl.setTop(ph);
30661 onMouseDown : function(e)
30665 this.dragable = true;
30666 this.pinching = false;
30668 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30669 this.dragable = false;
30673 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30674 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30678 onMouseMove : function(e)
30682 if(!this.canvasLoaded){
30686 if (!this.dragable){
30690 var minX = Math.ceil(this.thumbEl.getLeft(true));
30691 var minY = Math.ceil(this.thumbEl.getTop(true));
30693 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30694 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30696 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30697 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30699 x = x - this.mouseX;
30700 y = y - this.mouseY;
30702 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30703 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30705 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30706 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30708 this.previewEl.setLeft(bgX);
30709 this.previewEl.setTop(bgY);
30711 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30712 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30715 onMouseUp : function(e)
30719 this.dragable = false;
30722 onMouseWheel : function(e)
30726 this.startScale = this.scale;
30728 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30730 if(!this.zoomable()){
30731 this.scale = this.startScale;
30740 zoomable : function()
30742 var minScale = this.thumbEl.getWidth() / this.minWidth;
30744 if(this.minWidth < this.minHeight){
30745 minScale = this.thumbEl.getHeight() / this.minHeight;
30748 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30749 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30753 (this.rotate == 0 || this.rotate == 180) &&
30755 width > this.imageEl.OriginWidth ||
30756 height > this.imageEl.OriginHeight ||
30757 (width < this.minWidth && height < this.minHeight)
30765 (this.rotate == 90 || this.rotate == 270) &&
30767 width > this.imageEl.OriginWidth ||
30768 height > this.imageEl.OriginHeight ||
30769 (width < this.minHeight && height < this.minWidth)
30776 !this.isDocument &&
30777 (this.rotate == 0 || this.rotate == 180) &&
30779 width < this.minWidth ||
30780 width > this.imageEl.OriginWidth ||
30781 height < this.minHeight ||
30782 height > this.imageEl.OriginHeight
30789 !this.isDocument &&
30790 (this.rotate == 90 || this.rotate == 270) &&
30792 width < this.minHeight ||
30793 width > this.imageEl.OriginWidth ||
30794 height < this.minWidth ||
30795 height > this.imageEl.OriginHeight
30805 onRotateLeft : function(e)
30807 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30809 var minScale = this.thumbEl.getWidth() / this.minWidth;
30811 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30812 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30814 this.startScale = this.scale;
30816 while (this.getScaleLevel() < minScale){
30818 this.scale = this.scale + 1;
30820 if(!this.zoomable()){
30825 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30826 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30831 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30838 this.scale = this.startScale;
30840 this.onRotateFail();
30845 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30847 if(this.isDocument){
30848 this.setThumbBoxSize();
30849 this.setThumbBoxPosition();
30850 this.setCanvasPosition();
30855 this.fireEvent('rotate', this, 'left');
30859 onRotateRight : function(e)
30861 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30863 var minScale = this.thumbEl.getWidth() / this.minWidth;
30865 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30866 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30868 this.startScale = this.scale;
30870 while (this.getScaleLevel() < minScale){
30872 this.scale = this.scale + 1;
30874 if(!this.zoomable()){
30879 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30880 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30885 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30892 this.scale = this.startScale;
30894 this.onRotateFail();
30899 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30901 if(this.isDocument){
30902 this.setThumbBoxSize();
30903 this.setThumbBoxPosition();
30904 this.setCanvasPosition();
30909 this.fireEvent('rotate', this, 'right');
30912 onRotateFail : function()
30914 this.errorEl.show(true);
30918 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30923 this.previewEl.dom.innerHTML = '';
30925 var canvasEl = document.createElement("canvas");
30927 var contextEl = canvasEl.getContext("2d");
30929 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30930 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30931 var center = this.imageEl.OriginWidth / 2;
30933 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30934 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30935 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30936 center = this.imageEl.OriginHeight / 2;
30939 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30941 contextEl.translate(center, center);
30942 contextEl.rotate(this.rotate * Math.PI / 180);
30944 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30946 this.canvasEl = document.createElement("canvas");
30948 this.contextEl = this.canvasEl.getContext("2d");
30950 switch (this.rotate) {
30953 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30954 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30956 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30961 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30962 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30964 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30965 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);
30969 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30974 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30975 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30977 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30978 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);
30982 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);
30987 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30988 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30990 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30991 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30995 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);
31002 this.previewEl.appendChild(this.canvasEl);
31004 this.setCanvasPosition();
31009 if(!this.canvasLoaded){
31013 var imageCanvas = document.createElement("canvas");
31015 var imageContext = imageCanvas.getContext("2d");
31017 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31018 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31020 var center = imageCanvas.width / 2;
31022 imageContext.translate(center, center);
31024 imageContext.rotate(this.rotate * Math.PI / 180);
31026 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31028 var canvas = document.createElement("canvas");
31030 var context = canvas.getContext("2d");
31032 canvas.width = this.minWidth;
31033 canvas.height = this.minHeight;
31035 switch (this.rotate) {
31038 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31039 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31041 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31042 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31044 var targetWidth = this.minWidth - 2 * x;
31045 var targetHeight = this.minHeight - 2 * y;
31049 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31050 scale = targetWidth / width;
31053 if(x > 0 && y == 0){
31054 scale = targetHeight / height;
31057 if(x > 0 && y > 0){
31058 scale = targetWidth / width;
31060 if(width < height){
31061 scale = targetHeight / height;
31065 context.scale(scale, scale);
31067 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31068 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31070 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31071 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31073 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31078 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31079 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31081 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31082 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31084 var targetWidth = this.minWidth - 2 * x;
31085 var targetHeight = this.minHeight - 2 * y;
31089 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31090 scale = targetWidth / width;
31093 if(x > 0 && y == 0){
31094 scale = targetHeight / height;
31097 if(x > 0 && y > 0){
31098 scale = targetWidth / width;
31100 if(width < height){
31101 scale = targetHeight / height;
31105 context.scale(scale, scale);
31107 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31108 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31110 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31111 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31113 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31115 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31120 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31121 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31123 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31124 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31126 var targetWidth = this.minWidth - 2 * x;
31127 var targetHeight = this.minHeight - 2 * y;
31131 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31132 scale = targetWidth / width;
31135 if(x > 0 && y == 0){
31136 scale = targetHeight / height;
31139 if(x > 0 && y > 0){
31140 scale = targetWidth / width;
31142 if(width < height){
31143 scale = targetHeight / height;
31147 context.scale(scale, scale);
31149 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31150 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31152 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31153 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31155 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31156 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31158 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31163 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31164 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31166 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31167 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31169 var targetWidth = this.minWidth - 2 * x;
31170 var targetHeight = this.minHeight - 2 * y;
31174 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31175 scale = targetWidth / width;
31178 if(x > 0 && y == 0){
31179 scale = targetHeight / height;
31182 if(x > 0 && y > 0){
31183 scale = targetWidth / width;
31185 if(width < height){
31186 scale = targetHeight / height;
31190 context.scale(scale, scale);
31192 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31193 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31195 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31196 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31198 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31200 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31207 this.cropData = canvas.toDataURL(this.cropType);
31209 if(this.fireEvent('crop', this, this.cropData) !== false){
31210 this.process(this.file, this.cropData);
31217 setThumbBoxSize : function()
31221 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31222 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31223 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31225 this.minWidth = width;
31226 this.minHeight = height;
31228 if(this.rotate == 90 || this.rotate == 270){
31229 this.minWidth = height;
31230 this.minHeight = width;
31235 width = Math.ceil(this.minWidth * height / this.minHeight);
31237 if(this.minWidth > this.minHeight){
31239 height = Math.ceil(this.minHeight * width / this.minWidth);
31242 this.thumbEl.setStyle({
31243 width : width + 'px',
31244 height : height + 'px'
31251 setThumbBoxPosition : function()
31253 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31254 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31256 this.thumbEl.setLeft(x);
31257 this.thumbEl.setTop(y);
31261 baseRotateLevel : function()
31263 this.baseRotate = 1;
31266 typeof(this.exif) != 'undefined' &&
31267 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31268 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31270 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31273 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31277 baseScaleLevel : function()
31281 if(this.isDocument){
31283 if(this.baseRotate == 6 || this.baseRotate == 8){
31285 height = this.thumbEl.getHeight();
31286 this.baseScale = height / this.imageEl.OriginWidth;
31288 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31289 width = this.thumbEl.getWidth();
31290 this.baseScale = width / this.imageEl.OriginHeight;
31296 height = this.thumbEl.getHeight();
31297 this.baseScale = height / this.imageEl.OriginHeight;
31299 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31300 width = this.thumbEl.getWidth();
31301 this.baseScale = width / this.imageEl.OriginWidth;
31307 if(this.baseRotate == 6 || this.baseRotate == 8){
31309 width = this.thumbEl.getHeight();
31310 this.baseScale = width / this.imageEl.OriginHeight;
31312 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31313 height = this.thumbEl.getWidth();
31314 this.baseScale = height / this.imageEl.OriginHeight;
31317 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318 height = this.thumbEl.getWidth();
31319 this.baseScale = height / this.imageEl.OriginHeight;
31321 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31322 width = this.thumbEl.getHeight();
31323 this.baseScale = width / this.imageEl.OriginWidth;
31330 width = this.thumbEl.getWidth();
31331 this.baseScale = width / this.imageEl.OriginWidth;
31333 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31334 height = this.thumbEl.getHeight();
31335 this.baseScale = height / this.imageEl.OriginHeight;
31338 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31340 height = this.thumbEl.getHeight();
31341 this.baseScale = height / this.imageEl.OriginHeight;
31343 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31344 width = this.thumbEl.getWidth();
31345 this.baseScale = width / this.imageEl.OriginWidth;
31353 getScaleLevel : function()
31355 return this.baseScale * Math.pow(1.1, this.scale);
31358 onTouchStart : function(e)
31360 if(!this.canvasLoaded){
31361 this.beforeSelectFile(e);
31365 var touches = e.browserEvent.touches;
31371 if(touches.length == 1){
31372 this.onMouseDown(e);
31376 if(touches.length != 2){
31382 for(var i = 0, finger; finger = touches[i]; i++){
31383 coords.push(finger.pageX, finger.pageY);
31386 var x = Math.pow(coords[0] - coords[2], 2);
31387 var y = Math.pow(coords[1] - coords[3], 2);
31389 this.startDistance = Math.sqrt(x + y);
31391 this.startScale = this.scale;
31393 this.pinching = true;
31394 this.dragable = false;
31398 onTouchMove : function(e)
31400 if(!this.pinching && !this.dragable){
31404 var touches = e.browserEvent.touches;
31411 this.onMouseMove(e);
31417 for(var i = 0, finger; finger = touches[i]; i++){
31418 coords.push(finger.pageX, finger.pageY);
31421 var x = Math.pow(coords[0] - coords[2], 2);
31422 var y = Math.pow(coords[1] - coords[3], 2);
31424 this.endDistance = Math.sqrt(x + y);
31426 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31428 if(!this.zoomable()){
31429 this.scale = this.startScale;
31437 onTouchEnd : function(e)
31439 this.pinching = false;
31440 this.dragable = false;
31444 process : function(file, crop)
31447 this.maskEl.mask(this.loadingText);
31450 this.xhr = new XMLHttpRequest();
31452 file.xhr = this.xhr;
31454 this.xhr.open(this.method, this.url, true);
31457 "Accept": "application/json",
31458 "Cache-Control": "no-cache",
31459 "X-Requested-With": "XMLHttpRequest"
31462 for (var headerName in headers) {
31463 var headerValue = headers[headerName];
31465 this.xhr.setRequestHeader(headerName, headerValue);
31471 this.xhr.onload = function()
31473 _this.xhrOnLoad(_this.xhr);
31476 this.xhr.onerror = function()
31478 _this.xhrOnError(_this.xhr);
31481 var formData = new FormData();
31483 formData.append('returnHTML', 'NO');
31486 formData.append('crop', crop);
31489 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31490 formData.append(this.paramName, file, file.name);
31493 if(typeof(file.filename) != 'undefined'){
31494 formData.append('filename', file.filename);
31497 if(typeof(file.mimetype) != 'undefined'){
31498 formData.append('mimetype', file.mimetype);
31501 if(this.fireEvent('arrange', this, formData) != false){
31502 this.xhr.send(formData);
31506 xhrOnLoad : function(xhr)
31509 this.maskEl.unmask();
31512 if (xhr.readyState !== 4) {
31513 this.fireEvent('exception', this, xhr);
31517 var response = Roo.decode(xhr.responseText);
31519 if(!response.success){
31520 this.fireEvent('exception', this, xhr);
31524 var response = Roo.decode(xhr.responseText);
31526 this.fireEvent('upload', this, response);
31530 xhrOnError : function()
31533 this.maskEl.unmask();
31536 Roo.log('xhr on error');
31538 var response = Roo.decode(xhr.responseText);
31544 prepare : function(file)
31547 this.maskEl.mask(this.loadingText);
31553 if(typeof(file) === 'string'){
31554 this.loadCanvas(file);
31558 if(!file || !this.urlAPI){
31563 this.cropType = file.type;
31567 if(this.fireEvent('prepare', this, this.file) != false){
31569 var reader = new FileReader();
31571 reader.onload = function (e) {
31572 if (e.target.error) {
31573 Roo.log(e.target.error);
31577 var buffer = e.target.result,
31578 dataView = new DataView(buffer),
31580 maxOffset = dataView.byteLength - 4,
31584 if (dataView.getUint16(0) === 0xffd8) {
31585 while (offset < maxOffset) {
31586 markerBytes = dataView.getUint16(offset);
31588 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31589 markerLength = dataView.getUint16(offset + 2) + 2;
31590 if (offset + markerLength > dataView.byteLength) {
31591 Roo.log('Invalid meta data: Invalid segment size.');
31595 if(markerBytes == 0xffe1){
31596 _this.parseExifData(
31603 offset += markerLength;
31613 var url = _this.urlAPI.createObjectURL(_this.file);
31615 _this.loadCanvas(url);
31620 reader.readAsArrayBuffer(this.file);
31626 parseExifData : function(dataView, offset, length)
31628 var tiffOffset = offset + 10,
31632 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31633 // No Exif data, might be XMP data instead
31637 // Check for the ASCII code for "Exif" (0x45786966):
31638 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31639 // No Exif data, might be XMP data instead
31642 if (tiffOffset + 8 > dataView.byteLength) {
31643 Roo.log('Invalid Exif data: Invalid segment size.');
31646 // Check for the two null bytes:
31647 if (dataView.getUint16(offset + 8) !== 0x0000) {
31648 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31651 // Check the byte alignment:
31652 switch (dataView.getUint16(tiffOffset)) {
31654 littleEndian = true;
31657 littleEndian = false;
31660 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31663 // Check for the TIFF tag marker (0x002A):
31664 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31665 Roo.log('Invalid Exif data: Missing TIFF marker.');
31668 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31669 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31671 this.parseExifTags(
31674 tiffOffset + dirOffset,
31679 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31684 if (dirOffset + 6 > dataView.byteLength) {
31685 Roo.log('Invalid Exif data: Invalid directory offset.');
31688 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31689 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31690 if (dirEndOffset + 4 > dataView.byteLength) {
31691 Roo.log('Invalid Exif data: Invalid directory size.');
31694 for (i = 0; i < tagsNumber; i += 1) {
31698 dirOffset + 2 + 12 * i, // tag offset
31702 // Return the offset to the next directory:
31703 return dataView.getUint32(dirEndOffset, littleEndian);
31706 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31708 var tag = dataView.getUint16(offset, littleEndian);
31710 this.exif[tag] = this.getExifValue(
31714 dataView.getUint16(offset + 2, littleEndian), // tag type
31715 dataView.getUint32(offset + 4, littleEndian), // tag length
31720 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31722 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31731 Roo.log('Invalid Exif data: Invalid tag type.');
31735 tagSize = tagType.size * length;
31736 // Determine if the value is contained in the dataOffset bytes,
31737 // or if the value at the dataOffset is a pointer to the actual data:
31738 dataOffset = tagSize > 4 ?
31739 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31740 if (dataOffset + tagSize > dataView.byteLength) {
31741 Roo.log('Invalid Exif data: Invalid data offset.');
31744 if (length === 1) {
31745 return tagType.getValue(dataView, dataOffset, littleEndian);
31748 for (i = 0; i < length; i += 1) {
31749 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31752 if (tagType.ascii) {
31754 // Concatenate the chars:
31755 for (i = 0; i < values.length; i += 1) {
31757 // Ignore the terminating NULL byte(s):
31758 if (c === '\u0000') {
31770 Roo.apply(Roo.bootstrap.UploadCropbox, {
31772 'Orientation': 0x0112
31776 1: 0, //'top-left',
31778 3: 180, //'bottom-right',
31779 // 4: 'bottom-left',
31781 6: 90, //'right-top',
31782 // 7: 'right-bottom',
31783 8: 270 //'left-bottom'
31787 // byte, 8-bit unsigned int:
31789 getValue: function (dataView, dataOffset) {
31790 return dataView.getUint8(dataOffset);
31794 // ascii, 8-bit byte:
31796 getValue: function (dataView, dataOffset) {
31797 return String.fromCharCode(dataView.getUint8(dataOffset));
31802 // short, 16 bit int:
31804 getValue: function (dataView, dataOffset, littleEndian) {
31805 return dataView.getUint16(dataOffset, littleEndian);
31809 // long, 32 bit int:
31811 getValue: function (dataView, dataOffset, littleEndian) {
31812 return dataView.getUint32(dataOffset, littleEndian);
31816 // rational = two long values, first is numerator, second is denominator:
31818 getValue: function (dataView, dataOffset, littleEndian) {
31819 return dataView.getUint32(dataOffset, littleEndian) /
31820 dataView.getUint32(dataOffset + 4, littleEndian);
31824 // slong, 32 bit signed int:
31826 getValue: function (dataView, dataOffset, littleEndian) {
31827 return dataView.getInt32(dataOffset, littleEndian);
31831 // srational, two slongs, first is numerator, second is denominator:
31833 getValue: function (dataView, dataOffset, littleEndian) {
31834 return dataView.getInt32(dataOffset, littleEndian) /
31835 dataView.getInt32(dataOffset + 4, littleEndian);
31845 cls : 'btn-group roo-upload-cropbox-rotate-left',
31846 action : 'rotate-left',
31850 cls : 'btn btn-default',
31851 html : '<i class="fa fa-undo"></i>'
31857 cls : 'btn-group roo-upload-cropbox-picture',
31858 action : 'picture',
31862 cls : 'btn btn-default',
31863 html : '<i class="fa fa-picture-o"></i>'
31869 cls : 'btn-group roo-upload-cropbox-rotate-right',
31870 action : 'rotate-right',
31874 cls : 'btn btn-default',
31875 html : '<i class="fa fa-repeat"></i>'
31883 cls : 'btn-group roo-upload-cropbox-rotate-left',
31884 action : 'rotate-left',
31888 cls : 'btn btn-default',
31889 html : '<i class="fa fa-undo"></i>'
31895 cls : 'btn-group roo-upload-cropbox-download',
31896 action : 'download',
31900 cls : 'btn btn-default',
31901 html : '<i class="fa fa-download"></i>'
31907 cls : 'btn-group roo-upload-cropbox-crop',
31912 cls : 'btn btn-default',
31913 html : '<i class="fa fa-crop"></i>'
31919 cls : 'btn-group roo-upload-cropbox-trash',
31924 cls : 'btn btn-default',
31925 html : '<i class="fa fa-trash"></i>'
31931 cls : 'btn-group roo-upload-cropbox-rotate-right',
31932 action : 'rotate-right',
31936 cls : 'btn btn-default',
31937 html : '<i class="fa fa-repeat"></i>'
31945 cls : 'btn-group roo-upload-cropbox-rotate-left',
31946 action : 'rotate-left',
31950 cls : 'btn btn-default',
31951 html : '<i class="fa fa-undo"></i>'
31957 cls : 'btn-group roo-upload-cropbox-rotate-right',
31958 action : 'rotate-right',
31962 cls : 'btn btn-default',
31963 html : '<i class="fa fa-repeat"></i>'
31976 * @class Roo.bootstrap.DocumentManager
31977 * @extends Roo.bootstrap.Component
31978 * Bootstrap DocumentManager class
31979 * @cfg {String} paramName default 'imageUpload'
31980 * @cfg {String} toolTipName default 'filename'
31981 * @cfg {String} method default POST
31982 * @cfg {String} url action url
31983 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31984 * @cfg {Boolean} multiple multiple upload default true
31985 * @cfg {Number} thumbSize default 300
31986 * @cfg {String} fieldLabel
31987 * @cfg {Number} labelWidth default 4
31988 * @cfg {String} labelAlign (left|top) default left
31989 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31990 * @cfg {Number} labellg set the width of label (1-12)
31991 * @cfg {Number} labelmd set the width of label (1-12)
31992 * @cfg {Number} labelsm set the width of label (1-12)
31993 * @cfg {Number} labelxs set the width of label (1-12)
31996 * Create a new DocumentManager
31997 * @param {Object} config The config object
32000 Roo.bootstrap.DocumentManager = function(config){
32001 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32004 this.delegates = [];
32009 * Fire when initial the DocumentManager
32010 * @param {Roo.bootstrap.DocumentManager} this
32015 * inspect selected file
32016 * @param {Roo.bootstrap.DocumentManager} this
32017 * @param {File} file
32022 * Fire when xhr load exception
32023 * @param {Roo.bootstrap.DocumentManager} this
32024 * @param {XMLHttpRequest} xhr
32026 "exception" : true,
32028 * @event afterupload
32029 * Fire when xhr load exception
32030 * @param {Roo.bootstrap.DocumentManager} this
32031 * @param {XMLHttpRequest} xhr
32033 "afterupload" : true,
32036 * prepare the form data
32037 * @param {Roo.bootstrap.DocumentManager} this
32038 * @param {Object} formData
32043 * Fire when remove the file
32044 * @param {Roo.bootstrap.DocumentManager} this
32045 * @param {Object} file
32050 * Fire after refresh the file
32051 * @param {Roo.bootstrap.DocumentManager} this
32056 * Fire after click the image
32057 * @param {Roo.bootstrap.DocumentManager} this
32058 * @param {Object} file
32063 * Fire when upload a image and editable set to true
32064 * @param {Roo.bootstrap.DocumentManager} this
32065 * @param {Object} file
32069 * @event beforeselectfile
32070 * Fire before select file
32071 * @param {Roo.bootstrap.DocumentManager} this
32073 "beforeselectfile" : true,
32076 * Fire before process file
32077 * @param {Roo.bootstrap.DocumentManager} this
32078 * @param {Object} file
32082 * @event previewrendered
32083 * Fire when preview rendered
32084 * @param {Roo.bootstrap.DocumentManager} this
32085 * @param {Object} file
32087 "previewrendered" : true,
32090 "previewResize" : true
32095 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32104 paramName : 'imageUpload',
32105 toolTipName : 'filename',
32108 labelAlign : 'left',
32118 getAutoCreate : function()
32120 var managerWidget = {
32122 cls : 'roo-document-manager',
32126 cls : 'roo-document-manager-selector',
32131 cls : 'roo-document-manager-uploader',
32135 cls : 'roo-document-manager-upload-btn',
32136 html : '<i class="fa fa-plus"></i>'
32147 cls : 'column col-md-12',
32152 if(this.fieldLabel.length){
32157 cls : 'column col-md-12',
32158 html : this.fieldLabel
32162 cls : 'column col-md-12',
32167 if(this.labelAlign == 'left'){
32172 html : this.fieldLabel
32181 if(this.labelWidth > 12){
32182 content[0].style = "width: " + this.labelWidth + 'px';
32185 if(this.labelWidth < 13 && this.labelmd == 0){
32186 this.labelmd = this.labelWidth;
32189 if(this.labellg > 0){
32190 content[0].cls += ' col-lg-' + this.labellg;
32191 content[1].cls += ' col-lg-' + (12 - this.labellg);
32194 if(this.labelmd > 0){
32195 content[0].cls += ' col-md-' + this.labelmd;
32196 content[1].cls += ' col-md-' + (12 - this.labelmd);
32199 if(this.labelsm > 0){
32200 content[0].cls += ' col-sm-' + this.labelsm;
32201 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32204 if(this.labelxs > 0){
32205 content[0].cls += ' col-xs-' + this.labelxs;
32206 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32214 cls : 'row clearfix',
32222 initEvents : function()
32224 this.managerEl = this.el.select('.roo-document-manager', true).first();
32225 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32227 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32228 this.selectorEl.hide();
32231 this.selectorEl.attr('multiple', 'multiple');
32234 this.selectorEl.on('change', this.onFileSelected, this);
32236 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32237 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32239 this.uploader.on('click', this.onUploaderClick, this);
32241 this.renderProgressDialog();
32245 window.addEventListener("resize", function() { _this.refresh(); } );
32247 this.fireEvent('initial', this);
32250 renderProgressDialog : function()
32254 this.progressDialog = new Roo.bootstrap.Modal({
32255 cls : 'roo-document-manager-progress-dialog',
32256 allow_close : false,
32267 btnclick : function() {
32268 _this.uploadCancel();
32274 this.progressDialog.render(Roo.get(document.body));
32276 this.progress = new Roo.bootstrap.Progress({
32277 cls : 'roo-document-manager-progress',
32282 this.progress.render(this.progressDialog.getChildContainer());
32284 this.progressBar = new Roo.bootstrap.ProgressBar({
32285 cls : 'roo-document-manager-progress-bar',
32288 aria_valuemax : 12,
32292 this.progressBar.render(this.progress.getChildContainer());
32295 onUploaderClick : function(e)
32297 e.preventDefault();
32299 if(this.fireEvent('beforeselectfile', this) != false){
32300 this.selectorEl.dom.click();
32305 onFileSelected : function(e)
32307 e.preventDefault();
32309 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32313 Roo.each(this.selectorEl.dom.files, function(file){
32314 if(this.fireEvent('inspect', this, file) != false){
32315 this.files.push(file);
32325 this.selectorEl.dom.value = '';
32327 if(!this.files || !this.files.length){
32331 if(this.boxes > 0 && this.files.length > this.boxes){
32332 this.files = this.files.slice(0, this.boxes);
32335 this.uploader.show();
32337 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32338 this.uploader.hide();
32347 Roo.each(this.files, function(file){
32349 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32350 var f = this.renderPreview(file);
32355 if(file.type.indexOf('image') != -1){
32356 this.delegates.push(
32358 _this.process(file);
32359 }).createDelegate(this)
32367 _this.process(file);
32368 }).createDelegate(this)
32373 this.files = files;
32375 this.delegates = this.delegates.concat(docs);
32377 if(!this.delegates.length){
32382 this.progressBar.aria_valuemax = this.delegates.length;
32389 arrange : function()
32391 if(!this.delegates.length){
32392 this.progressDialog.hide();
32397 var delegate = this.delegates.shift();
32399 this.progressDialog.show();
32401 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32403 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32408 refresh : function()
32410 this.uploader.show();
32412 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32413 this.uploader.hide();
32416 Roo.isTouch ? this.closable(false) : this.closable(true);
32418 this.fireEvent('refresh', this);
32421 onRemove : function(e, el, o)
32423 e.preventDefault();
32425 this.fireEvent('remove', this, o);
32429 remove : function(o)
32433 Roo.each(this.files, function(file){
32434 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32443 this.files = files;
32450 Roo.each(this.files, function(file){
32455 file.target.remove();
32464 onClick : function(e, el, o)
32466 e.preventDefault();
32468 this.fireEvent('click', this, o);
32472 closable : function(closable)
32474 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32476 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32488 xhrOnLoad : function(xhr)
32490 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32494 if (xhr.readyState !== 4) {
32496 this.fireEvent('exception', this, xhr);
32500 var response = Roo.decode(xhr.responseText);
32502 if(!response.success){
32504 this.fireEvent('exception', this, xhr);
32508 var file = this.renderPreview(response.data);
32510 this.files.push(file);
32514 this.fireEvent('afterupload', this, xhr);
32518 xhrOnError : function(xhr)
32520 Roo.log('xhr on error');
32522 var response = Roo.decode(xhr.responseText);
32529 process : function(file)
32531 if(this.fireEvent('process', this, file) !== false){
32532 if(this.editable && file.type.indexOf('image') != -1){
32533 this.fireEvent('edit', this, file);
32537 this.uploadStart(file, false);
32544 uploadStart : function(file, crop)
32546 this.xhr = new XMLHttpRequest();
32548 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32553 file.xhr = this.xhr;
32555 this.managerEl.createChild({
32557 cls : 'roo-document-manager-loading',
32561 tooltip : file.name,
32562 cls : 'roo-document-manager-thumb',
32563 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32569 this.xhr.open(this.method, this.url, true);
32572 "Accept": "application/json",
32573 "Cache-Control": "no-cache",
32574 "X-Requested-With": "XMLHttpRequest"
32577 for (var headerName in headers) {
32578 var headerValue = headers[headerName];
32580 this.xhr.setRequestHeader(headerName, headerValue);
32586 this.xhr.onload = function()
32588 _this.xhrOnLoad(_this.xhr);
32591 this.xhr.onerror = function()
32593 _this.xhrOnError(_this.xhr);
32596 var formData = new FormData();
32598 formData.append('returnHTML', 'NO');
32601 formData.append('crop', crop);
32604 formData.append(this.paramName, file, file.name);
32611 if(this.fireEvent('prepare', this, formData, options) != false){
32613 if(options.manually){
32617 this.xhr.send(formData);
32621 this.uploadCancel();
32624 uploadCancel : function()
32630 this.delegates = [];
32632 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32639 renderPreview : function(file)
32641 if(typeof(file.target) != 'undefined' && file.target){
32645 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32647 var previewEl = this.managerEl.createChild({
32649 cls : 'roo-document-manager-preview',
32653 tooltip : file[this.toolTipName],
32654 cls : 'roo-document-manager-thumb',
32655 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32660 html : '<i class="fa fa-times-circle"></i>'
32665 var close = previewEl.select('button.close', true).first();
32667 close.on('click', this.onRemove, this, file);
32669 file.target = previewEl;
32671 var image = previewEl.select('img', true).first();
32675 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32677 image.on('click', this.onClick, this, file);
32679 this.fireEvent('previewrendered', this, file);
32685 onPreviewLoad : function(file, image)
32687 if(typeof(file.target) == 'undefined' || !file.target){
32691 var width = image.dom.naturalWidth || image.dom.width;
32692 var height = image.dom.naturalHeight || image.dom.height;
32694 if(!this.previewResize) {
32698 if(width > height){
32699 file.target.addClass('wide');
32703 file.target.addClass('tall');
32708 uploadFromSource : function(file, crop)
32710 this.xhr = new XMLHttpRequest();
32712 this.managerEl.createChild({
32714 cls : 'roo-document-manager-loading',
32718 tooltip : file.name,
32719 cls : 'roo-document-manager-thumb',
32720 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32726 this.xhr.open(this.method, this.url, true);
32729 "Accept": "application/json",
32730 "Cache-Control": "no-cache",
32731 "X-Requested-With": "XMLHttpRequest"
32734 for (var headerName in headers) {
32735 var headerValue = headers[headerName];
32737 this.xhr.setRequestHeader(headerName, headerValue);
32743 this.xhr.onload = function()
32745 _this.xhrOnLoad(_this.xhr);
32748 this.xhr.onerror = function()
32750 _this.xhrOnError(_this.xhr);
32753 var formData = new FormData();
32755 formData.append('returnHTML', 'NO');
32757 formData.append('crop', crop);
32759 if(typeof(file.filename) != 'undefined'){
32760 formData.append('filename', file.filename);
32763 if(typeof(file.mimetype) != 'undefined'){
32764 formData.append('mimetype', file.mimetype);
32769 if(this.fireEvent('prepare', this, formData) != false){
32770 this.xhr.send(formData);
32780 * @class Roo.bootstrap.DocumentViewer
32781 * @extends Roo.bootstrap.Component
32782 * Bootstrap DocumentViewer class
32783 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32784 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32787 * Create a new DocumentViewer
32788 * @param {Object} config The config object
32791 Roo.bootstrap.DocumentViewer = function(config){
32792 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32797 * Fire after initEvent
32798 * @param {Roo.bootstrap.DocumentViewer} this
32804 * @param {Roo.bootstrap.DocumentViewer} this
32809 * Fire after download button
32810 * @param {Roo.bootstrap.DocumentViewer} this
32815 * Fire after trash button
32816 * @param {Roo.bootstrap.DocumentViewer} this
32823 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32825 showDownload : true,
32829 getAutoCreate : function()
32833 cls : 'roo-document-viewer',
32837 cls : 'roo-document-viewer-body',
32841 cls : 'roo-document-viewer-thumb',
32845 cls : 'roo-document-viewer-image'
32853 cls : 'roo-document-viewer-footer',
32856 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32860 cls : 'btn-group roo-document-viewer-download',
32864 cls : 'btn btn-default',
32865 html : '<i class="fa fa-download"></i>'
32871 cls : 'btn-group roo-document-viewer-trash',
32875 cls : 'btn btn-default',
32876 html : '<i class="fa fa-trash"></i>'
32889 initEvents : function()
32891 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32892 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32894 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32895 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32897 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32898 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32900 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32901 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32903 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32904 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32906 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32907 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32909 this.bodyEl.on('click', this.onClick, this);
32910 this.downloadBtn.on('click', this.onDownload, this);
32911 this.trashBtn.on('click', this.onTrash, this);
32913 this.downloadBtn.hide();
32914 this.trashBtn.hide();
32916 if(this.showDownload){
32917 this.downloadBtn.show();
32920 if(this.showTrash){
32921 this.trashBtn.show();
32924 if(!this.showDownload && !this.showTrash) {
32925 this.footerEl.hide();
32930 initial : function()
32932 this.fireEvent('initial', this);
32936 onClick : function(e)
32938 e.preventDefault();
32940 this.fireEvent('click', this);
32943 onDownload : function(e)
32945 e.preventDefault();
32947 this.fireEvent('download', this);
32950 onTrash : function(e)
32952 e.preventDefault();
32954 this.fireEvent('trash', this);
32966 * @class Roo.bootstrap.NavProgressBar
32967 * @extends Roo.bootstrap.Component
32968 * Bootstrap NavProgressBar class
32971 * Create a new nav progress bar - a bar indicating step along a process
32972 * @param {Object} config The config object
32975 Roo.bootstrap.NavProgressBar = function(config){
32976 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32978 this.bullets = this.bullets || [];
32980 // Roo.bootstrap.NavProgressBar.register(this);
32984 * Fires when the active item changes
32985 * @param {Roo.bootstrap.NavProgressBar} this
32986 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32987 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32994 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32996 * @cfg {Roo.bootstrap.NavProgressItem} NavProgressBar:bullets[]
32997 * Bullets for the Nav Progress bar for the toolbar
33002 getAutoCreate : function()
33004 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33008 cls : 'roo-navigation-bar-group',
33012 cls : 'roo-navigation-top-bar'
33016 cls : 'roo-navigation-bullets-bar',
33020 cls : 'roo-navigation-bar'
33027 cls : 'roo-navigation-bottom-bar'
33037 initEvents: function()
33042 onRender : function(ct, position)
33044 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33046 if(this.bullets.length){
33047 Roo.each(this.bullets, function(b){
33056 addItem : function(cfg)
33058 var item = new Roo.bootstrap.NavProgressItem(cfg);
33060 item.parentId = this.id;
33061 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33064 var top = new Roo.bootstrap.Element({
33066 cls : 'roo-navigation-bar-text'
33069 var bottom = new Roo.bootstrap.Element({
33071 cls : 'roo-navigation-bar-text'
33074 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33075 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33077 var topText = new Roo.bootstrap.Element({
33079 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33082 var bottomText = new Roo.bootstrap.Element({
33084 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33087 topText.onRender(top.el, null);
33088 bottomText.onRender(bottom.el, null);
33091 item.bottomEl = bottom;
33094 this.barItems.push(item);
33099 getActive : function()
33101 var active = false;
33103 Roo.each(this.barItems, function(v){
33105 if (!v.isActive()) {
33117 setActiveItem : function(item)
33121 Roo.each(this.barItems, function(v){
33122 if (v.rid == item.rid) {
33126 if (v.isActive()) {
33127 v.setActive(false);
33132 item.setActive(true);
33134 this.fireEvent('changed', this, item, prev);
33137 getBarItem: function(rid)
33141 Roo.each(this.barItems, function(e) {
33142 if (e.rid != rid) {
33153 indexOfItem : function(item)
33157 Roo.each(this.barItems, function(v, i){
33159 if (v.rid != item.rid) {
33170 setActiveNext : function()
33172 var i = this.indexOfItem(this.getActive());
33174 if (i > this.barItems.length) {
33178 this.setActiveItem(this.barItems[i+1]);
33181 setActivePrev : function()
33183 var i = this.indexOfItem(this.getActive());
33189 this.setActiveItem(this.barItems[i-1]);
33192 format : function()
33194 if(!this.barItems.length){
33198 var width = 100 / this.barItems.length;
33200 Roo.each(this.barItems, function(i){
33201 i.el.setStyle('width', width + '%');
33202 i.topEl.el.setStyle('width', width + '%');
33203 i.bottomEl.el.setStyle('width', width + '%');
33212 * Nav Progress Item
33217 * @class Roo.bootstrap.NavProgressItem
33218 * @extends Roo.bootstrap.Component
33219 * Bootstrap NavProgressItem class
33220 * @cfg {String} rid the reference id
33221 * @cfg {Boolean} active (true|false) Is item active default false
33222 * @cfg {Boolean} disabled (true|false) Is item active default false
33223 * @cfg {String} html
33224 * @cfg {String} position (top|bottom) text position default bottom
33225 * @cfg {String} icon show icon instead of number
33228 * Create a new NavProgressItem
33229 * @param {Object} config The config object
33231 Roo.bootstrap.NavProgressItem = function(config){
33232 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33237 * The raw click event for the entire grid.
33238 * @param {Roo.bootstrap.NavProgressItem} this
33239 * @param {Roo.EventObject} e
33246 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33252 position : 'bottom',
33255 getAutoCreate : function()
33257 var iconCls = 'roo-navigation-bar-item-icon';
33259 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33263 cls: 'roo-navigation-bar-item',
33273 cfg.cls += ' active';
33276 cfg.cls += ' disabled';
33282 disable : function()
33284 this.setDisabled(true);
33287 enable : function()
33289 this.setDisabled(false);
33292 initEvents: function()
33294 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33296 this.iconEl.on('click', this.onClick, this);
33299 onClick : function(e)
33301 e.preventDefault();
33307 if(this.fireEvent('click', this, e) === false){
33311 this.parent().setActiveItem(this);
33314 isActive: function ()
33316 return this.active;
33319 setActive : function(state)
33321 if(this.active == state){
33325 this.active = state;
33328 this.el.addClass('active');
33332 this.el.removeClass('active');
33337 setDisabled : function(state)
33339 if(this.disabled == state){
33343 this.disabled = state;
33346 this.el.addClass('disabled');
33350 this.el.removeClass('disabled');
33353 tooltipEl : function()
33355 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33368 * @class Roo.bootstrap.FieldLabel
33369 * @extends Roo.bootstrap.Component
33370 * Bootstrap FieldLabel class
33371 * @cfg {String} html contents of the element
33372 * @cfg {String} tag tag of the element default label
33373 * @cfg {String} cls class of the element
33374 * @cfg {String} target label target
33375 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33376 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33377 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33378 * @cfg {String} iconTooltip default "This field is required"
33379 * @cfg {String} indicatorpos (left|right) default left
33382 * Create a new FieldLabel
33383 * @param {Object} config The config object
33386 Roo.bootstrap.FieldLabel = function(config){
33387 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33392 * Fires after the field has been marked as invalid.
33393 * @param {Roo.form.FieldLabel} this
33394 * @param {String} msg The validation message
33399 * Fires after the field has been validated with no errors.
33400 * @param {Roo.form.FieldLabel} this
33406 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33413 invalidClass : 'has-warning',
33414 validClass : 'has-success',
33415 iconTooltip : 'This field is required',
33416 indicatorpos : 'left',
33418 getAutoCreate : function(){
33421 if (!this.allowBlank) {
33427 cls : 'roo-bootstrap-field-label ' + this.cls,
33432 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33433 tooltip : this.iconTooltip
33442 if(this.indicatorpos == 'right'){
33445 cls : 'roo-bootstrap-field-label ' + this.cls,
33454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33455 tooltip : this.iconTooltip
33464 initEvents: function()
33466 Roo.bootstrap.Element.superclass.initEvents.call(this);
33468 this.indicator = this.indicatorEl();
33470 if(this.indicator){
33471 this.indicator.removeClass('visible');
33472 this.indicator.addClass('invisible');
33475 Roo.bootstrap.FieldLabel.register(this);
33478 indicatorEl : function()
33480 var indicator = this.el.select('i.roo-required-indicator',true).first();
33491 * Mark this field as valid
33493 markValid : function()
33495 if(this.indicator){
33496 this.indicator.removeClass('visible');
33497 this.indicator.addClass('invisible');
33499 if (Roo.bootstrap.version == 3) {
33500 this.el.removeClass(this.invalidClass);
33501 this.el.addClass(this.validClass);
33503 this.el.removeClass('is-invalid');
33504 this.el.addClass('is-valid');
33508 this.fireEvent('valid', this);
33512 * Mark this field as invalid
33513 * @param {String} msg The validation message
33515 markInvalid : function(msg)
33517 if(this.indicator){
33518 this.indicator.removeClass('invisible');
33519 this.indicator.addClass('visible');
33521 if (Roo.bootstrap.version == 3) {
33522 this.el.removeClass(this.validClass);
33523 this.el.addClass(this.invalidClass);
33525 this.el.removeClass('is-valid');
33526 this.el.addClass('is-invalid');
33530 this.fireEvent('invalid', this, msg);
33536 Roo.apply(Roo.bootstrap.FieldLabel, {
33541 * register a FieldLabel Group
33542 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33544 register : function(label)
33546 if(this.groups.hasOwnProperty(label.target)){
33550 this.groups[label.target] = label;
33554 * fetch a FieldLabel Group based on the target
33555 * @param {string} target
33556 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33558 get: function(target) {
33559 if (typeof(this.groups[target]) == 'undefined') {
33563 return this.groups[target] ;
33572 * page DateSplitField.
33578 * @class Roo.bootstrap.DateSplitField
33579 * @extends Roo.bootstrap.Component
33580 * Bootstrap DateSplitField class
33581 * @cfg {string} fieldLabel - the label associated
33582 * @cfg {Number} labelWidth set the width of label (0-12)
33583 * @cfg {String} labelAlign (top|left)
33584 * @cfg {Boolean} dayAllowBlank (true|false) default false
33585 * @cfg {Boolean} monthAllowBlank (true|false) default false
33586 * @cfg {Boolean} yearAllowBlank (true|false) default false
33587 * @cfg {string} dayPlaceholder
33588 * @cfg {string} monthPlaceholder
33589 * @cfg {string} yearPlaceholder
33590 * @cfg {string} dayFormat default 'd'
33591 * @cfg {string} monthFormat default 'm'
33592 * @cfg {string} yearFormat default 'Y'
33593 * @cfg {Number} labellg set the width of label (1-12)
33594 * @cfg {Number} labelmd set the width of label (1-12)
33595 * @cfg {Number} labelsm set the width of label (1-12)
33596 * @cfg {Number} labelxs set the width of label (1-12)
33600 * Create a new DateSplitField
33601 * @param {Object} config The config object
33604 Roo.bootstrap.DateSplitField = function(config){
33605 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33611 * getting the data of years
33612 * @param {Roo.bootstrap.DateSplitField} this
33613 * @param {Object} years
33618 * getting the data of days
33619 * @param {Roo.bootstrap.DateSplitField} this
33620 * @param {Object} days
33625 * Fires after the field has been marked as invalid.
33626 * @param {Roo.form.Field} this
33627 * @param {String} msg The validation message
33632 * Fires after the field has been validated with no errors.
33633 * @param {Roo.form.Field} this
33639 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33642 labelAlign : 'top',
33644 dayAllowBlank : false,
33645 monthAllowBlank : false,
33646 yearAllowBlank : false,
33647 dayPlaceholder : '',
33648 monthPlaceholder : '',
33649 yearPlaceholder : '',
33653 isFormField : true,
33659 getAutoCreate : function()
33663 cls : 'row roo-date-split-field-group',
33668 cls : 'form-hidden-field roo-date-split-field-group-value',
33674 var labelCls = 'col-md-12';
33675 var contentCls = 'col-md-4';
33677 if(this.fieldLabel){
33681 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33685 html : this.fieldLabel
33690 if(this.labelAlign == 'left'){
33692 if(this.labelWidth > 12){
33693 label.style = "width: " + this.labelWidth + 'px';
33696 if(this.labelWidth < 13 && this.labelmd == 0){
33697 this.labelmd = this.labelWidth;
33700 if(this.labellg > 0){
33701 labelCls = ' col-lg-' + this.labellg;
33702 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33705 if(this.labelmd > 0){
33706 labelCls = ' col-md-' + this.labelmd;
33707 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33710 if(this.labelsm > 0){
33711 labelCls = ' col-sm-' + this.labelsm;
33712 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33715 if(this.labelxs > 0){
33716 labelCls = ' col-xs-' + this.labelxs;
33717 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33721 label.cls += ' ' + labelCls;
33723 cfg.cn.push(label);
33726 Roo.each(['day', 'month', 'year'], function(t){
33729 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33736 inputEl: function ()
33738 return this.el.select('.roo-date-split-field-group-value', true).first();
33741 onRender : function(ct, position)
33745 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33747 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33749 this.dayField = new Roo.bootstrap.ComboBox({
33750 allowBlank : this.dayAllowBlank,
33751 alwaysQuery : true,
33752 displayField : 'value',
33755 forceSelection : true,
33757 placeholder : this.dayPlaceholder,
33758 selectOnFocus : true,
33759 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33760 triggerAction : 'all',
33762 valueField : 'value',
33763 store : new Roo.data.SimpleStore({
33764 data : (function() {
33766 _this.fireEvent('days', _this, days);
33769 fields : [ 'value' ]
33772 select : function (_self, record, index)
33774 _this.setValue(_this.getValue());
33779 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33781 this.monthField = new Roo.bootstrap.MonthField({
33782 after : '<i class=\"fa fa-calendar\"></i>',
33783 allowBlank : this.monthAllowBlank,
33784 placeholder : this.monthPlaceholder,
33787 render : function (_self)
33789 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33790 e.preventDefault();
33794 select : function (_self, oldvalue, newvalue)
33796 _this.setValue(_this.getValue());
33801 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33803 this.yearField = new Roo.bootstrap.ComboBox({
33804 allowBlank : this.yearAllowBlank,
33805 alwaysQuery : true,
33806 displayField : 'value',
33809 forceSelection : true,
33811 placeholder : this.yearPlaceholder,
33812 selectOnFocus : true,
33813 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33814 triggerAction : 'all',
33816 valueField : 'value',
33817 store : new Roo.data.SimpleStore({
33818 data : (function() {
33820 _this.fireEvent('years', _this, years);
33823 fields : [ 'value' ]
33826 select : function (_self, record, index)
33828 _this.setValue(_this.getValue());
33833 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33836 setValue : function(v, format)
33838 this.inputEl.dom.value = v;
33840 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33842 var d = Date.parseDate(v, f);
33849 this.setDay(d.format(this.dayFormat));
33850 this.setMonth(d.format(this.monthFormat));
33851 this.setYear(d.format(this.yearFormat));
33858 setDay : function(v)
33860 this.dayField.setValue(v);
33861 this.inputEl.dom.value = this.getValue();
33866 setMonth : function(v)
33868 this.monthField.setValue(v, true);
33869 this.inputEl.dom.value = this.getValue();
33874 setYear : function(v)
33876 this.yearField.setValue(v);
33877 this.inputEl.dom.value = this.getValue();
33882 getDay : function()
33884 return this.dayField.getValue();
33887 getMonth : function()
33889 return this.monthField.getValue();
33892 getYear : function()
33894 return this.yearField.getValue();
33897 getValue : function()
33899 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33901 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33911 this.inputEl.dom.value = '';
33916 validate : function()
33918 var d = this.dayField.validate();
33919 var m = this.monthField.validate();
33920 var y = this.yearField.validate();
33925 (!this.dayAllowBlank && !d) ||
33926 (!this.monthAllowBlank && !m) ||
33927 (!this.yearAllowBlank && !y)
33932 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33941 this.markInvalid();
33946 markValid : function()
33949 var label = this.el.select('label', true).first();
33950 var icon = this.el.select('i.fa-star', true).first();
33956 this.fireEvent('valid', this);
33960 * Mark this field as invalid
33961 * @param {String} msg The validation message
33963 markInvalid : function(msg)
33966 var label = this.el.select('label', true).first();
33967 var icon = this.el.select('i.fa-star', true).first();
33969 if(label && !icon){
33970 this.el.select('.roo-date-split-field-label', true).createChild({
33972 cls : 'text-danger fa fa-lg fa-star',
33973 tooltip : 'This field is required',
33974 style : 'margin-right:5px;'
33978 this.fireEvent('invalid', this, msg);
33981 clearInvalid : function()
33983 var label = this.el.select('label', true).first();
33984 var icon = this.el.select('i.fa-star', true).first();
33990 this.fireEvent('valid', this);
33993 getName: function()
34003 * http://masonry.desandro.com
34005 * The idea is to render all the bricks based on vertical width...
34007 * The original code extends 'outlayer' - we might need to use that....
34013 * @class Roo.bootstrap.LayoutMasonry
34014 * @extends Roo.bootstrap.Component
34015 * Bootstrap Layout Masonry class
34018 * Create a new Element
34019 * @param {Object} config The config object
34022 Roo.bootstrap.LayoutMasonry = function(config){
34024 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34028 Roo.bootstrap.LayoutMasonry.register(this);
34034 * Fire after layout the items
34035 * @param {Roo.bootstrap.LayoutMasonry} this
34036 * @param {Roo.EventObject} e
34043 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34046 * @cfg {Boolean} isLayoutInstant = no animation?
34048 isLayoutInstant : false, // needed?
34051 * @cfg {Number} boxWidth width of the columns
34056 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34061 * @cfg {Number} padWidth padding below box..
34066 * @cfg {Number} gutter gutter width..
34071 * @cfg {Number} maxCols maximum number of columns
34077 * @cfg {Boolean} isAutoInitial defalut true
34079 isAutoInitial : true,
34084 * @cfg {Boolean} isHorizontal defalut false
34086 isHorizontal : false,
34088 currentSize : null,
34094 bricks: null, //CompositeElement
34098 _isLayoutInited : false,
34100 // isAlternative : false, // only use for vertical layout...
34103 * @cfg {Number} alternativePadWidth padding below box..
34105 alternativePadWidth : 50,
34107 selectedBrick : [],
34109 getAutoCreate : function(){
34111 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34115 cls: 'blog-masonary-wrapper ' + this.cls,
34117 cls : 'mas-boxes masonary'
34124 getChildContainer: function( )
34126 if (this.boxesEl) {
34127 return this.boxesEl;
34130 this.boxesEl = this.el.select('.mas-boxes').first();
34132 return this.boxesEl;
34136 initEvents : function()
34140 if(this.isAutoInitial){
34141 Roo.log('hook children rendered');
34142 this.on('childrenrendered', function() {
34143 Roo.log('children rendered');
34149 initial : function()
34151 this.selectedBrick = [];
34153 this.currentSize = this.el.getBox(true);
34155 Roo.EventManager.onWindowResize(this.resize, this);
34157 if(!this.isAutoInitial){
34165 //this.layout.defer(500,this);
34169 resize : function()
34171 var cs = this.el.getBox(true);
34174 this.currentSize.width == cs.width &&
34175 this.currentSize.x == cs.x &&
34176 this.currentSize.height == cs.height &&
34177 this.currentSize.y == cs.y
34179 Roo.log("no change in with or X or Y");
34183 this.currentSize = cs;
34189 layout : function()
34191 this._resetLayout();
34193 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34195 this.layoutItems( isInstant );
34197 this._isLayoutInited = true;
34199 this.fireEvent('layout', this);
34203 _resetLayout : function()
34205 if(this.isHorizontal){
34206 this.horizontalMeasureColumns();
34210 this.verticalMeasureColumns();
34214 verticalMeasureColumns : function()
34216 this.getContainerWidth();
34218 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34219 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34223 var boxWidth = this.boxWidth + this.padWidth;
34225 if(this.containerWidth < this.boxWidth){
34226 boxWidth = this.containerWidth
34229 var containerWidth = this.containerWidth;
34231 var cols = Math.floor(containerWidth / boxWidth);
34233 this.cols = Math.max( cols, 1 );
34235 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34237 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34239 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34241 this.colWidth = boxWidth + avail - this.padWidth;
34243 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34244 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34247 horizontalMeasureColumns : function()
34249 this.getContainerWidth();
34251 var boxWidth = this.boxWidth;
34253 if(this.containerWidth < boxWidth){
34254 boxWidth = this.containerWidth;
34257 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34259 this.el.setHeight(boxWidth);
34263 getContainerWidth : function()
34265 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34268 layoutItems : function( isInstant )
34270 Roo.log(this.bricks);
34272 var items = Roo.apply([], this.bricks);
34274 if(this.isHorizontal){
34275 this._horizontalLayoutItems( items , isInstant );
34279 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34280 // this._verticalAlternativeLayoutItems( items , isInstant );
34284 this._verticalLayoutItems( items , isInstant );
34288 _verticalLayoutItems : function ( items , isInstant)
34290 if ( !items || !items.length ) {
34295 ['xs', 'xs', 'xs', 'tall'],
34296 ['xs', 'xs', 'tall'],
34297 ['xs', 'xs', 'sm'],
34298 ['xs', 'xs', 'xs'],
34304 ['sm', 'xs', 'xs'],
34308 ['tall', 'xs', 'xs', 'xs'],
34309 ['tall', 'xs', 'xs'],
34321 Roo.each(items, function(item, k){
34323 switch (item.size) {
34324 // these layouts take up a full box,
34335 boxes.push([item]);
34358 var filterPattern = function(box, length)
34366 var pattern = box.slice(0, length);
34370 Roo.each(pattern, function(i){
34371 format.push(i.size);
34374 Roo.each(standard, function(s){
34376 if(String(s) != String(format)){
34385 if(!match && length == 1){
34390 filterPattern(box, length - 1);
34394 queue.push(pattern);
34396 box = box.slice(length, box.length);
34398 filterPattern(box, 4);
34404 Roo.each(boxes, function(box, k){
34410 if(box.length == 1){
34415 filterPattern(box, 4);
34419 this._processVerticalLayoutQueue( queue, isInstant );
34423 // _verticalAlternativeLayoutItems : function( items , isInstant )
34425 // if ( !items || !items.length ) {
34429 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34433 _horizontalLayoutItems : function ( items , isInstant)
34435 if ( !items || !items.length || items.length < 3) {
34441 var eItems = items.slice(0, 3);
34443 items = items.slice(3, items.length);
34446 ['xs', 'xs', 'xs', 'wide'],
34447 ['xs', 'xs', 'wide'],
34448 ['xs', 'xs', 'sm'],
34449 ['xs', 'xs', 'xs'],
34455 ['sm', 'xs', 'xs'],
34459 ['wide', 'xs', 'xs', 'xs'],
34460 ['wide', 'xs', 'xs'],
34473 Roo.each(items, function(item, k){
34475 switch (item.size) {
34486 boxes.push([item]);
34510 var filterPattern = function(box, length)
34518 var pattern = box.slice(0, length);
34522 Roo.each(pattern, function(i){
34523 format.push(i.size);
34526 Roo.each(standard, function(s){
34528 if(String(s) != String(format)){
34537 if(!match && length == 1){
34542 filterPattern(box, length - 1);
34546 queue.push(pattern);
34548 box = box.slice(length, box.length);
34550 filterPattern(box, 4);
34556 Roo.each(boxes, function(box, k){
34562 if(box.length == 1){
34567 filterPattern(box, 4);
34574 var pos = this.el.getBox(true);
34578 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34580 var hit_end = false;
34582 Roo.each(queue, function(box){
34586 Roo.each(box, function(b){
34588 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34598 Roo.each(box, function(b){
34600 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34603 mx = Math.max(mx, b.x);
34607 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34611 Roo.each(box, function(b){
34613 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34627 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34630 /** Sets position of item in DOM
34631 * @param {Element} item
34632 * @param {Number} x - horizontal position
34633 * @param {Number} y - vertical position
34634 * @param {Boolean} isInstant - disables transitions
34636 _processVerticalLayoutQueue : function( queue, isInstant )
34638 var pos = this.el.getBox(true);
34643 for (var i = 0; i < this.cols; i++){
34647 Roo.each(queue, function(box, k){
34649 var col = k % this.cols;
34651 Roo.each(box, function(b,kk){
34653 b.el.position('absolute');
34655 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34656 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34658 if(b.size == 'md-left' || b.size == 'md-right'){
34659 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34660 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34663 b.el.setWidth(width);
34664 b.el.setHeight(height);
34666 b.el.select('iframe',true).setSize(width,height);
34670 for (var i = 0; i < this.cols; i++){
34672 if(maxY[i] < maxY[col]){
34677 col = Math.min(col, i);
34681 x = pos.x + col * (this.colWidth + this.padWidth);
34685 var positions = [];
34687 switch (box.length){
34689 positions = this.getVerticalOneBoxColPositions(x, y, box);
34692 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34695 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34698 positions = this.getVerticalFourBoxColPositions(x, y, box);
34704 Roo.each(box, function(b,kk){
34706 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34708 var sz = b.el.getSize();
34710 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34718 for (var i = 0; i < this.cols; i++){
34719 mY = Math.max(mY, maxY[i]);
34722 this.el.setHeight(mY - pos.y);
34726 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34728 // var pos = this.el.getBox(true);
34731 // var maxX = pos.right;
34733 // var maxHeight = 0;
34735 // Roo.each(items, function(item, k){
34739 // item.el.position('absolute');
34741 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34743 // item.el.setWidth(width);
34745 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34747 // item.el.setHeight(height);
34750 // item.el.setXY([x, y], isInstant ? false : true);
34752 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34755 // y = y + height + this.alternativePadWidth;
34757 // maxHeight = maxHeight + height + this.alternativePadWidth;
34761 // this.el.setHeight(maxHeight);
34765 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34767 var pos = this.el.getBox(true);
34772 var maxX = pos.right;
34774 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34776 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34778 Roo.each(queue, function(box, k){
34780 Roo.each(box, function(b, kk){
34782 b.el.position('absolute');
34784 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34785 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34787 if(b.size == 'md-left' || b.size == 'md-right'){
34788 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34789 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34792 b.el.setWidth(width);
34793 b.el.setHeight(height);
34801 var positions = [];
34803 switch (box.length){
34805 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34808 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34811 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34814 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34820 Roo.each(box, function(b,kk){
34822 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34824 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34832 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34834 Roo.each(eItems, function(b,k){
34836 b.size = (k == 0) ? 'sm' : 'xs';
34837 b.x = (k == 0) ? 2 : 1;
34838 b.y = (k == 0) ? 2 : 1;
34840 b.el.position('absolute');
34842 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34844 b.el.setWidth(width);
34846 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34848 b.el.setHeight(height);
34852 var positions = [];
34855 x : maxX - this.unitWidth * 2 - this.gutter,
34860 x : maxX - this.unitWidth,
34861 y : minY + (this.unitWidth + this.gutter) * 2
34865 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34869 Roo.each(eItems, function(b,k){
34871 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34877 getVerticalOneBoxColPositions : function(x, y, box)
34881 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34883 if(box[0].size == 'md-left'){
34887 if(box[0].size == 'md-right'){
34892 x : x + (this.unitWidth + this.gutter) * rand,
34899 getVerticalTwoBoxColPositions : function(x, y, box)
34903 if(box[0].size == 'xs'){
34907 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34911 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34925 x : x + (this.unitWidth + this.gutter) * 2,
34926 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34933 getVerticalThreeBoxColPositions : function(x, y, box)
34937 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34945 x : x + (this.unitWidth + this.gutter) * 1,
34950 x : x + (this.unitWidth + this.gutter) * 2,
34958 if(box[0].size == 'xs' && box[1].size == 'xs'){
34967 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34971 x : x + (this.unitWidth + this.gutter) * 1,
34985 x : x + (this.unitWidth + this.gutter) * 2,
34990 x : x + (this.unitWidth + this.gutter) * 2,
34991 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34998 getVerticalFourBoxColPositions : function(x, y, box)
35002 if(box[0].size == 'xs'){
35011 y : y + (this.unitHeight + this.gutter) * 1
35016 y : y + (this.unitHeight + this.gutter) * 2
35020 x : x + (this.unitWidth + this.gutter) * 1,
35034 x : x + (this.unitWidth + this.gutter) * 2,
35039 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35040 y : y + (this.unitHeight + this.gutter) * 1
35044 x : x + (this.unitWidth + this.gutter) * 2,
35045 y : y + (this.unitWidth + this.gutter) * 2
35052 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35056 if(box[0].size == 'md-left'){
35058 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35065 if(box[0].size == 'md-right'){
35067 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35068 y : minY + (this.unitWidth + this.gutter) * 1
35074 var rand = Math.floor(Math.random() * (4 - box[0].y));
35077 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35078 y : minY + (this.unitWidth + this.gutter) * rand
35085 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35089 if(box[0].size == 'xs'){
35092 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35097 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35098 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35106 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35111 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35112 y : minY + (this.unitWidth + this.gutter) * 2
35119 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35123 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35132 y : minY + (this.unitWidth + this.gutter) * 1
35136 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35137 y : minY + (this.unitWidth + this.gutter) * 2
35144 if(box[0].size == 'xs' && box[1].size == 'xs'){
35147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35152 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35157 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158 y : minY + (this.unitWidth + this.gutter) * 1
35166 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35171 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35172 y : minY + (this.unitWidth + this.gutter) * 2
35176 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35177 y : minY + (this.unitWidth + this.gutter) * 2
35184 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35188 if(box[0].size == 'xs'){
35191 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35196 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35201 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),
35206 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35207 y : minY + (this.unitWidth + this.gutter) * 1
35215 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35220 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35221 y : minY + (this.unitWidth + this.gutter) * 2
35225 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35226 y : minY + (this.unitWidth + this.gutter) * 2
35230 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),
35231 y : minY + (this.unitWidth + this.gutter) * 2
35239 * remove a Masonry Brick
35240 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35242 removeBrick : function(brick_id)
35248 for (var i = 0; i<this.bricks.length; i++) {
35249 if (this.bricks[i].id == brick_id) {
35250 this.bricks.splice(i,1);
35251 this.el.dom.removeChild(Roo.get(brick_id).dom);
35258 * adds a Masonry Brick
35259 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35261 addBrick : function(cfg)
35263 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35264 //this.register(cn);
35265 cn.parentId = this.id;
35266 cn.render(this.el);
35271 * register a Masonry Brick
35272 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35275 register : function(brick)
35277 this.bricks.push(brick);
35278 brick.masonryId = this.id;
35282 * clear all the Masonry Brick
35284 clearAll : function()
35287 //this.getChildContainer().dom.innerHTML = "";
35288 this.el.dom.innerHTML = '';
35291 getSelected : function()
35293 if (!this.selectedBrick) {
35297 return this.selectedBrick;
35301 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35305 * register a Masonry Layout
35306 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35309 register : function(layout)
35311 this.groups[layout.id] = layout;
35314 * fetch a Masonry Layout based on the masonry layout ID
35315 * @param {string} the masonry layout to add
35316 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35319 get: function(layout_id) {
35320 if (typeof(this.groups[layout_id]) == 'undefined') {
35323 return this.groups[layout_id] ;
35335 * http://masonry.desandro.com
35337 * The idea is to render all the bricks based on vertical width...
35339 * The original code extends 'outlayer' - we might need to use that....
35345 * @class Roo.bootstrap.LayoutMasonryAuto
35346 * @extends Roo.bootstrap.Component
35347 * Bootstrap Layout Masonry class
35350 * Create a new Element
35351 * @param {Object} config The config object
35354 Roo.bootstrap.LayoutMasonryAuto = function(config){
35355 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35358 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35361 * @cfg {Boolean} isFitWidth - resize the width..
35363 isFitWidth : false, // options..
35365 * @cfg {Boolean} isOriginLeft = left align?
35367 isOriginLeft : true,
35369 * @cfg {Boolean} isOriginTop = top align?
35371 isOriginTop : false,
35373 * @cfg {Boolean} isLayoutInstant = no animation?
35375 isLayoutInstant : false, // needed?
35377 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35379 isResizingContainer : true,
35381 * @cfg {Number} columnWidth width of the columns
35387 * @cfg {Number} maxCols maximum number of columns
35392 * @cfg {Number} padHeight padding below box..
35398 * @cfg {Boolean} isAutoInitial defalut true
35401 isAutoInitial : true,
35407 initialColumnWidth : 0,
35408 currentSize : null,
35410 colYs : null, // array.
35417 bricks: null, //CompositeElement
35418 cols : 0, // array?
35419 // element : null, // wrapped now this.el
35420 _isLayoutInited : null,
35423 getAutoCreate : function(){
35427 cls: 'blog-masonary-wrapper ' + this.cls,
35429 cls : 'mas-boxes masonary'
35436 getChildContainer: function( )
35438 if (this.boxesEl) {
35439 return this.boxesEl;
35442 this.boxesEl = this.el.select('.mas-boxes').first();
35444 return this.boxesEl;
35448 initEvents : function()
35452 if(this.isAutoInitial){
35453 Roo.log('hook children rendered');
35454 this.on('childrenrendered', function() {
35455 Roo.log('children rendered');
35462 initial : function()
35464 this.reloadItems();
35466 this.currentSize = this.el.getBox(true);
35468 /// was window resize... - let's see if this works..
35469 Roo.EventManager.onWindowResize(this.resize, this);
35471 if(!this.isAutoInitial){
35476 this.layout.defer(500,this);
35479 reloadItems: function()
35481 this.bricks = this.el.select('.masonry-brick', true);
35483 this.bricks.each(function(b) {
35484 //Roo.log(b.getSize());
35485 if (!b.attr('originalwidth')) {
35486 b.attr('originalwidth', b.getSize().width);
35491 Roo.log(this.bricks.elements.length);
35494 resize : function()
35497 var cs = this.el.getBox(true);
35499 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35500 Roo.log("no change in with or X");
35503 this.currentSize = cs;
35507 layout : function()
35510 this._resetLayout();
35511 //this._manageStamps();
35513 // don't animate first layout
35514 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35515 this.layoutItems( isInstant );
35517 // flag for initalized
35518 this._isLayoutInited = true;
35521 layoutItems : function( isInstant )
35523 //var items = this._getItemsForLayout( this.items );
35524 // original code supports filtering layout items.. we just ignore it..
35526 this._layoutItems( this.bricks , isInstant );
35528 this._postLayout();
35530 _layoutItems : function ( items , isInstant)
35532 //this.fireEvent( 'layout', this, items );
35535 if ( !items || !items.elements.length ) {
35536 // no items, emit event with empty array
35541 items.each(function(item) {
35542 Roo.log("layout item");
35544 // get x/y object from method
35545 var position = this._getItemLayoutPosition( item );
35547 position.item = item;
35548 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35549 queue.push( position );
35552 this._processLayoutQueue( queue );
35554 /** Sets position of item in DOM
35555 * @param {Element} item
35556 * @param {Number} x - horizontal position
35557 * @param {Number} y - vertical position
35558 * @param {Boolean} isInstant - disables transitions
35560 _processLayoutQueue : function( queue )
35562 for ( var i=0, len = queue.length; i < len; i++ ) {
35563 var obj = queue[i];
35564 obj.item.position('absolute');
35565 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35571 * Any logic you want to do after each layout,
35572 * i.e. size the container
35574 _postLayout : function()
35576 this.resizeContainer();
35579 resizeContainer : function()
35581 if ( !this.isResizingContainer ) {
35584 var size = this._getContainerSize();
35586 this.el.setSize(size.width,size.height);
35587 this.boxesEl.setSize(size.width,size.height);
35593 _resetLayout : function()
35595 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35596 this.colWidth = this.el.getWidth();
35597 //this.gutter = this.el.getWidth();
35599 this.measureColumns();
35605 this.colYs.push( 0 );
35611 measureColumns : function()
35613 this.getContainerWidth();
35614 // if columnWidth is 0, default to outerWidth of first item
35615 if ( !this.columnWidth ) {
35616 var firstItem = this.bricks.first();
35617 Roo.log(firstItem);
35618 this.columnWidth = this.containerWidth;
35619 if (firstItem && firstItem.attr('originalwidth') ) {
35620 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35622 // columnWidth fall back to item of first element
35623 Roo.log("set column width?");
35624 this.initialColumnWidth = this.columnWidth ;
35626 // if first elem has no width, default to size of container
35631 if (this.initialColumnWidth) {
35632 this.columnWidth = this.initialColumnWidth;
35637 // column width is fixed at the top - however if container width get's smaller we should
35640 // this bit calcs how man columns..
35642 var columnWidth = this.columnWidth += this.gutter;
35644 // calculate columns
35645 var containerWidth = this.containerWidth + this.gutter;
35647 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35648 // fix rounding errors, typically with gutters
35649 var excess = columnWidth - containerWidth % columnWidth;
35652 // if overshoot is less than a pixel, round up, otherwise floor it
35653 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35654 cols = Math[ mathMethod ]( cols );
35655 this.cols = Math.max( cols, 1 );
35656 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35658 // padding positioning..
35659 var totalColWidth = this.cols * this.columnWidth;
35660 var padavail = this.containerWidth - totalColWidth;
35661 // so for 2 columns - we need 3 'pads'
35663 var padNeeded = (1+this.cols) * this.padWidth;
35665 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35667 this.columnWidth += padExtra
35668 //this.padWidth = Math.floor(padavail / ( this.cols));
35670 // adjust colum width so that padding is fixed??
35672 // we have 3 columns ... total = width * 3
35673 // we have X left over... that should be used by
35675 //if (this.expandC) {
35683 getContainerWidth : function()
35685 /* // container is parent if fit width
35686 var container = this.isFitWidth ? this.element.parentNode : this.element;
35687 // check that this.size and size are there
35688 // IE8 triggers resize on body size change, so they might not be
35690 var size = getSize( container ); //FIXME
35691 this.containerWidth = size && size.innerWidth; //FIXME
35694 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35698 _getItemLayoutPosition : function( item ) // what is item?
35700 // we resize the item to our columnWidth..
35702 item.setWidth(this.columnWidth);
35703 item.autoBoxAdjust = false;
35705 var sz = item.getSize();
35707 // how many columns does this brick span
35708 var remainder = this.containerWidth % this.columnWidth;
35710 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35711 // round if off by 1 pixel, otherwise use ceil
35712 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35713 colSpan = Math.min( colSpan, this.cols );
35715 // normally this should be '1' as we dont' currently allow multi width columns..
35717 var colGroup = this._getColGroup( colSpan );
35718 // get the minimum Y value from the columns
35719 var minimumY = Math.min.apply( Math, colGroup );
35720 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35722 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35724 // position the brick
35726 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35727 y: this.currentSize.y + minimumY + this.padHeight
35731 // apply setHeight to necessary columns
35732 var setHeight = minimumY + sz.height + this.padHeight;
35733 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35735 var setSpan = this.cols + 1 - colGroup.length;
35736 for ( var i = 0; i < setSpan; i++ ) {
35737 this.colYs[ shortColIndex + i ] = setHeight ;
35744 * @param {Number} colSpan - number of columns the element spans
35745 * @returns {Array} colGroup
35747 _getColGroup : function( colSpan )
35749 if ( colSpan < 2 ) {
35750 // if brick spans only one column, use all the column Ys
35755 // how many different places could this brick fit horizontally
35756 var groupCount = this.cols + 1 - colSpan;
35757 // for each group potential horizontal position
35758 for ( var i = 0; i < groupCount; i++ ) {
35759 // make an array of colY values for that one group
35760 var groupColYs = this.colYs.slice( i, i + colSpan );
35761 // and get the max value of the array
35762 colGroup[i] = Math.max.apply( Math, groupColYs );
35767 _manageStamp : function( stamp )
35769 var stampSize = stamp.getSize();
35770 var offset = stamp.getBox();
35771 // get the columns that this stamp affects
35772 var firstX = this.isOriginLeft ? offset.x : offset.right;
35773 var lastX = firstX + stampSize.width;
35774 var firstCol = Math.floor( firstX / this.columnWidth );
35775 firstCol = Math.max( 0, firstCol );
35777 var lastCol = Math.floor( lastX / this.columnWidth );
35778 // lastCol should not go over if multiple of columnWidth #425
35779 lastCol -= lastX % this.columnWidth ? 0 : 1;
35780 lastCol = Math.min( this.cols - 1, lastCol );
35782 // set colYs to bottom of the stamp
35783 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35786 for ( var i = firstCol; i <= lastCol; i++ ) {
35787 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35792 _getContainerSize : function()
35794 this.maxY = Math.max.apply( Math, this.colYs );
35799 if ( this.isFitWidth ) {
35800 size.width = this._getContainerFitWidth();
35806 _getContainerFitWidth : function()
35808 var unusedCols = 0;
35809 // count unused columns
35812 if ( this.colYs[i] !== 0 ) {
35817 // fit container to columns that have been used
35818 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35821 needsResizeLayout : function()
35823 var previousWidth = this.containerWidth;
35824 this.getContainerWidth();
35825 return previousWidth !== this.containerWidth;
35840 * @class Roo.bootstrap.MasonryBrick
35841 * @extends Roo.bootstrap.Component
35842 * Bootstrap MasonryBrick class
35845 * Create a new MasonryBrick
35846 * @param {Object} config The config object
35849 Roo.bootstrap.MasonryBrick = function(config){
35851 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35853 Roo.bootstrap.MasonryBrick.register(this);
35859 * When a MasonryBrick is clcik
35860 * @param {Roo.bootstrap.MasonryBrick} this
35861 * @param {Roo.EventObject} e
35867 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35870 * @cfg {String} title
35874 * @cfg {String} html
35878 * @cfg {String} bgimage
35882 * @cfg {String} videourl
35886 * @cfg {String} cls
35890 * @cfg {String} href
35894 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35899 * @cfg {String} placetitle (center|bottom)
35904 * @cfg {Boolean} isFitContainer defalut true
35906 isFitContainer : true,
35909 * @cfg {Boolean} preventDefault defalut false
35911 preventDefault : false,
35914 * @cfg {Boolean} inverse defalut false
35916 maskInverse : false,
35918 getAutoCreate : function()
35920 if(!this.isFitContainer){
35921 return this.getSplitAutoCreate();
35924 var cls = 'masonry-brick masonry-brick-full';
35926 if(this.href.length){
35927 cls += ' masonry-brick-link';
35930 if(this.bgimage.length){
35931 cls += ' masonry-brick-image';
35934 if(this.maskInverse){
35935 cls += ' mask-inverse';
35938 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35939 cls += ' enable-mask';
35943 cls += ' masonry-' + this.size + '-brick';
35946 if(this.placetitle.length){
35948 switch (this.placetitle) {
35950 cls += ' masonry-center-title';
35953 cls += ' masonry-bottom-title';
35960 if(!this.html.length && !this.bgimage.length){
35961 cls += ' masonry-center-title';
35964 if(!this.html.length && this.bgimage.length){
35965 cls += ' masonry-bottom-title';
35970 cls += ' ' + this.cls;
35974 tag: (this.href.length) ? 'a' : 'div',
35979 cls: 'masonry-brick-mask'
35983 cls: 'masonry-brick-paragraph',
35989 if(this.href.length){
35990 cfg.href = this.href;
35993 var cn = cfg.cn[1].cn;
35995 if(this.title.length){
35998 cls: 'masonry-brick-title',
36003 if(this.html.length){
36006 cls: 'masonry-brick-text',
36011 if (!this.title.length && !this.html.length) {
36012 cfg.cn[1].cls += ' hide';
36015 if(this.bgimage.length){
36018 cls: 'masonry-brick-image-view',
36023 if(this.videourl.length){
36024 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36025 // youtube support only?
36028 cls: 'masonry-brick-image-view',
36031 allowfullscreen : true
36039 getSplitAutoCreate : function()
36041 var cls = 'masonry-brick masonry-brick-split';
36043 if(this.href.length){
36044 cls += ' masonry-brick-link';
36047 if(this.bgimage.length){
36048 cls += ' masonry-brick-image';
36052 cls += ' masonry-' + this.size + '-brick';
36055 switch (this.placetitle) {
36057 cls += ' masonry-center-title';
36060 cls += ' masonry-bottom-title';
36063 if(!this.bgimage.length){
36064 cls += ' masonry-center-title';
36067 if(this.bgimage.length){
36068 cls += ' masonry-bottom-title';
36074 cls += ' ' + this.cls;
36078 tag: (this.href.length) ? 'a' : 'div',
36083 cls: 'masonry-brick-split-head',
36087 cls: 'masonry-brick-paragraph',
36094 cls: 'masonry-brick-split-body',
36100 if(this.href.length){
36101 cfg.href = this.href;
36104 if(this.title.length){
36105 cfg.cn[0].cn[0].cn.push({
36107 cls: 'masonry-brick-title',
36112 if(this.html.length){
36113 cfg.cn[1].cn.push({
36115 cls: 'masonry-brick-text',
36120 if(this.bgimage.length){
36121 cfg.cn[0].cn.push({
36123 cls: 'masonry-brick-image-view',
36128 if(this.videourl.length){
36129 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36130 // youtube support only?
36131 cfg.cn[0].cn.cn.push({
36133 cls: 'masonry-brick-image-view',
36136 allowfullscreen : true
36143 initEvents: function()
36145 switch (this.size) {
36178 this.el.on('touchstart', this.onTouchStart, this);
36179 this.el.on('touchmove', this.onTouchMove, this);
36180 this.el.on('touchend', this.onTouchEnd, this);
36181 this.el.on('contextmenu', this.onContextMenu, this);
36183 this.el.on('mouseenter' ,this.enter, this);
36184 this.el.on('mouseleave', this.leave, this);
36185 this.el.on('click', this.onClick, this);
36188 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36189 this.parent().bricks.push(this);
36194 onClick: function(e, el)
36196 var time = this.endTimer - this.startTimer;
36197 // Roo.log(e.preventDefault());
36200 e.preventDefault();
36205 if(!this.preventDefault){
36209 e.preventDefault();
36211 if (this.activeClass != '') {
36212 this.selectBrick();
36215 this.fireEvent('click', this, e);
36218 enter: function(e, el)
36220 e.preventDefault();
36222 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36226 if(this.bgimage.length && this.html.length){
36227 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36231 leave: function(e, el)
36233 e.preventDefault();
36235 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36239 if(this.bgimage.length && this.html.length){
36240 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36244 onTouchStart: function(e, el)
36246 // e.preventDefault();
36248 this.touchmoved = false;
36250 if(!this.isFitContainer){
36254 if(!this.bgimage.length || !this.html.length){
36258 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36260 this.timer = new Date().getTime();
36264 onTouchMove: function(e, el)
36266 this.touchmoved = true;
36269 onContextMenu : function(e,el)
36271 e.preventDefault();
36272 e.stopPropagation();
36276 onTouchEnd: function(e, el)
36278 // e.preventDefault();
36280 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36287 if(!this.bgimage.length || !this.html.length){
36289 if(this.href.length){
36290 window.location.href = this.href;
36296 if(!this.isFitContainer){
36300 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36302 window.location.href = this.href;
36305 //selection on single brick only
36306 selectBrick : function() {
36308 if (!this.parentId) {
36312 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36313 var index = m.selectedBrick.indexOf(this.id);
36316 m.selectedBrick.splice(index,1);
36317 this.el.removeClass(this.activeClass);
36321 for(var i = 0; i < m.selectedBrick.length; i++) {
36322 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36323 b.el.removeClass(b.activeClass);
36326 m.selectedBrick = [];
36328 m.selectedBrick.push(this.id);
36329 this.el.addClass(this.activeClass);
36333 isSelected : function(){
36334 return this.el.hasClass(this.activeClass);
36339 Roo.apply(Roo.bootstrap.MasonryBrick, {
36342 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36344 * register a Masonry Brick
36345 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36348 register : function(brick)
36350 //this.groups[brick.id] = brick;
36351 this.groups.add(brick.id, brick);
36354 * fetch a masonry brick based on the masonry brick ID
36355 * @param {string} the masonry brick to add
36356 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36359 get: function(brick_id)
36361 // if (typeof(this.groups[brick_id]) == 'undefined') {
36364 // return this.groups[brick_id] ;
36366 if(this.groups.key(brick_id)) {
36367 return this.groups.key(brick_id);
36385 * @class Roo.bootstrap.Brick
36386 * @extends Roo.bootstrap.Component
36387 * Bootstrap Brick class
36390 * Create a new Brick
36391 * @param {Object} config The config object
36394 Roo.bootstrap.Brick = function(config){
36395 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36401 * When a Brick is click
36402 * @param {Roo.bootstrap.Brick} this
36403 * @param {Roo.EventObject} e
36409 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36412 * @cfg {String} title
36416 * @cfg {String} html
36420 * @cfg {String} bgimage
36424 * @cfg {String} cls
36428 * @cfg {String} href
36432 * @cfg {String} video
36436 * @cfg {Boolean} square
36440 getAutoCreate : function()
36442 var cls = 'roo-brick';
36444 if(this.href.length){
36445 cls += ' roo-brick-link';
36448 if(this.bgimage.length){
36449 cls += ' roo-brick-image';
36452 if(!this.html.length && !this.bgimage.length){
36453 cls += ' roo-brick-center-title';
36456 if(!this.html.length && this.bgimage.length){
36457 cls += ' roo-brick-bottom-title';
36461 cls += ' ' + this.cls;
36465 tag: (this.href.length) ? 'a' : 'div',
36470 cls: 'roo-brick-paragraph',
36476 if(this.href.length){
36477 cfg.href = this.href;
36480 var cn = cfg.cn[0].cn;
36482 if(this.title.length){
36485 cls: 'roo-brick-title',
36490 if(this.html.length){
36493 cls: 'roo-brick-text',
36500 if(this.bgimage.length){
36503 cls: 'roo-brick-image-view',
36511 initEvents: function()
36513 if(this.title.length || this.html.length){
36514 this.el.on('mouseenter' ,this.enter, this);
36515 this.el.on('mouseleave', this.leave, this);
36518 Roo.EventManager.onWindowResize(this.resize, this);
36520 if(this.bgimage.length){
36521 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36522 this.imageEl.on('load', this.onImageLoad, this);
36529 onImageLoad : function()
36534 resize : function()
36536 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36538 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36540 if(this.bgimage.length){
36541 var image = this.el.select('.roo-brick-image-view', true).first();
36543 image.setWidth(paragraph.getWidth());
36546 image.setHeight(paragraph.getWidth());
36549 this.el.setHeight(image.getHeight());
36550 paragraph.setHeight(image.getHeight());
36556 enter: function(e, el)
36558 e.preventDefault();
36560 if(this.bgimage.length){
36561 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36562 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36566 leave: function(e, el)
36568 e.preventDefault();
36570 if(this.bgimage.length){
36571 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36572 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36587 * @class Roo.bootstrap.NumberField
36588 * @extends Roo.bootstrap.Input
36589 * Bootstrap NumberField class
36595 * Create a new NumberField
36596 * @param {Object} config The config object
36599 Roo.bootstrap.NumberField = function(config){
36600 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36603 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36606 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36608 allowDecimals : true,
36610 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36612 decimalSeparator : ".",
36614 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36616 decimalPrecision : 2,
36618 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36620 allowNegative : true,
36623 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36627 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36629 minValue : Number.NEGATIVE_INFINITY,
36631 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36633 maxValue : Number.MAX_VALUE,
36635 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36637 minText : "The minimum value for this field is {0}",
36639 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36641 maxText : "The maximum value for this field is {0}",
36643 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36644 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36646 nanText : "{0} is not a valid number",
36648 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36650 thousandsDelimiter : false,
36652 * @cfg {String} valueAlign alignment of value
36654 valueAlign : "left",
36656 getAutoCreate : function()
36658 var hiddenInput = {
36662 cls: 'hidden-number-input'
36666 hiddenInput.name = this.name;
36671 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36673 this.name = hiddenInput.name;
36675 if(cfg.cn.length > 0) {
36676 cfg.cn.push(hiddenInput);
36683 initEvents : function()
36685 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36687 var allowed = "0123456789";
36689 if(this.allowDecimals){
36690 allowed += this.decimalSeparator;
36693 if(this.allowNegative){
36697 if(this.thousandsDelimiter) {
36701 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36703 var keyPress = function(e){
36705 var k = e.getKey();
36707 var c = e.getCharCode();
36710 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36711 allowed.indexOf(String.fromCharCode(c)) === -1
36717 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36721 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36726 this.el.on("keypress", keyPress, this);
36729 validateValue : function(value)
36732 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36736 var num = this.parseValue(value);
36739 this.markInvalid(String.format(this.nanText, value));
36743 if(num < this.minValue){
36744 this.markInvalid(String.format(this.minText, this.minValue));
36748 if(num > this.maxValue){
36749 this.markInvalid(String.format(this.maxText, this.maxValue));
36756 getValue : function()
36758 var v = this.hiddenEl().getValue();
36760 return this.fixPrecision(this.parseValue(v));
36763 parseValue : function(value)
36765 if(this.thousandsDelimiter) {
36767 r = new RegExp(",", "g");
36768 value = value.replace(r, "");
36771 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36772 return isNaN(value) ? '' : value;
36775 fixPrecision : function(value)
36777 if(this.thousandsDelimiter) {
36779 r = new RegExp(",", "g");
36780 value = value.replace(r, "");
36783 var nan = isNaN(value);
36785 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36786 return nan ? '' : value;
36788 return parseFloat(value).toFixed(this.decimalPrecision);
36791 setValue : function(v)
36793 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36799 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36801 this.inputEl().dom.value = (v == '') ? '' :
36802 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36804 if(!this.allowZero && v === '0') {
36805 this.hiddenEl().dom.value = '';
36806 this.inputEl().dom.value = '';
36813 decimalPrecisionFcn : function(v)
36815 return Math.floor(v);
36818 beforeBlur : function()
36820 var v = this.parseValue(this.getRawValue());
36822 if(v || v === 0 || v === ''){
36827 hiddenEl : function()
36829 return this.el.select('input.hidden-number-input',true).first();
36841 * @class Roo.bootstrap.DocumentSlider
36842 * @extends Roo.bootstrap.Component
36843 * Bootstrap DocumentSlider class
36846 * Create a new DocumentViewer
36847 * @param {Object} config The config object
36850 Roo.bootstrap.DocumentSlider = function(config){
36851 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36858 * Fire after initEvent
36859 * @param {Roo.bootstrap.DocumentSlider} this
36864 * Fire after update
36865 * @param {Roo.bootstrap.DocumentSlider} this
36871 * @param {Roo.bootstrap.DocumentSlider} this
36877 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36883 getAutoCreate : function()
36887 cls : 'roo-document-slider',
36891 cls : 'roo-document-slider-header',
36895 cls : 'roo-document-slider-header-title'
36901 cls : 'roo-document-slider-body',
36905 cls : 'roo-document-slider-prev',
36909 cls : 'fa fa-chevron-left'
36915 cls : 'roo-document-slider-thumb',
36919 cls : 'roo-document-slider-image'
36925 cls : 'roo-document-slider-next',
36929 cls : 'fa fa-chevron-right'
36941 initEvents : function()
36943 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36944 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36946 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36947 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36949 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36950 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36952 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36953 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36955 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36956 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36958 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36959 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36961 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36962 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36964 this.thumbEl.on('click', this.onClick, this);
36966 this.prevIndicator.on('click', this.prev, this);
36968 this.nextIndicator.on('click', this.next, this);
36972 initial : function()
36974 if(this.files.length){
36975 this.indicator = 1;
36979 this.fireEvent('initial', this);
36982 update : function()
36984 this.imageEl.attr('src', this.files[this.indicator - 1]);
36986 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36988 this.prevIndicator.show();
36990 if(this.indicator == 1){
36991 this.prevIndicator.hide();
36994 this.nextIndicator.show();
36996 if(this.indicator == this.files.length){
36997 this.nextIndicator.hide();
37000 this.thumbEl.scrollTo('top');
37002 this.fireEvent('update', this);
37005 onClick : function(e)
37007 e.preventDefault();
37009 this.fireEvent('click', this);
37014 e.preventDefault();
37016 this.indicator = Math.max(1, this.indicator - 1);
37023 e.preventDefault();
37025 this.indicator = Math.min(this.files.length, this.indicator + 1);
37039 * @class Roo.bootstrap.RadioSet
37040 * @extends Roo.bootstrap.Input
37041 * @children Roo.bootstrap.Radio
37042 * Bootstrap RadioSet class
37043 * @cfg {String} indicatorpos (left|right) default left
37044 * @cfg {Boolean} inline (true|false) inline the element (default true)
37045 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37047 * Create a new RadioSet
37048 * @param {Object} config The config object
37051 Roo.bootstrap.RadioSet = function(config){
37053 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37057 Roo.bootstrap.RadioSet.register(this);
37062 * Fires when the element is checked or unchecked.
37063 * @param {Roo.bootstrap.RadioSet} this This radio
37064 * @param {Roo.bootstrap.Radio} item The checked item
37069 * Fires when the element is click.
37070 * @param {Roo.bootstrap.RadioSet} this This radio set
37071 * @param {Roo.bootstrap.Radio} item The checked item
37072 * @param {Roo.EventObject} e The event object
37079 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37087 indicatorpos : 'left',
37089 getAutoCreate : function()
37093 cls : 'roo-radio-set-label',
37097 html : this.fieldLabel
37101 if (Roo.bootstrap.version == 3) {
37104 if(this.indicatorpos == 'left'){
37107 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37108 tooltip : 'This field is required'
37113 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37114 tooltip : 'This field is required'
37120 cls : 'roo-radio-set-items'
37123 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37125 if (align === 'left' && this.fieldLabel.length) {
37128 cls : "roo-radio-set-right",
37134 if(this.labelWidth > 12){
37135 label.style = "width: " + this.labelWidth + 'px';
37138 if(this.labelWidth < 13 && this.labelmd == 0){
37139 this.labelmd = this.labelWidth;
37142 if(this.labellg > 0){
37143 label.cls += ' col-lg-' + this.labellg;
37144 items.cls += ' col-lg-' + (12 - this.labellg);
37147 if(this.labelmd > 0){
37148 label.cls += ' col-md-' + this.labelmd;
37149 items.cls += ' col-md-' + (12 - this.labelmd);
37152 if(this.labelsm > 0){
37153 label.cls += ' col-sm-' + this.labelsm;
37154 items.cls += ' col-sm-' + (12 - this.labelsm);
37157 if(this.labelxs > 0){
37158 label.cls += ' col-xs-' + this.labelxs;
37159 items.cls += ' col-xs-' + (12 - this.labelxs);
37165 cls : 'roo-radio-set',
37169 cls : 'roo-radio-set-input',
37172 value : this.value ? this.value : ''
37179 if(this.weight.length){
37180 cfg.cls += ' roo-radio-' + this.weight;
37184 cfg.cls += ' roo-radio-set-inline';
37188 ['xs','sm','md','lg'].map(function(size){
37189 if (settings[size]) {
37190 cfg.cls += ' col-' + size + '-' + settings[size];
37198 initEvents : function()
37200 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37201 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37203 if(!this.fieldLabel.length){
37204 this.labelEl.hide();
37207 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37208 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37210 this.indicator = this.indicatorEl();
37212 if(this.indicator){
37213 this.indicator.addClass('invisible');
37216 this.originalValue = this.getValue();
37220 inputEl: function ()
37222 return this.el.select('.roo-radio-set-input', true).first();
37225 getChildContainer : function()
37227 return this.itemsEl;
37230 register : function(item)
37232 this.radioes.push(item);
37236 validate : function()
37238 if(this.getVisibilityEl().hasClass('hidden')){
37244 Roo.each(this.radioes, function(i){
37253 if(this.allowBlank) {
37257 if(this.disabled || valid){
37262 this.markInvalid();
37267 markValid : function()
37269 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37270 this.indicatorEl().removeClass('visible');
37271 this.indicatorEl().addClass('invisible');
37275 if (Roo.bootstrap.version == 3) {
37276 this.el.removeClass([this.invalidClass, this.validClass]);
37277 this.el.addClass(this.validClass);
37279 this.el.removeClass(['is-invalid','is-valid']);
37280 this.el.addClass(['is-valid']);
37282 this.fireEvent('valid', this);
37285 markInvalid : function(msg)
37287 if(this.allowBlank || this.disabled){
37291 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37292 this.indicatorEl().removeClass('invisible');
37293 this.indicatorEl().addClass('visible');
37295 if (Roo.bootstrap.version == 3) {
37296 this.el.removeClass([this.invalidClass, this.validClass]);
37297 this.el.addClass(this.invalidClass);
37299 this.el.removeClass(['is-invalid','is-valid']);
37300 this.el.addClass(['is-invalid']);
37303 this.fireEvent('invalid', this, msg);
37307 setValue : function(v, suppressEvent)
37309 if(this.value === v){
37316 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37319 Roo.each(this.radioes, function(i){
37321 i.el.removeClass('checked');
37324 Roo.each(this.radioes, function(i){
37326 if(i.value === v || i.value.toString() === v.toString()){
37328 i.el.addClass('checked');
37330 if(suppressEvent !== true){
37331 this.fireEvent('check', this, i);
37342 clearInvalid : function(){
37344 if(!this.el || this.preventMark){
37348 this.el.removeClass([this.invalidClass]);
37350 this.fireEvent('valid', this);
37355 Roo.apply(Roo.bootstrap.RadioSet, {
37359 register : function(set)
37361 this.groups[set.name] = set;
37364 get: function(name)
37366 if (typeof(this.groups[name]) == 'undefined') {
37370 return this.groups[name] ;
37376 * Ext JS Library 1.1.1
37377 * Copyright(c) 2006-2007, Ext JS, LLC.
37379 * Originally Released Under LGPL - original licence link has changed is not relivant.
37382 * <script type="text/javascript">
37387 * @class Roo.bootstrap.SplitBar
37388 * @extends Roo.util.Observable
37389 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37393 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37394 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37395 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37396 split.minSize = 100;
37397 split.maxSize = 600;
37398 split.animate = true;
37399 split.on('moved', splitterMoved);
37402 * Create a new SplitBar
37403 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37404 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37405 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37406 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37407 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37408 position of the SplitBar).
37410 Roo.bootstrap.SplitBar = function(cfg){
37415 // dragElement : elm
37416 // resizingElement: el,
37418 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37419 // placement : Roo.bootstrap.SplitBar.LEFT ,
37420 // existingProxy ???
37423 this.el = Roo.get(cfg.dragElement, true);
37424 this.el.dom.unselectable = "on";
37426 this.resizingEl = Roo.get(cfg.resizingElement, true);
37430 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37431 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37434 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37437 * The minimum size of the resizing element. (Defaults to 0)
37443 * The maximum size of the resizing element. (Defaults to 2000)
37446 this.maxSize = 2000;
37449 * Whether to animate the transition to the new size
37452 this.animate = false;
37455 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37458 this.useShim = false;
37463 if(!cfg.existingProxy){
37465 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37467 this.proxy = Roo.get(cfg.existingProxy).dom;
37470 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37473 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37476 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37479 this.dragSpecs = {};
37482 * @private The adapter to use to positon and resize elements
37484 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37485 this.adapter.init(this);
37487 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37489 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37490 this.el.addClass("roo-splitbar-h");
37493 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37494 this.el.addClass("roo-splitbar-v");
37500 * Fires when the splitter is moved (alias for {@link #event-moved})
37501 * @param {Roo.bootstrap.SplitBar} this
37502 * @param {Number} newSize the new width or height
37507 * Fires when the splitter is moved
37508 * @param {Roo.bootstrap.SplitBar} this
37509 * @param {Number} newSize the new width or height
37513 * @event beforeresize
37514 * Fires before the splitter is dragged
37515 * @param {Roo.bootstrap.SplitBar} this
37517 "beforeresize" : true,
37519 "beforeapply" : true
37522 Roo.util.Observable.call(this);
37525 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37526 onStartProxyDrag : function(x, y){
37527 this.fireEvent("beforeresize", this);
37529 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37531 o.enableDisplayMode("block");
37532 // all splitbars share the same overlay
37533 Roo.bootstrap.SplitBar.prototype.overlay = o;
37535 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37536 this.overlay.show();
37537 Roo.get(this.proxy).setDisplayed("block");
37538 var size = this.adapter.getElementSize(this);
37539 this.activeMinSize = this.getMinimumSize();;
37540 this.activeMaxSize = this.getMaximumSize();;
37541 var c1 = size - this.activeMinSize;
37542 var c2 = Math.max(this.activeMaxSize - size, 0);
37543 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37544 this.dd.resetConstraints();
37545 this.dd.setXConstraint(
37546 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37547 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37549 this.dd.setYConstraint(0, 0);
37551 this.dd.resetConstraints();
37552 this.dd.setXConstraint(0, 0);
37553 this.dd.setYConstraint(
37554 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37555 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37558 this.dragSpecs.startSize = size;
37559 this.dragSpecs.startPoint = [x, y];
37560 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37564 * @private Called after the drag operation by the DDProxy
37566 onEndProxyDrag : function(e){
37567 Roo.get(this.proxy).setDisplayed(false);
37568 var endPoint = Roo.lib.Event.getXY(e);
37570 this.overlay.hide();
37573 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37574 newSize = this.dragSpecs.startSize +
37575 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37576 endPoint[0] - this.dragSpecs.startPoint[0] :
37577 this.dragSpecs.startPoint[0] - endPoint[0]
37580 newSize = this.dragSpecs.startSize +
37581 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37582 endPoint[1] - this.dragSpecs.startPoint[1] :
37583 this.dragSpecs.startPoint[1] - endPoint[1]
37586 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37587 if(newSize != this.dragSpecs.startSize){
37588 if(this.fireEvent('beforeapply', this, newSize) !== false){
37589 this.adapter.setElementSize(this, newSize);
37590 this.fireEvent("moved", this, newSize);
37591 this.fireEvent("resize", this, newSize);
37597 * Get the adapter this SplitBar uses
37598 * @return The adapter object
37600 getAdapter : function(){
37601 return this.adapter;
37605 * Set the adapter this SplitBar uses
37606 * @param {Object} adapter A SplitBar adapter object
37608 setAdapter : function(adapter){
37609 this.adapter = adapter;
37610 this.adapter.init(this);
37614 * Gets the minimum size for the resizing element
37615 * @return {Number} The minimum size
37617 getMinimumSize : function(){
37618 return this.minSize;
37622 * Sets the minimum size for the resizing element
37623 * @param {Number} minSize The minimum size
37625 setMinimumSize : function(minSize){
37626 this.minSize = minSize;
37630 * Gets the maximum size for the resizing element
37631 * @return {Number} The maximum size
37633 getMaximumSize : function(){
37634 return this.maxSize;
37638 * Sets the maximum size for the resizing element
37639 * @param {Number} maxSize The maximum size
37641 setMaximumSize : function(maxSize){
37642 this.maxSize = maxSize;
37646 * Sets the initialize size for the resizing element
37647 * @param {Number} size The initial size
37649 setCurrentSize : function(size){
37650 var oldAnimate = this.animate;
37651 this.animate = false;
37652 this.adapter.setElementSize(this, size);
37653 this.animate = oldAnimate;
37657 * Destroy this splitbar.
37658 * @param {Boolean} removeEl True to remove the element
37660 destroy : function(removeEl){
37662 this.shim.remove();
37665 this.proxy.parentNode.removeChild(this.proxy);
37673 * @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.
37675 Roo.bootstrap.SplitBar.createProxy = function(dir){
37676 var proxy = new Roo.Element(document.createElement("div"));
37677 proxy.unselectable();
37678 var cls = 'roo-splitbar-proxy';
37679 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37680 document.body.appendChild(proxy.dom);
37685 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37686 * Default Adapter. It assumes the splitter and resizing element are not positioned
37687 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37689 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37692 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37693 // do nothing for now
37694 init : function(s){
37698 * Called before drag operations to get the current size of the resizing element.
37699 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37701 getElementSize : function(s){
37702 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37703 return s.resizingEl.getWidth();
37705 return s.resizingEl.getHeight();
37710 * Called after drag operations to set the size of the resizing element.
37711 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37712 * @param {Number} newSize The new size to set
37713 * @param {Function} onComplete A function to be invoked when resizing is complete
37715 setElementSize : function(s, newSize, onComplete){
37716 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37718 s.resizingEl.setWidth(newSize);
37720 onComplete(s, newSize);
37723 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37728 s.resizingEl.setHeight(newSize);
37730 onComplete(s, newSize);
37733 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37740 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37741 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37742 * Adapter that moves the splitter element to align with the resized sizing element.
37743 * Used with an absolute positioned SplitBar.
37744 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37745 * document.body, make sure you assign an id to the body element.
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37748 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37749 this.container = Roo.get(container);
37752 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37753 init : function(s){
37754 this.basic.init(s);
37757 getElementSize : function(s){
37758 return this.basic.getElementSize(s);
37761 setElementSize : function(s, newSize, onComplete){
37762 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37765 moveSplitter : function(s){
37766 var yes = Roo.bootstrap.SplitBar;
37767 switch(s.placement){
37769 s.el.setX(s.resizingEl.getRight());
37772 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37775 s.el.setY(s.resizingEl.getBottom());
37778 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37785 * Orientation constant - Create a vertical SplitBar
37789 Roo.bootstrap.SplitBar.VERTICAL = 1;
37792 * Orientation constant - Create a horizontal SplitBar
37796 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37799 * Placement constant - The resizing element is to the left of the splitter element
37803 Roo.bootstrap.SplitBar.LEFT = 1;
37806 * Placement constant - The resizing element is to the right of the splitter element
37810 Roo.bootstrap.SplitBar.RIGHT = 2;
37813 * Placement constant - The resizing element is positioned above the splitter element
37817 Roo.bootstrap.SplitBar.TOP = 3;
37820 * Placement constant - The resizing element is positioned under splitter element
37824 Roo.bootstrap.SplitBar.BOTTOM = 4;
37825 Roo.namespace("Roo.bootstrap.layout");/*
37827 * Ext JS Library 1.1.1
37828 * Copyright(c) 2006-2007, Ext JS, LLC.
37830 * Originally Released Under LGPL - original licence link has changed is not relivant.
37833 * <script type="text/javascript">
37837 * @class Roo.bootstrap.layout.Manager
37838 * @extends Roo.bootstrap.Component
37839 * Base class for layout managers.
37841 Roo.bootstrap.layout.Manager = function(config)
37843 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37849 /** false to disable window resize monitoring @type Boolean */
37850 this.monitorWindowResize = true;
37855 * Fires when a layout is performed.
37856 * @param {Roo.LayoutManager} this
37860 * @event regionresized
37861 * Fires when the user resizes a region.
37862 * @param {Roo.LayoutRegion} region The resized region
37863 * @param {Number} newSize The new size (width for east/west, height for north/south)
37865 "regionresized" : true,
37867 * @event regioncollapsed
37868 * Fires when a region is collapsed.
37869 * @param {Roo.LayoutRegion} region The collapsed region
37871 "regioncollapsed" : true,
37873 * @event regionexpanded
37874 * Fires when a region is expanded.
37875 * @param {Roo.LayoutRegion} region The expanded region
37877 "regionexpanded" : true
37879 this.updating = false;
37882 this.el = Roo.get(config.el);
37888 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37893 monitorWindowResize : true,
37899 onRender : function(ct, position)
37902 this.el = Roo.get(ct);
37905 //this.fireEvent('render',this);
37909 initEvents: function()
37913 // ie scrollbar fix
37914 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37915 document.body.scroll = "no";
37916 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37917 this.el.position('relative');
37919 this.id = this.el.id;
37920 this.el.addClass("roo-layout-container");
37921 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37922 if(this.el.dom != document.body ) {
37923 this.el.on('resize', this.layout,this);
37924 this.el.on('show', this.layout,this);
37930 * Returns true if this layout is currently being updated
37931 * @return {Boolean}
37933 isUpdating : function(){
37934 return this.updating;
37938 * Suspend the LayoutManager from doing auto-layouts while
37939 * making multiple add or remove calls
37941 beginUpdate : function(){
37942 this.updating = true;
37946 * Restore auto-layouts and optionally disable the manager from performing a layout
37947 * @param {Boolean} noLayout true to disable a layout update
37949 endUpdate : function(noLayout){
37950 this.updating = false;
37956 layout: function(){
37960 onRegionResized : function(region, newSize){
37961 this.fireEvent("regionresized", region, newSize);
37965 onRegionCollapsed : function(region){
37966 this.fireEvent("regioncollapsed", region);
37969 onRegionExpanded : function(region){
37970 this.fireEvent("regionexpanded", region);
37974 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37975 * performs box-model adjustments.
37976 * @return {Object} The size as an object {width: (the width), height: (the height)}
37978 getViewSize : function()
37981 if(this.el.dom != document.body){
37982 size = this.el.getSize();
37984 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37986 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37987 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37992 * Returns the Element this layout is bound to.
37993 * @return {Roo.Element}
37995 getEl : function(){
38000 * Returns the specified region.
38001 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38002 * @return {Roo.LayoutRegion}
38004 getRegion : function(target){
38005 return this.regions[target.toLowerCase()];
38008 onWindowResize : function(){
38009 if(this.monitorWindowResize){
38016 * Ext JS Library 1.1.1
38017 * Copyright(c) 2006-2007, Ext JS, LLC.
38019 * Originally Released Under LGPL - original licence link has changed is not relivant.
38022 * <script type="text/javascript">
38025 * @class Roo.bootstrap.layout.Border
38026 * @extends Roo.bootstrap.layout.Manager
38028 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38029 * please see: examples/bootstrap/nested.html<br><br>
38031 <b>The container the layout is rendered into can be either the body element or any other element.
38032 If it is not the body element, the container needs to either be an absolute positioned element,
38033 or you will need to add "position:relative" to the css of the container. You will also need to specify
38034 the container size if it is not the body element.</b>
38037 * Create a new Border
38038 * @param {Object} config Configuration options
38040 Roo.bootstrap.layout.Border = function(config){
38041 config = config || {};
38042 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38046 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38047 if(config[region]){
38048 config[region].region = region;
38049 this.addRegion(config[region]);
38055 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38057 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38059 parent : false, // this might point to a 'nest' or a ???
38062 * Creates and adds a new region if it doesn't already exist.
38063 * @param {String} target The target region key (north, south, east, west or center).
38064 * @param {Object} config The regions config object
38065 * @return {BorderLayoutRegion} The new region
38067 addRegion : function(config)
38069 if(!this.regions[config.region]){
38070 var r = this.factory(config);
38071 this.bindRegion(r);
38073 return this.regions[config.region];
38077 bindRegion : function(r){
38078 this.regions[r.config.region] = r;
38080 r.on("visibilitychange", this.layout, this);
38081 r.on("paneladded", this.layout, this);
38082 r.on("panelremoved", this.layout, this);
38083 r.on("invalidated", this.layout, this);
38084 r.on("resized", this.onRegionResized, this);
38085 r.on("collapsed", this.onRegionCollapsed, this);
38086 r.on("expanded", this.onRegionExpanded, this);
38090 * Performs a layout update.
38092 layout : function()
38094 if(this.updating) {
38098 // render all the rebions if they have not been done alreayd?
38099 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38100 if(this.regions[region] && !this.regions[region].bodyEl){
38101 this.regions[region].onRender(this.el)
38105 var size = this.getViewSize();
38106 var w = size.width;
38107 var h = size.height;
38112 //var x = 0, y = 0;
38114 var rs = this.regions;
38115 var north = rs["north"];
38116 var south = rs["south"];
38117 var west = rs["west"];
38118 var east = rs["east"];
38119 var center = rs["center"];
38120 //if(this.hideOnLayout){ // not supported anymore
38121 //c.el.setStyle("display", "none");
38123 if(north && north.isVisible()){
38124 var b = north.getBox();
38125 var m = north.getMargins();
38126 b.width = w - (m.left+m.right);
38129 centerY = b.height + b.y + m.bottom;
38130 centerH -= centerY;
38131 north.updateBox(this.safeBox(b));
38133 if(south && south.isVisible()){
38134 var b = south.getBox();
38135 var m = south.getMargins();
38136 b.width = w - (m.left+m.right);
38138 var totalHeight = (b.height + m.top + m.bottom);
38139 b.y = h - totalHeight + m.top;
38140 centerH -= totalHeight;
38141 south.updateBox(this.safeBox(b));
38143 if(west && west.isVisible()){
38144 var b = west.getBox();
38145 var m = west.getMargins();
38146 b.height = centerH - (m.top+m.bottom);
38148 b.y = centerY + m.top;
38149 var totalWidth = (b.width + m.left + m.right);
38150 centerX += totalWidth;
38151 centerW -= totalWidth;
38152 west.updateBox(this.safeBox(b));
38154 if(east && east.isVisible()){
38155 var b = east.getBox();
38156 var m = east.getMargins();
38157 b.height = centerH - (m.top+m.bottom);
38158 var totalWidth = (b.width + m.left + m.right);
38159 b.x = w - totalWidth + m.left;
38160 b.y = centerY + m.top;
38161 centerW -= totalWidth;
38162 east.updateBox(this.safeBox(b));
38165 var m = center.getMargins();
38167 x: centerX + m.left,
38168 y: centerY + m.top,
38169 width: centerW - (m.left+m.right),
38170 height: centerH - (m.top+m.bottom)
38172 //if(this.hideOnLayout){
38173 //center.el.setStyle("display", "block");
38175 center.updateBox(this.safeBox(centerBox));
38178 this.fireEvent("layout", this);
38182 safeBox : function(box){
38183 box.width = Math.max(0, box.width);
38184 box.height = Math.max(0, box.height);
38189 * Adds a ContentPanel (or subclass) to this layout.
38190 * @param {String} target The target region key (north, south, east, west or center).
38191 * @param {Roo.ContentPanel} panel The panel to add
38192 * @return {Roo.ContentPanel} The added panel
38194 add : function(target, panel){
38196 target = target.toLowerCase();
38197 return this.regions[target].add(panel);
38201 * Remove a ContentPanel (or subclass) to this layout.
38202 * @param {String} target The target region key (north, south, east, west or center).
38203 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38204 * @return {Roo.ContentPanel} The removed panel
38206 remove : function(target, panel){
38207 target = target.toLowerCase();
38208 return this.regions[target].remove(panel);
38212 * Searches all regions for a panel with the specified id
38213 * @param {String} panelId
38214 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38216 findPanel : function(panelId){
38217 var rs = this.regions;
38218 for(var target in rs){
38219 if(typeof rs[target] != "function"){
38220 var p = rs[target].getPanel(panelId);
38230 * Searches all regions for a panel with the specified id and activates (shows) it.
38231 * @param {String/ContentPanel} panelId The panels id or the panel itself
38232 * @return {Roo.ContentPanel} The shown panel or null
38234 showPanel : function(panelId) {
38235 var rs = this.regions;
38236 for(var target in rs){
38237 var r = rs[target];
38238 if(typeof r != "function"){
38239 if(r.hasPanel(panelId)){
38240 return r.showPanel(panelId);
38248 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38249 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38252 restoreState : function(provider){
38254 provider = Roo.state.Manager;
38256 var sm = new Roo.LayoutStateManager();
38257 sm.init(this, provider);
38263 * Adds a xtype elements to the layout.
38267 xtype : 'ContentPanel',
38274 xtype : 'NestedLayoutPanel',
38280 items : [ ... list of content panels or nested layout panels.. ]
38284 * @param {Object} cfg Xtype definition of item to add.
38286 addxtype : function(cfg)
38288 // basically accepts a pannel...
38289 // can accept a layout region..!?!?
38290 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38293 // theory? children can only be panels??
38295 //if (!cfg.xtype.match(/Panel$/)) {
38300 if (typeof(cfg.region) == 'undefined') {
38301 Roo.log("Failed to add Panel, region was not set");
38305 var region = cfg.region;
38311 xitems = cfg.items;
38316 if ( region == 'center') {
38317 Roo.log("Center: " + cfg.title);
38323 case 'Content': // ContentPanel (el, cfg)
38324 case 'Scroll': // ContentPanel (el, cfg)
38326 cfg.autoCreate = cfg.autoCreate || true;
38327 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38329 // var el = this.el.createChild();
38330 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38333 this.add(region, ret);
38337 case 'TreePanel': // our new panel!
38338 cfg.el = this.el.createChild();
38339 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38340 this.add(region, ret);
38345 // create a new Layout (which is a Border Layout...
38347 var clayout = cfg.layout;
38348 clayout.el = this.el.createChild();
38349 clayout.items = clayout.items || [];
38353 // replace this exitems with the clayout ones..
38354 xitems = clayout.items;
38356 // force background off if it's in center...
38357 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38358 cfg.background = false;
38360 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38363 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38364 //console.log('adding nested layout panel ' + cfg.toSource());
38365 this.add(region, ret);
38366 nb = {}; /// find first...
38371 // needs grid and region
38373 //var el = this.getRegion(region).el.createChild();
38375 *var el = this.el.createChild();
38376 // create the grid first...
38377 cfg.grid.container = el;
38378 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38381 if (region == 'center' && this.active ) {
38382 cfg.background = false;
38385 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38387 this.add(region, ret);
38389 if (cfg.background) {
38390 // render grid on panel activation (if panel background)
38391 ret.on('activate', function(gp) {
38392 if (!gp.grid.rendered) {
38393 // gp.grid.render(el);
38397 // cfg.grid.render(el);
38403 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38404 // it was the old xcomponent building that caused this before.
38405 // espeically if border is the top element in the tree.
38415 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38417 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38418 this.add(region, ret);
38422 throw "Can not add '" + cfg.xtype + "' to Border";
38428 this.beginUpdate();
38432 Roo.each(xitems, function(i) {
38433 region = nb && i.region ? i.region : false;
38435 var add = ret.addxtype(i);
38438 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38439 if (!i.background) {
38440 abn[region] = nb[region] ;
38447 // make the last non-background panel active..
38448 //if (nb) { Roo.log(abn); }
38451 for(var r in abn) {
38452 region = this.getRegion(r);
38454 // tried using nb[r], but it does not work..
38456 region.showPanel(abn[r]);
38467 factory : function(cfg)
38470 var validRegions = Roo.bootstrap.layout.Border.regions;
38472 var target = cfg.region;
38475 var r = Roo.bootstrap.layout;
38479 return new r.North(cfg);
38481 return new r.South(cfg);
38483 return new r.East(cfg);
38485 return new r.West(cfg);
38487 return new r.Center(cfg);
38489 throw 'Layout region "'+target+'" not supported.';
38496 * Ext JS Library 1.1.1
38497 * Copyright(c) 2006-2007, Ext JS, LLC.
38499 * Originally Released Under LGPL - original licence link has changed is not relivant.
38502 * <script type="text/javascript">
38506 * @class Roo.bootstrap.layout.Basic
38507 * @extends Roo.util.Observable
38508 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38509 * and does not have a titlebar, tabs or any other features. All it does is size and position
38510 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38511 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38512 * @cfg {string} region the region that it inhabits..
38513 * @cfg {bool} skipConfig skip config?
38517 Roo.bootstrap.layout.Basic = function(config){
38519 this.mgr = config.mgr;
38521 this.position = config.region;
38523 var skipConfig = config.skipConfig;
38527 * @scope Roo.BasicLayoutRegion
38531 * @event beforeremove
38532 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38533 * @param {Roo.LayoutRegion} this
38534 * @param {Roo.ContentPanel} panel The panel
38535 * @param {Object} e The cancel event object
38537 "beforeremove" : true,
38539 * @event invalidated
38540 * Fires when the layout for this region is changed.
38541 * @param {Roo.LayoutRegion} this
38543 "invalidated" : true,
38545 * @event visibilitychange
38546 * Fires when this region is shown or hidden
38547 * @param {Roo.LayoutRegion} this
38548 * @param {Boolean} visibility true or false
38550 "visibilitychange" : true,
38552 * @event paneladded
38553 * Fires when a panel is added.
38554 * @param {Roo.LayoutRegion} this
38555 * @param {Roo.ContentPanel} panel The panel
38557 "paneladded" : true,
38559 * @event panelremoved
38560 * Fires when a panel is removed.
38561 * @param {Roo.LayoutRegion} this
38562 * @param {Roo.ContentPanel} panel The panel
38564 "panelremoved" : true,
38566 * @event beforecollapse
38567 * Fires when this region before collapse.
38568 * @param {Roo.LayoutRegion} this
38570 "beforecollapse" : true,
38573 * Fires when this region is collapsed.
38574 * @param {Roo.LayoutRegion} this
38576 "collapsed" : true,
38579 * Fires when this region is expanded.
38580 * @param {Roo.LayoutRegion} this
38585 * Fires when this region is slid into view.
38586 * @param {Roo.LayoutRegion} this
38588 "slideshow" : true,
38591 * Fires when this region slides out of view.
38592 * @param {Roo.LayoutRegion} this
38594 "slidehide" : true,
38596 * @event panelactivated
38597 * Fires when a panel is activated.
38598 * @param {Roo.LayoutRegion} this
38599 * @param {Roo.ContentPanel} panel The activated panel
38601 "panelactivated" : true,
38604 * Fires when the user resizes this region.
38605 * @param {Roo.LayoutRegion} this
38606 * @param {Number} newSize The new size (width for east/west, height for north/south)
38610 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38611 this.panels = new Roo.util.MixedCollection();
38612 this.panels.getKey = this.getPanelId.createDelegate(this);
38614 this.activePanel = null;
38615 // ensure listeners are added...
38617 if (config.listeners || config.events) {
38618 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38619 listeners : config.listeners || {},
38620 events : config.events || {}
38624 if(skipConfig !== true){
38625 this.applyConfig(config);
38629 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38631 getPanelId : function(p){
38635 applyConfig : function(config){
38636 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38637 this.config = config;
38642 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38643 * the width, for horizontal (north, south) the height.
38644 * @param {Number} newSize The new width or height
38646 resizeTo : function(newSize){
38647 var el = this.el ? this.el :
38648 (this.activePanel ? this.activePanel.getEl() : null);
38650 switch(this.position){
38653 el.setWidth(newSize);
38654 this.fireEvent("resized", this, newSize);
38658 el.setHeight(newSize);
38659 this.fireEvent("resized", this, newSize);
38665 getBox : function(){
38666 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38669 getMargins : function(){
38670 return this.margins;
38673 updateBox : function(box){
38675 var el = this.activePanel.getEl();
38676 el.dom.style.left = box.x + "px";
38677 el.dom.style.top = box.y + "px";
38678 this.activePanel.setSize(box.width, box.height);
38682 * Returns the container element for this region.
38683 * @return {Roo.Element}
38685 getEl : function(){
38686 return this.activePanel;
38690 * Returns true if this region is currently visible.
38691 * @return {Boolean}
38693 isVisible : function(){
38694 return this.activePanel ? true : false;
38697 setActivePanel : function(panel){
38698 panel = this.getPanel(panel);
38699 if(this.activePanel && this.activePanel != panel){
38700 this.activePanel.setActiveState(false);
38701 this.activePanel.getEl().setLeftTop(-10000,-10000);
38703 this.activePanel = panel;
38704 panel.setActiveState(true);
38706 panel.setSize(this.box.width, this.box.height);
38708 this.fireEvent("panelactivated", this, panel);
38709 this.fireEvent("invalidated");
38713 * Show the specified panel.
38714 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38715 * @return {Roo.ContentPanel} The shown panel or null
38717 showPanel : function(panel){
38718 panel = this.getPanel(panel);
38720 this.setActivePanel(panel);
38726 * Get the active panel for this region.
38727 * @return {Roo.ContentPanel} The active panel or null
38729 getActivePanel : function(){
38730 return this.activePanel;
38734 * Add the passed ContentPanel(s)
38735 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38736 * @return {Roo.ContentPanel} The panel added (if only one was added)
38738 add : function(panel){
38739 if(arguments.length > 1){
38740 for(var i = 0, len = arguments.length; i < len; i++) {
38741 this.add(arguments[i]);
38745 if(this.hasPanel(panel)){
38746 this.showPanel(panel);
38749 var el = panel.getEl();
38750 if(el.dom.parentNode != this.mgr.el.dom){
38751 this.mgr.el.dom.appendChild(el.dom);
38753 if(panel.setRegion){
38754 panel.setRegion(this);
38756 this.panels.add(panel);
38757 el.setStyle("position", "absolute");
38758 if(!panel.background){
38759 this.setActivePanel(panel);
38760 if(this.config.initialSize && this.panels.getCount()==1){
38761 this.resizeTo(this.config.initialSize);
38764 this.fireEvent("paneladded", this, panel);
38769 * Returns true if the panel is in this region.
38770 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38771 * @return {Boolean}
38773 hasPanel : function(panel){
38774 if(typeof panel == "object"){ // must be panel obj
38775 panel = panel.getId();
38777 return this.getPanel(panel) ? true : false;
38781 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38782 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38783 * @param {Boolean} preservePanel Overrides the config preservePanel option
38784 * @return {Roo.ContentPanel} The panel that was removed
38786 remove : function(panel, preservePanel){
38787 panel = this.getPanel(panel);
38792 this.fireEvent("beforeremove", this, panel, e);
38793 if(e.cancel === true){
38796 var panelId = panel.getId();
38797 this.panels.removeKey(panelId);
38802 * Returns the panel specified or null if it's not in this region.
38803 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804 * @return {Roo.ContentPanel}
38806 getPanel : function(id){
38807 if(typeof id == "object"){ // must be panel obj
38810 return this.panels.get(id);
38814 * Returns this regions position (north/south/east/west/center).
38817 getPosition: function(){
38818 return this.position;
38822 * Ext JS Library 1.1.1
38823 * Copyright(c) 2006-2007, Ext JS, LLC.
38825 * Originally Released Under LGPL - original licence link has changed is not relivant.
38828 * <script type="text/javascript">
38832 * @class Roo.bootstrap.layout.Region
38833 * @extends Roo.bootstrap.layout.Basic
38834 * This class represents a region in a layout manager.
38836 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38837 * @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})
38838 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38839 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38840 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38841 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38842 * @cfg {String} title The title for the region (overrides panel titles)
38843 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38844 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38845 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38846 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38847 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38848 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38849 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38850 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38851 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38852 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38854 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38855 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38856 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38857 * @cfg {Number} width For East/West panels
38858 * @cfg {Number} height For North/South panels
38859 * @cfg {Boolean} split To show the splitter
38860 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38862 * @cfg {string} cls Extra CSS classes to add to region
38864 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38865 * @cfg {string} region the region that it inhabits..
38868 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38869 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38871 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38872 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38873 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38875 Roo.bootstrap.layout.Region = function(config)
38877 this.applyConfig(config);
38879 var mgr = config.mgr;
38880 var pos = config.region;
38881 config.skipConfig = true;
38882 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38885 this.onRender(mgr.el);
38888 this.visible = true;
38889 this.collapsed = false;
38890 this.unrendered_panels = [];
38893 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38895 position: '', // set by wrapper (eg. north/south etc..)
38896 unrendered_panels : null, // unrendered panels.
38898 tabPosition : false,
38900 mgr: false, // points to 'Border'
38903 createBody : function(){
38904 /** This region's body element
38905 * @type Roo.Element */
38906 this.bodyEl = this.el.createChild({
38908 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38912 onRender: function(ctr, pos)
38914 var dh = Roo.DomHelper;
38915 /** This region's container element
38916 * @type Roo.Element */
38917 this.el = dh.append(ctr.dom, {
38919 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38921 /** This region's title element
38922 * @type Roo.Element */
38924 this.titleEl = dh.append(this.el.dom, {
38926 unselectable: "on",
38927 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38929 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38930 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38934 this.titleEl.enableDisplayMode();
38935 /** This region's title text element
38936 * @type HTMLElement */
38937 this.titleTextEl = this.titleEl.dom.firstChild;
38938 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38940 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38941 this.closeBtn.enableDisplayMode();
38942 this.closeBtn.on("click", this.closeClicked, this);
38943 this.closeBtn.hide();
38945 this.createBody(this.config);
38946 if(this.config.hideWhenEmpty){
38948 this.on("paneladded", this.validateVisibility, this);
38949 this.on("panelremoved", this.validateVisibility, this);
38951 if(this.autoScroll){
38952 this.bodyEl.setStyle("overflow", "auto");
38954 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38956 //if(c.titlebar !== false){
38957 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38958 this.titleEl.hide();
38960 this.titleEl.show();
38961 if(this.config.title){
38962 this.titleTextEl.innerHTML = this.config.title;
38966 if(this.config.collapsed){
38967 this.collapse(true);
38969 if(this.config.hidden){
38973 if (this.unrendered_panels && this.unrendered_panels.length) {
38974 for (var i =0;i< this.unrendered_panels.length; i++) {
38975 this.add(this.unrendered_panels[i]);
38977 this.unrendered_panels = null;
38983 applyConfig : function(c)
38986 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38987 var dh = Roo.DomHelper;
38988 if(c.titlebar !== false){
38989 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38990 this.collapseBtn.on("click", this.collapse, this);
38991 this.collapseBtn.enableDisplayMode();
38993 if(c.showPin === true || this.showPin){
38994 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38995 this.stickBtn.enableDisplayMode();
38996 this.stickBtn.on("click", this.expand, this);
38997 this.stickBtn.hide();
39002 /** This region's collapsed element
39003 * @type Roo.Element */
39006 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39007 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39010 if(c.floatable !== false){
39011 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39012 this.collapsedEl.on("click", this.collapseClick, this);
39015 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39016 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39017 id: "message", unselectable: "on", style:{"float":"left"}});
39018 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39020 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39021 this.expandBtn.on("click", this.expand, this);
39025 if(this.collapseBtn){
39026 this.collapseBtn.setVisible(c.collapsible == true);
39029 this.cmargins = c.cmargins || this.cmargins ||
39030 (this.position == "west" || this.position == "east" ?
39031 {top: 0, left: 2, right:2, bottom: 0} :
39032 {top: 2, left: 0, right:0, bottom: 2});
39034 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39037 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39039 this.autoScroll = c.autoScroll || false;
39044 this.duration = c.duration || .30;
39045 this.slideDuration = c.slideDuration || .45;
39050 * Returns true if this region is currently visible.
39051 * @return {Boolean}
39053 isVisible : function(){
39054 return this.visible;
39058 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39059 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39061 //setCollapsedTitle : function(title){
39062 // title = title || " ";
39063 // if(this.collapsedTitleTextEl){
39064 // this.collapsedTitleTextEl.innerHTML = title;
39068 getBox : function(){
39070 // if(!this.collapsed){
39071 b = this.el.getBox(false, true);
39073 // b = this.collapsedEl.getBox(false, true);
39078 getMargins : function(){
39079 return this.margins;
39080 //return this.collapsed ? this.cmargins : this.margins;
39083 highlight : function(){
39084 this.el.addClass("x-layout-panel-dragover");
39087 unhighlight : function(){
39088 this.el.removeClass("x-layout-panel-dragover");
39091 updateBox : function(box)
39093 if (!this.bodyEl) {
39094 return; // not rendered yet..
39098 if(!this.collapsed){
39099 this.el.dom.style.left = box.x + "px";
39100 this.el.dom.style.top = box.y + "px";
39101 this.updateBody(box.width, box.height);
39103 this.collapsedEl.dom.style.left = box.x + "px";
39104 this.collapsedEl.dom.style.top = box.y + "px";
39105 this.collapsedEl.setSize(box.width, box.height);
39108 this.tabs.autoSizeTabs();
39112 updateBody : function(w, h)
39115 this.el.setWidth(w);
39116 w -= this.el.getBorderWidth("rl");
39117 if(this.config.adjustments){
39118 w += this.config.adjustments[0];
39121 if(h !== null && h > 0){
39122 this.el.setHeight(h);
39123 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39124 h -= this.el.getBorderWidth("tb");
39125 if(this.config.adjustments){
39126 h += this.config.adjustments[1];
39128 this.bodyEl.setHeight(h);
39130 h = this.tabs.syncHeight(h);
39133 if(this.panelSize){
39134 w = w !== null ? w : this.panelSize.width;
39135 h = h !== null ? h : this.panelSize.height;
39137 if(this.activePanel){
39138 var el = this.activePanel.getEl();
39139 w = w !== null ? w : el.getWidth();
39140 h = h !== null ? h : el.getHeight();
39141 this.panelSize = {width: w, height: h};
39142 this.activePanel.setSize(w, h);
39144 if(Roo.isIE && this.tabs){
39145 this.tabs.el.repaint();
39150 * Returns the container element for this region.
39151 * @return {Roo.Element}
39153 getEl : function(){
39158 * Hides this region.
39161 //if(!this.collapsed){
39162 this.el.dom.style.left = "-2000px";
39165 // this.collapsedEl.dom.style.left = "-2000px";
39166 // this.collapsedEl.hide();
39168 this.visible = false;
39169 this.fireEvent("visibilitychange", this, false);
39173 * Shows this region if it was previously hidden.
39176 //if(!this.collapsed){
39179 // this.collapsedEl.show();
39181 this.visible = true;
39182 this.fireEvent("visibilitychange", this, true);
39185 closeClicked : function(){
39186 if(this.activePanel){
39187 this.remove(this.activePanel);
39191 collapseClick : function(e){
39193 e.stopPropagation();
39196 e.stopPropagation();
39202 * Collapses this region.
39203 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39206 collapse : function(skipAnim, skipCheck = false){
39207 if(this.collapsed) {
39211 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39213 this.collapsed = true;
39215 this.split.el.hide();
39217 if(this.config.animate && skipAnim !== true){
39218 this.fireEvent("invalidated", this);
39219 this.animateCollapse();
39221 this.el.setLocation(-20000,-20000);
39223 this.collapsedEl.show();
39224 this.fireEvent("collapsed", this);
39225 this.fireEvent("invalidated", this);
39231 animateCollapse : function(){
39236 * Expands this region if it was previously collapsed.
39237 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39238 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39241 expand : function(e, skipAnim){
39243 e.stopPropagation();
39245 if(!this.collapsed || this.el.hasActiveFx()) {
39249 this.afterSlideIn();
39252 this.collapsed = false;
39253 if(this.config.animate && skipAnim !== true){
39254 this.animateExpand();
39258 this.split.el.show();
39260 this.collapsedEl.setLocation(-2000,-2000);
39261 this.collapsedEl.hide();
39262 this.fireEvent("invalidated", this);
39263 this.fireEvent("expanded", this);
39267 animateExpand : function(){
39271 initTabs : function()
39273 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39275 var ts = new Roo.bootstrap.panel.Tabs({
39276 el: this.bodyEl.dom,
39278 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39279 disableTooltips: this.config.disableTabTips,
39280 toolbar : this.config.toolbar
39283 if(this.config.hideTabs){
39284 ts.stripWrap.setDisplayed(false);
39287 ts.resizeTabs = this.config.resizeTabs === true;
39288 ts.minTabWidth = this.config.minTabWidth || 40;
39289 ts.maxTabWidth = this.config.maxTabWidth || 250;
39290 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39291 ts.monitorResize = false;
39292 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39293 ts.bodyEl.addClass('roo-layout-tabs-body');
39294 this.panels.each(this.initPanelAsTab, this);
39297 initPanelAsTab : function(panel){
39298 var ti = this.tabs.addTab(
39302 this.config.closeOnTab && panel.isClosable(),
39305 if(panel.tabTip !== undefined){
39306 ti.setTooltip(panel.tabTip);
39308 ti.on("activate", function(){
39309 this.setActivePanel(panel);
39312 if(this.config.closeOnTab){
39313 ti.on("beforeclose", function(t, e){
39315 this.remove(panel);
39319 panel.tabItem = ti;
39324 updatePanelTitle : function(panel, title)
39326 if(this.activePanel == panel){
39327 this.updateTitle(title);
39330 var ti = this.tabs.getTab(panel.getEl().id);
39332 if(panel.tabTip !== undefined){
39333 ti.setTooltip(panel.tabTip);
39338 updateTitle : function(title){
39339 if(this.titleTextEl && !this.config.title){
39340 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39344 setActivePanel : function(panel)
39346 panel = this.getPanel(panel);
39347 if(this.activePanel && this.activePanel != panel){
39348 if(this.activePanel.setActiveState(false) === false){
39352 this.activePanel = panel;
39353 panel.setActiveState(true);
39354 if(this.panelSize){
39355 panel.setSize(this.panelSize.width, this.panelSize.height);
39358 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39360 this.updateTitle(panel.getTitle());
39362 this.fireEvent("invalidated", this);
39364 this.fireEvent("panelactivated", this, panel);
39368 * Shows the specified panel.
39369 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39370 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39372 showPanel : function(panel)
39374 panel = this.getPanel(panel);
39377 var tab = this.tabs.getTab(panel.getEl().id);
39378 if(tab.isHidden()){
39379 this.tabs.unhideTab(tab.id);
39383 this.setActivePanel(panel);
39390 * Get the active panel for this region.
39391 * @return {Roo.ContentPanel} The active panel or null
39393 getActivePanel : function(){
39394 return this.activePanel;
39397 validateVisibility : function(){
39398 if(this.panels.getCount() < 1){
39399 this.updateTitle(" ");
39400 this.closeBtn.hide();
39403 if(!this.isVisible()){
39410 * Adds the passed ContentPanel(s) to this region.
39411 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39412 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39414 add : function(panel)
39416 if(arguments.length > 1){
39417 for(var i = 0, len = arguments.length; i < len; i++) {
39418 this.add(arguments[i]);
39423 // if we have not been rendered yet, then we can not really do much of this..
39424 if (!this.bodyEl) {
39425 this.unrendered_panels.push(panel);
39432 if(this.hasPanel(panel)){
39433 this.showPanel(panel);
39436 panel.setRegion(this);
39437 this.panels.add(panel);
39438 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39439 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39440 // and hide them... ???
39441 this.bodyEl.dom.appendChild(panel.getEl().dom);
39442 if(panel.background !== true){
39443 this.setActivePanel(panel);
39445 this.fireEvent("paneladded", this, panel);
39452 this.initPanelAsTab(panel);
39456 if(panel.background !== true){
39457 this.tabs.activate(panel.getEl().id);
39459 this.fireEvent("paneladded", this, panel);
39464 * Hides the tab for the specified panel.
39465 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39467 hidePanel : function(panel){
39468 if(this.tabs && (panel = this.getPanel(panel))){
39469 this.tabs.hideTab(panel.getEl().id);
39474 * Unhides the tab for a previously hidden panel.
39475 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39477 unhidePanel : function(panel){
39478 if(this.tabs && (panel = this.getPanel(panel))){
39479 this.tabs.unhideTab(panel.getEl().id);
39483 clearPanels : function(){
39484 while(this.panels.getCount() > 0){
39485 this.remove(this.panels.first());
39490 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39491 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492 * @param {Boolean} preservePanel Overrides the config preservePanel option
39493 * @return {Roo.ContentPanel} The panel that was removed
39495 remove : function(panel, preservePanel)
39497 panel = this.getPanel(panel);
39502 this.fireEvent("beforeremove", this, panel, e);
39503 if(e.cancel === true){
39506 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39507 var panelId = panel.getId();
39508 this.panels.removeKey(panelId);
39510 document.body.appendChild(panel.getEl().dom);
39513 this.tabs.removeTab(panel.getEl().id);
39514 }else if (!preservePanel){
39515 this.bodyEl.dom.removeChild(panel.getEl().dom);
39517 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39518 var p = this.panels.first();
39519 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39520 tempEl.appendChild(p.getEl().dom);
39521 this.bodyEl.update("");
39522 this.bodyEl.dom.appendChild(p.getEl().dom);
39524 this.updateTitle(p.getTitle());
39526 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39527 this.setActivePanel(p);
39529 panel.setRegion(null);
39530 if(this.activePanel == panel){
39531 this.activePanel = null;
39533 if(this.config.autoDestroy !== false && preservePanel !== true){
39534 try{panel.destroy();}catch(e){}
39536 this.fireEvent("panelremoved", this, panel);
39541 * Returns the TabPanel component used by this region
39542 * @return {Roo.TabPanel}
39544 getTabs : function(){
39548 createTool : function(parentEl, className){
39549 var btn = Roo.DomHelper.append(parentEl, {
39551 cls: "x-layout-tools-button",
39554 cls: "roo-layout-tools-button-inner " + className,
39558 btn.addClassOnOver("roo-layout-tools-button-over");
39563 * Ext JS Library 1.1.1
39564 * Copyright(c) 2006-2007, Ext JS, LLC.
39566 * Originally Released Under LGPL - original licence link has changed is not relivant.
39569 * <script type="text/javascript">
39575 * @class Roo.SplitLayoutRegion
39576 * @extends Roo.LayoutRegion
39577 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39579 Roo.bootstrap.layout.Split = function(config){
39580 this.cursor = config.cursor;
39581 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39584 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39586 splitTip : "Drag to resize.",
39587 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39588 useSplitTips : false,
39590 applyConfig : function(config){
39591 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39594 onRender : function(ctr,pos) {
39596 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39597 if(!this.config.split){
39602 var splitEl = Roo.DomHelper.append(ctr.dom, {
39604 id: this.el.id + "-split",
39605 cls: "roo-layout-split roo-layout-split-"+this.position,
39608 /** The SplitBar for this region
39609 * @type Roo.SplitBar */
39610 // does not exist yet...
39611 Roo.log([this.position, this.orientation]);
39613 this.split = new Roo.bootstrap.SplitBar({
39614 dragElement : splitEl,
39615 resizingElement: this.el,
39616 orientation : this.orientation
39619 this.split.on("moved", this.onSplitMove, this);
39620 this.split.useShim = this.config.useShim === true;
39621 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39622 if(this.useSplitTips){
39623 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39625 //if(config.collapsible){
39626 // this.split.el.on("dblclick", this.collapse, this);
39629 if(typeof this.config.minSize != "undefined"){
39630 this.split.minSize = this.config.minSize;
39632 if(typeof this.config.maxSize != "undefined"){
39633 this.split.maxSize = this.config.maxSize;
39635 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39636 this.hideSplitter();
39641 getHMaxSize : function(){
39642 var cmax = this.config.maxSize || 10000;
39643 var center = this.mgr.getRegion("center");
39644 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39647 getVMaxSize : function(){
39648 var cmax = this.config.maxSize || 10000;
39649 var center = this.mgr.getRegion("center");
39650 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39653 onSplitMove : function(split, newSize){
39654 this.fireEvent("resized", this, newSize);
39658 * Returns the {@link Roo.SplitBar} for this region.
39659 * @return {Roo.SplitBar}
39661 getSplitBar : function(){
39666 this.hideSplitter();
39667 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39670 hideSplitter : function(){
39672 this.split.el.setLocation(-2000,-2000);
39673 this.split.el.hide();
39679 this.split.el.show();
39681 Roo.bootstrap.layout.Split.superclass.show.call(this);
39684 beforeSlide: function(){
39685 if(Roo.isGecko){// firefox overflow auto bug workaround
39686 this.bodyEl.clip();
39688 this.tabs.bodyEl.clip();
39690 if(this.activePanel){
39691 this.activePanel.getEl().clip();
39693 if(this.activePanel.beforeSlide){
39694 this.activePanel.beforeSlide();
39700 afterSlide : function(){
39701 if(Roo.isGecko){// firefox overflow auto bug workaround
39702 this.bodyEl.unclip();
39704 this.tabs.bodyEl.unclip();
39706 if(this.activePanel){
39707 this.activePanel.getEl().unclip();
39708 if(this.activePanel.afterSlide){
39709 this.activePanel.afterSlide();
39715 initAutoHide : function(){
39716 if(this.autoHide !== false){
39717 if(!this.autoHideHd){
39718 var st = new Roo.util.DelayedTask(this.slideIn, this);
39719 this.autoHideHd = {
39720 "mouseout": function(e){
39721 if(!e.within(this.el, true)){
39725 "mouseover" : function(e){
39731 this.el.on(this.autoHideHd);
39735 clearAutoHide : function(){
39736 if(this.autoHide !== false){
39737 this.el.un("mouseout", this.autoHideHd.mouseout);
39738 this.el.un("mouseover", this.autoHideHd.mouseover);
39742 clearMonitor : function(){
39743 Roo.get(document).un("click", this.slideInIf, this);
39746 // these names are backwards but not changed for compat
39747 slideOut : function(){
39748 if(this.isSlid || this.el.hasActiveFx()){
39751 this.isSlid = true;
39752 if(this.collapseBtn){
39753 this.collapseBtn.hide();
39755 this.closeBtnState = this.closeBtn.getStyle('display');
39756 this.closeBtn.hide();
39758 this.stickBtn.show();
39761 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39762 this.beforeSlide();
39763 this.el.setStyle("z-index", 10001);
39764 this.el.slideIn(this.getSlideAnchor(), {
39765 callback: function(){
39767 this.initAutoHide();
39768 Roo.get(document).on("click", this.slideInIf, this);
39769 this.fireEvent("slideshow", this);
39776 afterSlideIn : function(){
39777 this.clearAutoHide();
39778 this.isSlid = false;
39779 this.clearMonitor();
39780 this.el.setStyle("z-index", "");
39781 if(this.collapseBtn){
39782 this.collapseBtn.show();
39784 this.closeBtn.setStyle('display', this.closeBtnState);
39786 this.stickBtn.hide();
39788 this.fireEvent("slidehide", this);
39791 slideIn : function(cb){
39792 if(!this.isSlid || this.el.hasActiveFx()){
39796 this.isSlid = false;
39797 this.beforeSlide();
39798 this.el.slideOut(this.getSlideAnchor(), {
39799 callback: function(){
39800 this.el.setLeftTop(-10000, -10000);
39802 this.afterSlideIn();
39810 slideInIf : function(e){
39811 if(!e.within(this.el)){
39816 animateCollapse : function(){
39817 this.beforeSlide();
39818 this.el.setStyle("z-index", 20000);
39819 var anchor = this.getSlideAnchor();
39820 this.el.slideOut(anchor, {
39821 callback : function(){
39822 this.el.setStyle("z-index", "");
39823 this.collapsedEl.slideIn(anchor, {duration:.3});
39825 this.el.setLocation(-10000,-10000);
39827 this.fireEvent("collapsed", this);
39834 animateExpand : function(){
39835 this.beforeSlide();
39836 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39837 this.el.setStyle("z-index", 20000);
39838 this.collapsedEl.hide({
39841 this.el.slideIn(this.getSlideAnchor(), {
39842 callback : function(){
39843 this.el.setStyle("z-index", "");
39846 this.split.el.show();
39848 this.fireEvent("invalidated", this);
39849 this.fireEvent("expanded", this);
39877 getAnchor : function(){
39878 return this.anchors[this.position];
39881 getCollapseAnchor : function(){
39882 return this.canchors[this.position];
39885 getSlideAnchor : function(){
39886 return this.sanchors[this.position];
39889 getAlignAdj : function(){
39890 var cm = this.cmargins;
39891 switch(this.position){
39907 getExpandAdj : function(){
39908 var c = this.collapsedEl, cm = this.cmargins;
39909 switch(this.position){
39911 return [-(cm.right+c.getWidth()+cm.left), 0];
39914 return [cm.right+c.getWidth()+cm.left, 0];
39917 return [0, -(cm.top+cm.bottom+c.getHeight())];
39920 return [0, cm.top+cm.bottom+c.getHeight()];
39926 * Ext JS Library 1.1.1
39927 * Copyright(c) 2006-2007, Ext JS, LLC.
39929 * Originally Released Under LGPL - original licence link has changed is not relivant.
39932 * <script type="text/javascript">
39935 * These classes are private internal classes
39937 Roo.bootstrap.layout.Center = function(config){
39938 config.region = "center";
39939 Roo.bootstrap.layout.Region.call(this, config);
39940 this.visible = true;
39941 this.minWidth = config.minWidth || 20;
39942 this.minHeight = config.minHeight || 20;
39945 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39947 // center panel can't be hidden
39951 // center panel can't be hidden
39954 getMinWidth: function(){
39955 return this.minWidth;
39958 getMinHeight: function(){
39959 return this.minHeight;
39973 Roo.bootstrap.layout.North = function(config)
39975 config.region = 'north';
39976 config.cursor = 'n-resize';
39978 Roo.bootstrap.layout.Split.call(this, config);
39982 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39983 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39984 this.split.el.addClass("roo-layout-split-v");
39986 //var size = config.initialSize || config.height;
39987 //if(this.el && typeof size != "undefined"){
39988 // this.el.setHeight(size);
39991 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39993 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39996 onRender : function(ctr, pos)
39998 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39999 var size = this.config.initialSize || this.config.height;
40000 if(this.el && typeof size != "undefined"){
40001 this.el.setHeight(size);
40006 getBox : function(){
40007 if(this.collapsed){
40008 return this.collapsedEl.getBox();
40010 var box = this.el.getBox();
40012 box.height += this.split.el.getHeight();
40017 updateBox : function(box){
40018 if(this.split && !this.collapsed){
40019 box.height -= this.split.el.getHeight();
40020 this.split.el.setLeft(box.x);
40021 this.split.el.setTop(box.y+box.height);
40022 this.split.el.setWidth(box.width);
40024 if(this.collapsed){
40025 this.updateBody(box.width, null);
40027 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40035 Roo.bootstrap.layout.South = function(config){
40036 config.region = 'south';
40037 config.cursor = 's-resize';
40038 Roo.bootstrap.layout.Split.call(this, config);
40040 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40041 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40042 this.split.el.addClass("roo-layout-split-v");
40047 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40048 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40050 onRender : function(ctr, pos)
40052 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40053 var size = this.config.initialSize || this.config.height;
40054 if(this.el && typeof size != "undefined"){
40055 this.el.setHeight(size);
40060 getBox : function(){
40061 if(this.collapsed){
40062 return this.collapsedEl.getBox();
40064 var box = this.el.getBox();
40066 var sh = this.split.el.getHeight();
40073 updateBox : function(box){
40074 if(this.split && !this.collapsed){
40075 var sh = this.split.el.getHeight();
40078 this.split.el.setLeft(box.x);
40079 this.split.el.setTop(box.y-sh);
40080 this.split.el.setWidth(box.width);
40082 if(this.collapsed){
40083 this.updateBody(box.width, null);
40085 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40089 Roo.bootstrap.layout.East = function(config){
40090 config.region = "east";
40091 config.cursor = "e-resize";
40092 Roo.bootstrap.layout.Split.call(this, config);
40094 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40095 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40096 this.split.el.addClass("roo-layout-split-h");
40100 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40101 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40103 onRender : function(ctr, pos)
40105 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40106 var size = this.config.initialSize || this.config.width;
40107 if(this.el && typeof size != "undefined"){
40108 this.el.setWidth(size);
40113 getBox : function(){
40114 if(this.collapsed){
40115 return this.collapsedEl.getBox();
40117 var box = this.el.getBox();
40119 var sw = this.split.el.getWidth();
40126 updateBox : function(box){
40127 if(this.split && !this.collapsed){
40128 var sw = this.split.el.getWidth();
40130 this.split.el.setLeft(box.x);
40131 this.split.el.setTop(box.y);
40132 this.split.el.setHeight(box.height);
40135 if(this.collapsed){
40136 this.updateBody(null, box.height);
40138 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40142 Roo.bootstrap.layout.West = function(config){
40143 config.region = "west";
40144 config.cursor = "w-resize";
40146 Roo.bootstrap.layout.Split.call(this, config);
40148 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40149 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40150 this.split.el.addClass("roo-layout-split-h");
40154 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40155 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40157 onRender: function(ctr, pos)
40159 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40160 var size = this.config.initialSize || this.config.width;
40161 if(typeof size != "undefined"){
40162 this.el.setWidth(size);
40166 getBox : function(){
40167 if(this.collapsed){
40168 return this.collapsedEl.getBox();
40170 var box = this.el.getBox();
40171 if (box.width == 0) {
40172 box.width = this.config.width; // kludge?
40175 box.width += this.split.el.getWidth();
40180 updateBox : function(box){
40181 if(this.split && !this.collapsed){
40182 var sw = this.split.el.getWidth();
40184 this.split.el.setLeft(box.x+box.width);
40185 this.split.el.setTop(box.y);
40186 this.split.el.setHeight(box.height);
40188 if(this.collapsed){
40189 this.updateBody(null, box.height);
40191 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40193 });Roo.namespace("Roo.bootstrap.panel");/*
40195 * Ext JS Library 1.1.1
40196 * Copyright(c) 2006-2007, Ext JS, LLC.
40198 * Originally Released Under LGPL - original licence link has changed is not relivant.
40201 * <script type="text/javascript">
40204 * @class Roo.bootstrap.paenl.Content
40205 * @extends Roo.util.Observable
40207 * @children Roo.bootstrap.Component
40208 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40209 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40210 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40211 * @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
40212 * @cfg {Boolean} closable True if the panel can be closed/removed
40213 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40214 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40215 * @cfg {Toolbar} toolbar A toolbar for this panel
40216 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40217 * @cfg {String} title The title for this panel
40218 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40219 * @cfg {String} url Calls {@link #setUrl} with this value
40220 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40221 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40222 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40223 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40224 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40225 * @cfg {Boolean} badges render the badges
40226 * @cfg {String} cls extra classes to use
40227 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40230 * Create a new ContentPanel.
40231 * @param {String/Object} config A string to set only the title or a config object
40234 Roo.bootstrap.panel.Content = function( config){
40236 this.tpl = config.tpl || false;
40238 var el = config.el;
40239 var content = config.content;
40241 if(config.autoCreate){ // xtype is available if this is called from factory
40244 this.el = Roo.get(el);
40245 if(!this.el && config && config.autoCreate){
40246 if(typeof config.autoCreate == "object"){
40247 if(!config.autoCreate.id){
40248 config.autoCreate.id = config.id||el;
40250 this.el = Roo.DomHelper.append(document.body,
40251 config.autoCreate, true);
40255 cls: (config.cls || '') +
40256 (config.background ? ' bg-' + config.background : '') +
40257 " roo-layout-inactive-content",
40260 if (config.iframe) {
40264 style : 'border: 0px',
40265 src : 'about:blank'
40271 elcfg.html = config.html;
40275 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40276 if (config.iframe) {
40277 this.iframeEl = this.el.select('iframe',true).first();
40282 this.closable = false;
40283 this.loaded = false;
40284 this.active = false;
40287 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40289 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40291 this.wrapEl = this.el; //this.el.wrap();
40293 if (config.toolbar.items) {
40294 ti = config.toolbar.items ;
40295 delete config.toolbar.items ;
40299 this.toolbar.render(this.wrapEl, 'before');
40300 for(var i =0;i < ti.length;i++) {
40301 // Roo.log(['add child', items[i]]);
40302 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40304 this.toolbar.items = nitems;
40305 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40306 delete config.toolbar;
40310 // xtype created footer. - not sure if will work as we normally have to render first..
40311 if (this.footer && !this.footer.el && this.footer.xtype) {
40312 if (!this.wrapEl) {
40313 this.wrapEl = this.el.wrap();
40316 this.footer.container = this.wrapEl.createChild();
40318 this.footer = Roo.factory(this.footer, Roo);
40323 if(typeof config == "string"){
40324 this.title = config;
40326 Roo.apply(this, config);
40330 this.resizeEl = Roo.get(this.resizeEl, true);
40332 this.resizeEl = this.el;
40334 // handle view.xtype
40342 * Fires when this panel is activated.
40343 * @param {Roo.ContentPanel} this
40347 * @event deactivate
40348 * Fires when this panel is activated.
40349 * @param {Roo.ContentPanel} this
40351 "deactivate" : true,
40355 * Fires when this panel is resized if fitToFrame is true.
40356 * @param {Roo.ContentPanel} this
40357 * @param {Number} width The width after any component adjustments
40358 * @param {Number} height The height after any component adjustments
40364 * Fires when this tab is created
40365 * @param {Roo.ContentPanel} this
40371 * Fires when this content is scrolled
40372 * @param {Roo.ContentPanel} this
40373 * @param {Event} scrollEvent
40384 if(this.autoScroll && !this.iframe){
40385 this.resizeEl.setStyle("overflow", "auto");
40386 this.resizeEl.on('scroll', this.onScroll, this);
40388 // fix randome scrolling
40389 //this.el.on('scroll', function() {
40390 // Roo.log('fix random scolling');
40391 // this.scrollTo('top',0);
40394 content = content || this.content;
40396 this.setContent(content);
40398 if(config && config.url){
40399 this.setUrl(this.url, this.params, this.loadOnce);
40404 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40406 if (this.view && typeof(this.view.xtype) != 'undefined') {
40407 this.view.el = this.el.appendChild(document.createElement("div"));
40408 this.view = Roo.factory(this.view);
40409 this.view.render && this.view.render(false, '');
40413 this.fireEvent('render', this);
40416 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40426 /* Resize Element - use this to work out scroll etc. */
40429 setRegion : function(region){
40430 this.region = region;
40431 this.setActiveClass(region && !this.background);
40435 setActiveClass: function(state)
40438 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40439 this.el.setStyle('position','relative');
40441 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40442 this.el.setStyle('position', 'absolute');
40447 * Returns the toolbar for this Panel if one was configured.
40448 * @return {Roo.Toolbar}
40450 getToolbar : function(){
40451 return this.toolbar;
40454 setActiveState : function(active)
40456 this.active = active;
40457 this.setActiveClass(active);
40459 if(this.fireEvent("deactivate", this) === false){
40464 this.fireEvent("activate", this);
40468 * Updates this panel's element (not for iframe)
40469 * @param {String} content The new content
40470 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40472 setContent : function(content, loadScripts){
40477 this.el.update(content, loadScripts);
40480 ignoreResize : function(w, h){
40481 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40484 this.lastSize = {width: w, height: h};
40489 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40490 * @return {Roo.UpdateManager} The UpdateManager
40492 getUpdateManager : function(){
40496 return this.el.getUpdateManager();
40499 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40500 * Does not work with IFRAME contents
40501 * @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:
40504 url: "your-url.php",
40505 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40506 callback: yourFunction,
40507 scope: yourObject, //(optional scope)
40510 text: "Loading...",
40516 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40517 * 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.
40518 * @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}
40519 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40520 * @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.
40521 * @return {Roo.ContentPanel} this
40529 var um = this.el.getUpdateManager();
40530 um.update.apply(um, arguments);
40536 * 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.
40537 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40538 * @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)
40539 * @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)
40540 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40542 setUrl : function(url, params, loadOnce){
40544 this.iframeEl.dom.src = url;
40548 if(this.refreshDelegate){
40549 this.removeListener("activate", this.refreshDelegate);
40551 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40552 this.on("activate", this.refreshDelegate);
40553 return this.el.getUpdateManager();
40556 _handleRefresh : function(url, params, loadOnce){
40557 if(!loadOnce || !this.loaded){
40558 var updater = this.el.getUpdateManager();
40559 updater.update(url, params, this._setLoaded.createDelegate(this));
40563 _setLoaded : function(){
40564 this.loaded = true;
40568 * Returns this panel's id
40571 getId : function(){
40576 * Returns this panel's element - used by regiosn to add.
40577 * @return {Roo.Element}
40579 getEl : function(){
40580 return this.wrapEl || this.el;
40585 adjustForComponents : function(width, height)
40587 //Roo.log('adjustForComponents ');
40588 if(this.resizeEl != this.el){
40589 width -= this.el.getFrameWidth('lr');
40590 height -= this.el.getFrameWidth('tb');
40593 var te = this.toolbar.getEl();
40594 te.setWidth(width);
40595 height -= te.getHeight();
40598 var te = this.footer.getEl();
40599 te.setWidth(width);
40600 height -= te.getHeight();
40604 if(this.adjustments){
40605 width += this.adjustments[0];
40606 height += this.adjustments[1];
40608 return {"width": width, "height": height};
40611 setSize : function(width, height){
40612 if(this.fitToFrame && !this.ignoreResize(width, height)){
40613 if(this.fitContainer && this.resizeEl != this.el){
40614 this.el.setSize(width, height);
40616 var size = this.adjustForComponents(width, height);
40618 this.iframeEl.setSize(width,height);
40621 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40622 this.fireEvent('resize', this, size.width, size.height);
40629 * Returns this panel's title
40632 getTitle : function(){
40634 if (typeof(this.title) != 'object') {
40639 for (var k in this.title) {
40640 if (!this.title.hasOwnProperty(k)) {
40644 if (k.indexOf('-') >= 0) {
40645 var s = k.split('-');
40646 for (var i = 0; i<s.length; i++) {
40647 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40650 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40657 * Set this panel's title
40658 * @param {String} title
40660 setTitle : function(title){
40661 this.title = title;
40663 this.region.updatePanelTitle(this, title);
40668 * Returns true is this panel was configured to be closable
40669 * @return {Boolean}
40671 isClosable : function(){
40672 return this.closable;
40675 beforeSlide : function(){
40677 this.resizeEl.clip();
40680 afterSlide : function(){
40682 this.resizeEl.unclip();
40686 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40687 * Will fail silently if the {@link #setUrl} method has not been called.
40688 * This does not activate the panel, just updates its content.
40690 refresh : function(){
40691 if(this.refreshDelegate){
40692 this.loaded = false;
40693 this.refreshDelegate();
40698 * Destroys this panel
40700 destroy : function(){
40701 this.el.removeAllListeners();
40702 var tempEl = document.createElement("span");
40703 tempEl.appendChild(this.el.dom);
40704 tempEl.innerHTML = "";
40710 * form - if the content panel contains a form - this is a reference to it.
40711 * @type {Roo.form.Form}
40715 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40716 * This contains a reference to it.
40722 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40732 * @param {Object} cfg Xtype definition of item to add.
40736 getChildContainer: function () {
40737 return this.getEl();
40741 onScroll : function(e)
40743 this.fireEvent('scroll', this, e);
40748 var ret = new Roo.factory(cfg);
40753 if (cfg.xtype.match(/^Form$/)) {
40756 //if (this.footer) {
40757 // el = this.footer.container.insertSibling(false, 'before');
40759 el = this.el.createChild();
40762 this.form = new Roo.form.Form(cfg);
40765 if ( this.form.allItems.length) {
40766 this.form.render(el.dom);
40770 // should only have one of theses..
40771 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40772 // views.. should not be just added - used named prop 'view''
40774 cfg.el = this.el.appendChild(document.createElement("div"));
40777 var ret = new Roo.factory(cfg);
40779 ret.render && ret.render(false, ''); // render blank..
40789 * @class Roo.bootstrap.panel.Grid
40790 * @extends Roo.bootstrap.panel.Content
40792 * Create a new GridPanel.
40793 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
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 {Roo.BorderLayout} layout The layout for this panel
40954 * @param {String/Object} config A string to set only the title or a config object
40956 Roo.bootstrap.panel.Nest = function(config)
40958 // construct with only one argument..
40959 /* FIXME - implement nicer consturctors
40960 if (layout.layout) {
40962 layout = config.layout;
40963 delete config.layout;
40965 if (layout.xtype && !layout.getEl) {
40966 // then layout needs constructing..
40967 layout = Roo.factory(layout, Roo);
40971 config.el = config.layout.getEl();
40973 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40975 config.layout.monitorWindowResize = false; // turn off autosizing
40976 this.layout = config.layout;
40977 this.layout.getEl().addClass("roo-layout-nested-layout");
40978 this.layout.parent = this;
40985 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40987 setSize : function(width, height){
40988 if(!this.ignoreResize(width, height)){
40989 var size = this.adjustForComponents(width, height);
40990 var el = this.layout.getEl();
40991 if (size.height < 1) {
40992 el.setWidth(size.width);
40994 el.setSize(size.width, size.height);
40996 var touch = el.dom.offsetWidth;
40997 this.layout.layout();
40998 // ie requires a double layout on the first pass
40999 if(Roo.isIE && !this.initialized){
41000 this.initialized = true;
41001 this.layout.layout();
41006 // activate all subpanels if not currently active..
41008 setActiveState : function(active){
41009 this.active = active;
41010 this.setActiveClass(active);
41013 this.fireEvent("deactivate", this);
41017 this.fireEvent("activate", this);
41018 // not sure if this should happen before or after..
41019 if (!this.layout) {
41020 return; // should not happen..
41023 for (var r in this.layout.regions) {
41024 reg = this.layout.getRegion(r);
41025 if (reg.getActivePanel()) {
41026 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41027 reg.setActivePanel(reg.getActivePanel());
41030 if (!reg.panels.length) {
41033 reg.showPanel(reg.getPanel(0));
41042 * Returns the nested BorderLayout for this panel
41043 * @return {Roo.BorderLayout}
41045 getLayout : function(){
41046 return this.layout;
41050 * Adds a xtype elements to the layout of the nested panel
41054 xtype : 'ContentPanel',
41061 xtype : 'NestedLayoutPanel',
41067 items : [ ... list of content panels or nested layout panels.. ]
41071 * @param {Object} cfg Xtype definition of item to add.
41073 addxtype : function(cfg) {
41074 return this.layout.addxtype(cfg);
41079 * Ext JS Library 1.1.1
41080 * Copyright(c) 2006-2007, Ext JS, LLC.
41082 * Originally Released Under LGPL - original licence link has changed is not relivant.
41085 * <script type="text/javascript">
41088 * @class Roo.TabPanel
41089 * @extends Roo.util.Observable
41090 * A lightweight tab container.
41094 // basic tabs 1, built from existing content
41095 var tabs = new Roo.TabPanel("tabs1");
41096 tabs.addTab("script", "View Script");
41097 tabs.addTab("markup", "View Markup");
41098 tabs.activate("script");
41100 // more advanced tabs, built from javascript
41101 var jtabs = new Roo.TabPanel("jtabs");
41102 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41104 // set up the UpdateManager
41105 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41106 var updater = tab2.getUpdateManager();
41107 updater.setDefaultUrl("ajax1.htm");
41108 tab2.on('activate', updater.refresh, updater, true);
41110 // Use setUrl for Ajax loading
41111 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41112 tab3.setUrl("ajax2.htm", null, true);
41115 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41118 jtabs.activate("jtabs-1");
41121 * Create a new TabPanel.
41122 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41123 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41125 Roo.bootstrap.panel.Tabs = function(config){
41127 * The container element for this TabPanel.
41128 * @type Roo.Element
41130 this.el = Roo.get(config.el);
41133 if(typeof config == "boolean"){
41134 this.tabPosition = config ? "bottom" : "top";
41136 Roo.apply(this, config);
41140 if(this.tabPosition == "bottom"){
41141 // if tabs are at the bottom = create the body first.
41142 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41143 this.el.addClass("roo-tabs-bottom");
41145 // next create the tabs holders
41147 if (this.tabPosition == "west"){
41149 var reg = this.region; // fake it..
41151 if (!reg.mgr.parent) {
41154 reg = reg.mgr.parent.region;
41156 Roo.log("got nest?");
41158 if (reg.mgr.getRegion('west')) {
41159 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41160 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41161 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41162 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41163 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41171 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41172 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41173 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41174 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41179 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41182 // finally - if tabs are at the top, then create the body last..
41183 if(this.tabPosition != "bottom"){
41184 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41185 * @type Roo.Element
41187 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41188 this.el.addClass("roo-tabs-top");
41192 this.bodyEl.setStyle("position", "relative");
41194 this.active = null;
41195 this.activateDelegate = this.activate.createDelegate(this);
41200 * Fires when the active tab changes
41201 * @param {Roo.TabPanel} this
41202 * @param {Roo.TabPanelItem} activePanel The new active tab
41206 * @event beforetabchange
41207 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41208 * @param {Roo.TabPanel} this
41209 * @param {Object} e Set cancel to true on this object to cancel the tab change
41210 * @param {Roo.TabPanelItem} tab The tab being changed to
41212 "beforetabchange" : true
41215 Roo.EventManager.onWindowResize(this.onResize, this);
41216 this.cpad = this.el.getPadding("lr");
41217 this.hiddenCount = 0;
41220 // toolbar on the tabbar support...
41221 if (this.toolbar) {
41222 alert("no toolbar support yet");
41223 this.toolbar = false;
41225 var tcfg = this.toolbar;
41226 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41227 this.toolbar = new Roo.Toolbar(tcfg);
41228 if (Roo.isSafari) {
41229 var tbl = tcfg.container.child('table', true);
41230 tbl.setAttribute('width', '100%');
41238 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41241 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41243 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41245 tabPosition : "top",
41247 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41249 currentTabWidth : 0,
41251 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41255 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41259 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41261 preferredTabWidth : 175,
41263 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41265 resizeTabs : false,
41267 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41269 monitorResize : true,
41271 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41273 toolbar : false, // set by caller..
41275 region : false, /// set by caller
41277 disableTooltips : true, // not used yet...
41280 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41281 * @param {String} id The id of the div to use <b>or create</b>
41282 * @param {String} text The text for the tab
41283 * @param {String} content (optional) Content to put in the TabPanelItem body
41284 * @param {Boolean} closable (optional) True to create a close icon on the tab
41285 * @return {Roo.TabPanelItem} The created TabPanelItem
41287 addTab : function(id, text, content, closable, tpl)
41289 var item = new Roo.bootstrap.panel.TabItem({
41293 closable : closable,
41296 this.addTabItem(item);
41298 item.setContent(content);
41304 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41305 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41306 * @return {Roo.TabPanelItem}
41308 getTab : function(id){
41309 return this.items[id];
41313 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41314 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41316 hideTab : function(id){
41317 var t = this.items[id];
41320 this.hiddenCount++;
41321 this.autoSizeTabs();
41326 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41327 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41329 unhideTab : function(id){
41330 var t = this.items[id];
41332 t.setHidden(false);
41333 this.hiddenCount--;
41334 this.autoSizeTabs();
41339 * Adds an existing {@link Roo.TabPanelItem}.
41340 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41342 addTabItem : function(item)
41344 this.items[item.id] = item;
41345 this.items.push(item);
41346 this.autoSizeTabs();
41347 // if(this.resizeTabs){
41348 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41349 // this.autoSizeTabs();
41351 // item.autoSize();
41356 * Removes a {@link Roo.TabPanelItem}.
41357 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41359 removeTab : function(id){
41360 var items = this.items;
41361 var tab = items[id];
41362 if(!tab) { return; }
41363 var index = items.indexOf(tab);
41364 if(this.active == tab && items.length > 1){
41365 var newTab = this.getNextAvailable(index);
41370 this.stripEl.dom.removeChild(tab.pnode.dom);
41371 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41372 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41374 items.splice(index, 1);
41375 delete this.items[tab.id];
41376 tab.fireEvent("close", tab);
41377 tab.purgeListeners();
41378 this.autoSizeTabs();
41381 getNextAvailable : function(start){
41382 var items = this.items;
41384 // look for a next tab that will slide over to
41385 // replace the one being removed
41386 while(index < items.length){
41387 var item = items[++index];
41388 if(item && !item.isHidden()){
41392 // if one isn't found select the previous tab (on the left)
41395 var item = items[--index];
41396 if(item && !item.isHidden()){
41404 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41405 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41407 disableTab : function(id){
41408 var tab = this.items[id];
41409 if(tab && this.active != tab){
41415 * Enables a {@link Roo.TabPanelItem} that is disabled.
41416 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41418 enableTab : function(id){
41419 var tab = this.items[id];
41424 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41425 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41426 * @return {Roo.TabPanelItem} The TabPanelItem.
41428 activate : function(id)
41430 //Roo.log('activite:' + id);
41432 var tab = this.items[id];
41436 if(tab == this.active || tab.disabled){
41440 this.fireEvent("beforetabchange", this, e, tab);
41441 if(e.cancel !== true && !tab.disabled){
41443 this.active.hide();
41445 this.active = this.items[id];
41446 this.active.show();
41447 this.fireEvent("tabchange", this, this.active);
41453 * Gets the active {@link Roo.TabPanelItem}.
41454 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41456 getActiveTab : function(){
41457 return this.active;
41461 * Updates the tab body element to fit the height of the container element
41462 * for overflow scrolling
41463 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41465 syncHeight : function(targetHeight){
41466 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41467 var bm = this.bodyEl.getMargins();
41468 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41469 this.bodyEl.setHeight(newHeight);
41473 onResize : function(){
41474 if(this.monitorResize){
41475 this.autoSizeTabs();
41480 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41482 beginUpdate : function(){
41483 this.updating = true;
41487 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41489 endUpdate : function(){
41490 this.updating = false;
41491 this.autoSizeTabs();
41495 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41497 autoSizeTabs : function()
41499 var count = this.items.length;
41500 var vcount = count - this.hiddenCount;
41503 this.stripEl.hide();
41505 this.stripEl.show();
41508 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41513 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41514 var availWidth = Math.floor(w / vcount);
41515 var b = this.stripBody;
41516 if(b.getWidth() > w){
41517 var tabs = this.items;
41518 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41519 if(availWidth < this.minTabWidth){
41520 /*if(!this.sleft){ // incomplete scrolling code
41521 this.createScrollButtons();
41524 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41527 if(this.currentTabWidth < this.preferredTabWidth){
41528 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41534 * Returns the number of tabs in this TabPanel.
41537 getCount : function(){
41538 return this.items.length;
41542 * Resizes all the tabs to the passed width
41543 * @param {Number} The new width
41545 setTabWidth : function(width){
41546 this.currentTabWidth = width;
41547 for(var i = 0, len = this.items.length; i < len; i++) {
41548 if(!this.items[i].isHidden()) {
41549 this.items[i].setWidth(width);
41555 * Destroys this TabPanel
41556 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41558 destroy : function(removeEl){
41559 Roo.EventManager.removeResizeListener(this.onResize, this);
41560 for(var i = 0, len = this.items.length; i < len; i++){
41561 this.items[i].purgeListeners();
41563 if(removeEl === true){
41564 this.el.update("");
41569 createStrip : function(container)
41571 var strip = document.createElement("nav");
41572 strip.className = Roo.bootstrap.version == 4 ?
41573 "navbar-light bg-light" :
41574 "navbar navbar-default"; //"x-tabs-wrap";
41575 container.appendChild(strip);
41579 createStripList : function(strip)
41581 // div wrapper for retard IE
41582 // returns the "tr" element.
41583 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41584 //'<div class="x-tabs-strip-wrap">'+
41585 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41586 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41587 return strip.firstChild; //.firstChild.firstChild.firstChild;
41589 createBody : function(container)
41591 var body = document.createElement("div");
41592 Roo.id(body, "tab-body");
41593 //Roo.fly(body).addClass("x-tabs-body");
41594 Roo.fly(body).addClass("tab-content");
41595 container.appendChild(body);
41598 createItemBody :function(bodyEl, id){
41599 var body = Roo.getDom(id);
41601 body = document.createElement("div");
41604 //Roo.fly(body).addClass("x-tabs-item-body");
41605 Roo.fly(body).addClass("tab-pane");
41606 bodyEl.insertBefore(body, bodyEl.firstChild);
41610 createStripElements : function(stripEl, text, closable, tpl)
41612 var td = document.createElement("li"); // was td..
41613 td.className = 'nav-item';
41615 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41618 stripEl.appendChild(td);
41620 td.className = "x-tabs-closable";
41621 if(!this.closeTpl){
41622 this.closeTpl = new Roo.Template(
41623 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41624 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41625 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41628 var el = this.closeTpl.overwrite(td, {"text": text});
41629 var close = el.getElementsByTagName("div")[0];
41630 var inner = el.getElementsByTagName("em")[0];
41631 return {"el": el, "close": close, "inner": inner};
41634 // not sure what this is..
41635 // if(!this.tabTpl){
41636 //this.tabTpl = new Roo.Template(
41637 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41638 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41640 // this.tabTpl = new Roo.Template(
41641 // '<a href="#">' +
41642 // '<span unselectable="on"' +
41643 // (this.disableTooltips ? '' : ' title="{text}"') +
41644 // ' >{text}</span></a>'
41650 var template = tpl || this.tabTpl || false;
41653 template = new Roo.Template(
41654 Roo.bootstrap.version == 4 ?
41656 '<a class="nav-link" href="#" unselectable="on"' +
41657 (this.disableTooltips ? '' : ' title="{text}"') +
41660 '<a class="nav-link" href="#">' +
41661 '<span unselectable="on"' +
41662 (this.disableTooltips ? '' : ' title="{text}"') +
41663 ' >{text}</span></a>'
41668 switch (typeof(template)) {
41672 template = new Roo.Template(template);
41678 var el = template.overwrite(td, {"text": text});
41680 var inner = el.getElementsByTagName("span")[0];
41682 return {"el": el, "inner": inner};
41690 * @class Roo.TabPanelItem
41691 * @extends Roo.util.Observable
41692 * Represents an individual item (tab plus body) in a TabPanel.
41693 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41694 * @param {String} id The id of this TabPanelItem
41695 * @param {String} text The text for the tab of this TabPanelItem
41696 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41698 Roo.bootstrap.panel.TabItem = function(config){
41700 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41701 * @type Roo.TabPanel
41703 this.tabPanel = config.panel;
41705 * The id for this TabPanelItem
41708 this.id = config.id;
41710 this.disabled = false;
41712 this.text = config.text;
41714 this.loaded = false;
41715 this.closable = config.closable;
41718 * The body element for this TabPanelItem.
41719 * @type Roo.Element
41721 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41722 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41723 this.bodyEl.setStyle("display", "block");
41724 this.bodyEl.setStyle("zoom", "1");
41725 //this.hideAction();
41727 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41729 this.el = Roo.get(els.el);
41730 this.inner = Roo.get(els.inner, true);
41731 this.textEl = Roo.bootstrap.version == 4 ?
41732 this.el : Roo.get(this.el.dom.firstChild, true);
41734 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41735 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41738 // this.el.on("mousedown", this.onTabMouseDown, this);
41739 this.el.on("click", this.onTabClick, this);
41741 if(config.closable){
41742 var c = Roo.get(els.close, true);
41743 c.dom.title = this.closeText;
41744 c.addClassOnOver("close-over");
41745 c.on("click", this.closeClick, this);
41751 * Fires when this tab becomes the active tab.
41752 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41753 * @param {Roo.TabPanelItem} this
41757 * @event beforeclose
41758 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41759 * @param {Roo.TabPanelItem} this
41760 * @param {Object} e Set cancel to true on this object to cancel the close.
41762 "beforeclose": true,
41765 * Fires when this tab is closed.
41766 * @param {Roo.TabPanelItem} this
41770 * @event deactivate
41771 * Fires when this tab is no longer the active tab.
41772 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773 * @param {Roo.TabPanelItem} this
41775 "deactivate" : true
41777 this.hidden = false;
41779 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41782 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41784 purgeListeners : function(){
41785 Roo.util.Observable.prototype.purgeListeners.call(this);
41786 this.el.removeAllListeners();
41789 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41792 this.status_node.addClass("active");
41795 this.tabPanel.stripWrap.repaint();
41797 this.fireEvent("activate", this.tabPanel, this);
41801 * Returns true if this tab is the active tab.
41802 * @return {Boolean}
41804 isActive : function(){
41805 return this.tabPanel.getActiveTab() == this;
41809 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41812 this.status_node.removeClass("active");
41814 this.fireEvent("deactivate", this.tabPanel, this);
41817 hideAction : function(){
41818 this.bodyEl.hide();
41819 this.bodyEl.setStyle("position", "absolute");
41820 this.bodyEl.setLeft("-20000px");
41821 this.bodyEl.setTop("-20000px");
41824 showAction : function(){
41825 this.bodyEl.setStyle("position", "relative");
41826 this.bodyEl.setTop("");
41827 this.bodyEl.setLeft("");
41828 this.bodyEl.show();
41832 * Set the tooltip for the tab.
41833 * @param {String} tooltip The tab's tooltip
41835 setTooltip : function(text){
41836 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41837 this.textEl.dom.qtip = text;
41838 this.textEl.dom.removeAttribute('title');
41840 this.textEl.dom.title = text;
41844 onTabClick : function(e){
41845 e.preventDefault();
41846 this.tabPanel.activate(this.id);
41849 onTabMouseDown : function(e){
41850 e.preventDefault();
41851 this.tabPanel.activate(this.id);
41854 getWidth : function(){
41855 return this.inner.getWidth();
41858 setWidth : function(width){
41859 var iwidth = width - this.linode.getPadding("lr");
41860 this.inner.setWidth(iwidth);
41861 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41862 this.linode.setWidth(width);
41866 * Show or hide the tab
41867 * @param {Boolean} hidden True to hide or false to show.
41869 setHidden : function(hidden){
41870 this.hidden = hidden;
41871 this.linode.setStyle("display", hidden ? "none" : "");
41875 * Returns true if this tab is "hidden"
41876 * @return {Boolean}
41878 isHidden : function(){
41879 return this.hidden;
41883 * Returns the text for this tab
41886 getText : function(){
41890 autoSize : function(){
41891 //this.el.beginMeasure();
41892 this.textEl.setWidth(1);
41894 * #2804 [new] Tabs in Roojs
41895 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41897 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41898 //this.el.endMeasure();
41902 * Sets the text for the tab (Note: this also sets the tooltip text)
41903 * @param {String} text The tab's text and tooltip
41905 setText : function(text){
41907 this.textEl.update(text);
41908 this.setTooltip(text);
41909 //if(!this.tabPanel.resizeTabs){
41910 // this.autoSize();
41914 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41916 activate : function(){
41917 this.tabPanel.activate(this.id);
41921 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41923 disable : function(){
41924 if(this.tabPanel.active != this){
41925 this.disabled = true;
41926 this.status_node.addClass("disabled");
41931 * Enables this TabPanelItem if it was previously disabled.
41933 enable : function(){
41934 this.disabled = false;
41935 this.status_node.removeClass("disabled");
41939 * Sets the content for this TabPanelItem.
41940 * @param {String} content The content
41941 * @param {Boolean} loadScripts true to look for and load scripts
41943 setContent : function(content, loadScripts){
41944 this.bodyEl.update(content, loadScripts);
41948 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41949 * @return {Roo.UpdateManager} The UpdateManager
41951 getUpdateManager : function(){
41952 return this.bodyEl.getUpdateManager();
41956 * Set a URL to be used to load the content for this TabPanelItem.
41957 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41958 * @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)
41959 * @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)
41960 * @return {Roo.UpdateManager} The UpdateManager
41962 setUrl : function(url, params, loadOnce){
41963 if(this.refreshDelegate){
41964 this.un('activate', this.refreshDelegate);
41966 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41967 this.on("activate", this.refreshDelegate);
41968 return this.bodyEl.getUpdateManager();
41972 _handleRefresh : function(url, params, loadOnce){
41973 if(!loadOnce || !this.loaded){
41974 var updater = this.bodyEl.getUpdateManager();
41975 updater.update(url, params, this._setLoaded.createDelegate(this));
41980 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41981 * Will fail silently if the setUrl method has not been called.
41982 * This does not activate the panel, just updates its content.
41984 refresh : function(){
41985 if(this.refreshDelegate){
41986 this.loaded = false;
41987 this.refreshDelegate();
41992 _setLoaded : function(){
41993 this.loaded = true;
41997 closeClick : function(e){
42000 this.fireEvent("beforeclose", this, o);
42001 if(o.cancel !== true){
42002 this.tabPanel.removeTab(this.id);
42006 * The text displayed in the tooltip for the close icon.
42009 closeText : "Close this tab"
42012 * This script refer to:
42013 * Title: International Telephone Input
42014 * Author: Jack O'Connor
42015 * Code version: v12.1.12
42016 * Availability: https://github.com/jackocnr/intl-tel-input.git
42019 Roo.bootstrap.PhoneInputData = function() {
42022 "Afghanistan (افغانستان)",
42027 "Albania (Shqipëri)",
42032 "Algeria (الجزائر)",
42057 "Antigua and Barbuda",
42067 "Armenia (Հայաստան)",
42083 "Austria (Österreich)",
42088 "Azerbaijan (Azərbaycan)",
42098 "Bahrain (البحرين)",
42103 "Bangladesh (বাংলাদেশ)",
42113 "Belarus (Беларусь)",
42118 "Belgium (België)",
42148 "Bosnia and Herzegovina (Босна и Херцеговина)",
42163 "British Indian Ocean Territory",
42168 "British Virgin Islands",
42178 "Bulgaria (България)",
42188 "Burundi (Uburundi)",
42193 "Cambodia (កម្ពុជា)",
42198 "Cameroon (Cameroun)",
42207 ["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"]
42210 "Cape Verde (Kabu Verdi)",
42215 "Caribbean Netherlands",
42226 "Central African Republic (République centrafricaine)",
42246 "Christmas Island",
42252 "Cocos (Keeling) Islands",
42263 "Comoros (جزر القمر)",
42268 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42273 "Congo (Republic) (Congo-Brazzaville)",
42293 "Croatia (Hrvatska)",
42314 "Czech Republic (Česká republika)",
42319 "Denmark (Danmark)",
42334 "Dominican Republic (República Dominicana)",
42338 ["809", "829", "849"]
42356 "Equatorial Guinea (Guinea Ecuatorial)",
42376 "Falkland Islands (Islas Malvinas)",
42381 "Faroe Islands (Føroyar)",
42402 "French Guiana (Guyane française)",
42407 "French Polynesia (Polynésie française)",
42422 "Georgia (საქართველო)",
42427 "Germany (Deutschland)",
42447 "Greenland (Kalaallit Nunaat)",
42484 "Guinea-Bissau (Guiné Bissau)",
42509 "Hungary (Magyarország)",
42514 "Iceland (Ísland)",
42534 "Iraq (العراق)",
42550 "Israel (ישראל)",
42577 "Jordan (الأردن)",
42582 "Kazakhstan (Казахстан)",
42603 "Kuwait (الكويت)",
42608 "Kyrgyzstan (Кыргызстан)",
42618 "Latvia (Latvija)",
42623 "Lebanon (لبنان)",
42638 "Libya (ليبيا)",
42648 "Lithuania (Lietuva)",
42663 "Macedonia (FYROM) (Македонија)",
42668 "Madagascar (Madagasikara)",
42698 "Marshall Islands",
42708 "Mauritania (موريتانيا)",
42713 "Mauritius (Moris)",
42734 "Moldova (Republica Moldova)",
42744 "Mongolia (Монгол)",
42749 "Montenegro (Crna Gora)",
42759 "Morocco (المغرب)",
42765 "Mozambique (Moçambique)",
42770 "Myanmar (Burma) (မြန်မာ)",
42775 "Namibia (Namibië)",
42790 "Netherlands (Nederland)",
42795 "New Caledonia (Nouvelle-Calédonie)",
42830 "North Korea (조선 민주주의 인민 공화국)",
42835 "Northern Mariana Islands",
42851 "Pakistan (پاکستان)",
42861 "Palestine (فلسطين)",
42871 "Papua New Guinea",
42913 "Réunion (La Réunion)",
42919 "Romania (România)",
42935 "Saint Barthélemy",
42946 "Saint Kitts and Nevis",
42956 "Saint Martin (Saint-Martin (partie française))",
42962 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42967 "Saint Vincent and the Grenadines",
42982 "São Tomé and Príncipe (São Tomé e Príncipe)",
42987 "Saudi Arabia (المملكة العربية السعودية)",
42992 "Senegal (Sénégal)",
43022 "Slovakia (Slovensko)",
43027 "Slovenia (Slovenija)",
43037 "Somalia (Soomaaliya)",
43047 "South Korea (대한민국)",
43052 "South Sudan (جنوب السودان)",
43062 "Sri Lanka (ශ්රී ලංකාව)",
43067 "Sudan (السودان)",
43077 "Svalbard and Jan Mayen",
43088 "Sweden (Sverige)",
43093 "Switzerland (Schweiz)",
43098 "Syria (سوريا)",
43143 "Trinidad and Tobago",
43148 "Tunisia (تونس)",
43153 "Turkey (Türkiye)",
43163 "Turks and Caicos Islands",
43173 "U.S. Virgin Islands",
43183 "Ukraine (Україна)",
43188 "United Arab Emirates (الإمارات العربية المتحدة)",
43210 "Uzbekistan (Oʻzbekiston)",
43220 "Vatican City (Città del Vaticano)",
43231 "Vietnam (Việt Nam)",
43236 "Wallis and Futuna (Wallis-et-Futuna)",
43241 "Western Sahara (الصحراء الغربية)",
43247 "Yemen (اليمن)",
43271 * This script refer to:
43272 * Title: International Telephone Input
43273 * Author: Jack O'Connor
43274 * Code version: v12.1.12
43275 * Availability: https://github.com/jackocnr/intl-tel-input.git
43279 * @class Roo.bootstrap.PhoneInput
43280 * @extends Roo.bootstrap.TriggerField
43281 * An input with International dial-code selection
43283 * @cfg {String} defaultDialCode default '+852'
43284 * @cfg {Array} preferedCountries default []
43287 * Create a new PhoneInput.
43288 * @param {Object} config Configuration options
43291 Roo.bootstrap.PhoneInput = function(config) {
43292 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43295 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43297 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43299 listWidth: undefined,
43301 selectedClass: 'active',
43303 invalidClass : "has-warning",
43305 validClass: 'has-success',
43307 allowed: '0123456789',
43312 * @cfg {String} defaultDialCode The default dial code when initializing the input
43314 defaultDialCode: '+852',
43317 * @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
43319 preferedCountries: false,
43321 getAutoCreate : function()
43323 var data = Roo.bootstrap.PhoneInputData();
43324 var align = this.labelAlign || this.parentLabelAlign();
43327 this.allCountries = [];
43328 this.dialCodeMapping = [];
43330 for (var i = 0; i < data.length; i++) {
43332 this.allCountries[i] = {
43336 priority: c[3] || 0,
43337 areaCodes: c[4] || null
43339 this.dialCodeMapping[c[2]] = {
43342 priority: c[3] || 0,
43343 areaCodes: c[4] || null
43355 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43356 maxlength: this.max_length,
43357 cls : 'form-control tel-input',
43358 autocomplete: 'new-password'
43361 var hiddenInput = {
43364 cls: 'hidden-tel-input'
43368 hiddenInput.name = this.name;
43371 if (this.disabled) {
43372 input.disabled = true;
43375 var flag_container = {
43392 cls: this.hasFeedback ? 'has-feedback' : '',
43398 cls: 'dial-code-holder',
43405 cls: 'roo-select2-container input-group',
43412 if (this.fieldLabel.length) {
43415 tooltip: 'This field is required'
43421 cls: 'control-label',
43427 html: this.fieldLabel
43430 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43436 if(this.indicatorpos == 'right') {
43437 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43444 if(align == 'left') {
43452 if(this.labelWidth > 12){
43453 label.style = "width: " + this.labelWidth + 'px';
43455 if(this.labelWidth < 13 && this.labelmd == 0){
43456 this.labelmd = this.labelWidth;
43458 if(this.labellg > 0){
43459 label.cls += ' col-lg-' + this.labellg;
43460 input.cls += ' col-lg-' + (12 - this.labellg);
43462 if(this.labelmd > 0){
43463 label.cls += ' col-md-' + this.labelmd;
43464 container.cls += ' col-md-' + (12 - this.labelmd);
43466 if(this.labelsm > 0){
43467 label.cls += ' col-sm-' + this.labelsm;
43468 container.cls += ' col-sm-' + (12 - this.labelsm);
43470 if(this.labelxs > 0){
43471 label.cls += ' col-xs-' + this.labelxs;
43472 container.cls += ' col-xs-' + (12 - this.labelxs);
43482 var settings = this;
43484 ['xs','sm','md','lg'].map(function(size){
43485 if (settings[size]) {
43486 cfg.cls += ' col-' + size + '-' + settings[size];
43490 this.store = new Roo.data.Store({
43491 proxy : new Roo.data.MemoryProxy({}),
43492 reader : new Roo.data.JsonReader({
43503 'name' : 'dialCode',
43507 'name' : 'priority',
43511 'name' : 'areaCodes',
43518 if(!this.preferedCountries) {
43519 this.preferedCountries = [
43526 var p = this.preferedCountries.reverse();
43529 for (var i = 0; i < p.length; i++) {
43530 for (var j = 0; j < this.allCountries.length; j++) {
43531 if(this.allCountries[j].iso2 == p[i]) {
43532 var t = this.allCountries[j];
43533 this.allCountries.splice(j,1);
43534 this.allCountries.unshift(t);
43540 this.store.proxy.data = {
43542 data: this.allCountries
43548 initEvents : function()
43551 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43553 this.indicator = this.indicatorEl();
43554 this.flag = this.flagEl();
43555 this.dialCodeHolder = this.dialCodeHolderEl();
43557 this.trigger = this.el.select('div.flag-box',true).first();
43558 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43563 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43564 _this.list.setWidth(lw);
43567 this.list.on('mouseover', this.onViewOver, this);
43568 this.list.on('mousemove', this.onViewMove, this);
43569 this.inputEl().on("keyup", this.onKeyUp, this);
43570 this.inputEl().on("keypress", this.onKeyPress, this);
43572 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43574 this.view = new Roo.View(this.list, this.tpl, {
43575 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43578 this.view.on('click', this.onViewClick, this);
43579 this.setValue(this.defaultDialCode);
43582 onTriggerClick : function(e)
43584 Roo.log('trigger click');
43589 if(this.isExpanded()){
43591 this.hasFocus = false;
43593 this.store.load({});
43594 this.hasFocus = true;
43599 isExpanded : function()
43601 return this.list.isVisible();
43604 collapse : function()
43606 if(!this.isExpanded()){
43610 Roo.get(document).un('mousedown', this.collapseIf, this);
43611 Roo.get(document).un('mousewheel', this.collapseIf, this);
43612 this.fireEvent('collapse', this);
43616 expand : function()
43620 if(this.isExpanded() || !this.hasFocus){
43624 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43625 this.list.setWidth(lw);
43628 this.restrictHeight();
43630 Roo.get(document).on('mousedown', this.collapseIf, this);
43631 Roo.get(document).on('mousewheel', this.collapseIf, this);
43633 this.fireEvent('expand', this);
43636 restrictHeight : function()
43638 this.list.alignTo(this.inputEl(), this.listAlign);
43639 this.list.alignTo(this.inputEl(), this.listAlign);
43642 onViewOver : function(e, t)
43644 if(this.inKeyMode){
43647 var item = this.view.findItemFromChild(t);
43650 var index = this.view.indexOf(item);
43651 this.select(index, false);
43656 onViewClick : function(view, doFocus, el, e)
43658 var index = this.view.getSelectedIndexes()[0];
43660 var r = this.store.getAt(index);
43663 this.onSelect(r, index);
43665 if(doFocus !== false && !this.blockFocus){
43666 this.inputEl().focus();
43670 onViewMove : function(e, t)
43672 this.inKeyMode = false;
43675 select : function(index, scrollIntoView)
43677 this.selectedIndex = index;
43678 this.view.select(index);
43679 if(scrollIntoView !== false){
43680 var el = this.view.getNode(index);
43682 this.list.scrollChildIntoView(el, false);
43687 createList : function()
43689 this.list = Roo.get(document.body).createChild({
43691 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43692 style: 'display:none'
43695 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43698 collapseIf : function(e)
43700 var in_combo = e.within(this.el);
43701 var in_list = e.within(this.list);
43702 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43704 if (in_combo || in_list || is_list) {
43710 onSelect : function(record, index)
43712 if(this.fireEvent('beforeselect', this, record, index) !== false){
43714 this.setFlagClass(record.data.iso2);
43715 this.setDialCode(record.data.dialCode);
43716 this.hasFocus = false;
43718 this.fireEvent('select', this, record, index);
43722 flagEl : function()
43724 var flag = this.el.select('div.flag',true).first();
43731 dialCodeHolderEl : function()
43733 var d = this.el.select('input.dial-code-holder',true).first();
43740 setDialCode : function(v)
43742 this.dialCodeHolder.dom.value = '+'+v;
43745 setFlagClass : function(n)
43747 this.flag.dom.className = 'flag '+n;
43750 getValue : function()
43752 var v = this.inputEl().getValue();
43753 if(this.dialCodeHolder) {
43754 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43759 setValue : function(v)
43761 var d = this.getDialCode(v);
43763 //invalid dial code
43764 if(v.length == 0 || !d || d.length == 0) {
43766 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43767 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43773 this.setFlagClass(this.dialCodeMapping[d].iso2);
43774 this.setDialCode(d);
43775 this.inputEl().dom.value = v.replace('+'+d,'');
43776 this.hiddenEl().dom.value = this.getValue();
43781 getDialCode : function(v)
43785 if (v.length == 0) {
43786 return this.dialCodeHolder.dom.value;
43790 if (v.charAt(0) != "+") {
43793 var numericChars = "";
43794 for (var i = 1; i < v.length; i++) {
43795 var c = v.charAt(i);
43798 if (this.dialCodeMapping[numericChars]) {
43799 dialCode = v.substr(1, i);
43801 if (numericChars.length == 4) {
43811 this.setValue(this.defaultDialCode);
43815 hiddenEl : function()
43817 return this.el.select('input.hidden-tel-input',true).first();
43820 // after setting val
43821 onKeyUp : function(e){
43822 this.setValue(this.getValue());
43825 onKeyPress : function(e){
43826 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43833 * @class Roo.bootstrap.MoneyField
43834 * @extends Roo.bootstrap.ComboBox
43835 * Bootstrap MoneyField class
43838 * Create a new MoneyField.
43839 * @param {Object} config Configuration options
43842 Roo.bootstrap.MoneyField = function(config) {
43844 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43848 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43851 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43853 allowDecimals : true,
43855 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43857 decimalSeparator : ".",
43859 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43861 decimalPrecision : 0,
43863 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43865 allowNegative : true,
43867 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43871 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43873 minValue : Number.NEGATIVE_INFINITY,
43875 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43877 maxValue : Number.MAX_VALUE,
43879 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43881 minText : "The minimum value for this field is {0}",
43883 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43885 maxText : "The maximum value for this field is {0}",
43887 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43888 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43890 nanText : "{0} is not a valid number",
43892 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43896 * @cfg {String} defaults currency of the MoneyField
43897 * value should be in lkey
43899 defaultCurrency : false,
43901 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43903 thousandsDelimiter : false,
43905 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43916 getAutoCreate : function()
43918 var align = this.labelAlign || this.parentLabelAlign();
43930 cls : 'form-control roo-money-amount-input',
43931 autocomplete: 'new-password'
43934 var hiddenInput = {
43938 cls: 'hidden-number-input'
43941 if(this.max_length) {
43942 input.maxlength = this.max_length;
43946 hiddenInput.name = this.name;
43949 if (this.disabled) {
43950 input.disabled = true;
43953 var clg = 12 - this.inputlg;
43954 var cmd = 12 - this.inputmd;
43955 var csm = 12 - this.inputsm;
43956 var cxs = 12 - this.inputxs;
43960 cls : 'row roo-money-field',
43964 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43968 cls: 'roo-select2-container input-group',
43972 cls : 'form-control roo-money-currency-input',
43973 autocomplete: 'new-password',
43975 name : this.currencyName
43979 cls : 'input-group-addon',
43993 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43997 cls: this.hasFeedback ? 'has-feedback' : '',
44008 if (this.fieldLabel.length) {
44011 tooltip: 'This field is required'
44017 cls: 'control-label',
44023 html: this.fieldLabel
44026 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44032 if(this.indicatorpos == 'right') {
44033 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44040 if(align == 'left') {
44048 if(this.labelWidth > 12){
44049 label.style = "width: " + this.labelWidth + 'px';
44051 if(this.labelWidth < 13 && this.labelmd == 0){
44052 this.labelmd = this.labelWidth;
44054 if(this.labellg > 0){
44055 label.cls += ' col-lg-' + this.labellg;
44056 input.cls += ' col-lg-' + (12 - this.labellg);
44058 if(this.labelmd > 0){
44059 label.cls += ' col-md-' + this.labelmd;
44060 container.cls += ' col-md-' + (12 - this.labelmd);
44062 if(this.labelsm > 0){
44063 label.cls += ' col-sm-' + this.labelsm;
44064 container.cls += ' col-sm-' + (12 - this.labelsm);
44066 if(this.labelxs > 0){
44067 label.cls += ' col-xs-' + this.labelxs;
44068 container.cls += ' col-xs-' + (12 - this.labelxs);
44079 var settings = this;
44081 ['xs','sm','md','lg'].map(function(size){
44082 if (settings[size]) {
44083 cfg.cls += ' col-' + size + '-' + settings[size];
44090 initEvents : function()
44092 this.indicator = this.indicatorEl();
44094 this.initCurrencyEvent();
44096 this.initNumberEvent();
44099 initCurrencyEvent : function()
44102 throw "can not find store for combo";
44105 this.store = Roo.factory(this.store, Roo.data);
44106 this.store.parent = this;
44110 this.triggerEl = this.el.select('.input-group-addon', true).first();
44112 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44117 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44118 _this.list.setWidth(lw);
44121 this.list.on('mouseover', this.onViewOver, this);
44122 this.list.on('mousemove', this.onViewMove, this);
44123 this.list.on('scroll', this.onViewScroll, this);
44126 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44129 this.view = new Roo.View(this.list, this.tpl, {
44130 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44133 this.view.on('click', this.onViewClick, this);
44135 this.store.on('beforeload', this.onBeforeLoad, this);
44136 this.store.on('load', this.onLoad, this);
44137 this.store.on('loadexception', this.onLoadException, this);
44139 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44140 "up" : function(e){
44141 this.inKeyMode = true;
44145 "down" : function(e){
44146 if(!this.isExpanded()){
44147 this.onTriggerClick();
44149 this.inKeyMode = true;
44154 "enter" : function(e){
44157 if(this.fireEvent("specialkey", this, e)){
44158 this.onViewClick(false);
44164 "esc" : function(e){
44168 "tab" : function(e){
44171 if(this.fireEvent("specialkey", this, e)){
44172 this.onViewClick(false);
44180 doRelay : function(foo, bar, hname){
44181 if(hname == 'down' || this.scope.isExpanded()){
44182 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44190 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44194 initNumberEvent : function(e)
44196 this.inputEl().on("keydown" , this.fireKey, this);
44197 this.inputEl().on("focus", this.onFocus, this);
44198 this.inputEl().on("blur", this.onBlur, this);
44200 this.inputEl().relayEvent('keyup', this);
44202 if(this.indicator){
44203 this.indicator.addClass('invisible');
44206 this.originalValue = this.getValue();
44208 if(this.validationEvent == 'keyup'){
44209 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44210 this.inputEl().on('keyup', this.filterValidation, this);
44212 else if(this.validationEvent !== false){
44213 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44216 if(this.selectOnFocus){
44217 this.on("focus", this.preFocus, this);
44220 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44221 this.inputEl().on("keypress", this.filterKeys, this);
44223 this.inputEl().relayEvent('keypress', this);
44226 var allowed = "0123456789";
44228 if(this.allowDecimals){
44229 allowed += this.decimalSeparator;
44232 if(this.allowNegative){
44236 if(this.thousandsDelimiter) {
44240 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44242 var keyPress = function(e){
44244 var k = e.getKey();
44246 var c = e.getCharCode();
44249 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44250 allowed.indexOf(String.fromCharCode(c)) === -1
44256 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44260 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44265 this.inputEl().on("keypress", keyPress, this);
44269 onTriggerClick : function(e)
44276 this.loadNext = false;
44278 if(this.isExpanded()){
44283 this.hasFocus = true;
44285 if(this.triggerAction == 'all') {
44286 this.doQuery(this.allQuery, true);
44290 this.doQuery(this.getRawValue());
44293 getCurrency : function()
44295 var v = this.currencyEl().getValue();
44300 restrictHeight : function()
44302 this.list.alignTo(this.currencyEl(), this.listAlign);
44303 this.list.alignTo(this.currencyEl(), this.listAlign);
44306 onViewClick : function(view, doFocus, el, e)
44308 var index = this.view.getSelectedIndexes()[0];
44310 var r = this.store.getAt(index);
44313 this.onSelect(r, index);
44317 onSelect : function(record, index){
44319 if(this.fireEvent('beforeselect', this, record, index) !== false){
44321 this.setFromCurrencyData(index > -1 ? record.data : false);
44325 this.fireEvent('select', this, record, index);
44329 setFromCurrencyData : function(o)
44333 this.lastCurrency = o;
44335 if (this.currencyField) {
44336 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44338 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44341 this.lastSelectionText = currency;
44343 //setting default currency
44344 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44345 this.setCurrency(this.defaultCurrency);
44349 this.setCurrency(currency);
44352 setFromData : function(o)
44356 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44358 this.setFromCurrencyData(c);
44363 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44365 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44368 this.setValue(value);
44372 setCurrency : function(v)
44374 this.currencyValue = v;
44377 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44382 setValue : function(v)
44384 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44390 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44392 this.inputEl().dom.value = (v == '') ? '' :
44393 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44395 if(!this.allowZero && v === '0') {
44396 this.hiddenEl().dom.value = '';
44397 this.inputEl().dom.value = '';
44404 getRawValue : function()
44406 var v = this.inputEl().getValue();
44411 getValue : function()
44413 return this.fixPrecision(this.parseValue(this.getRawValue()));
44416 parseValue : function(value)
44418 if(this.thousandsDelimiter) {
44420 r = new RegExp(",", "g");
44421 value = value.replace(r, "");
44424 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44425 return isNaN(value) ? '' : value;
44429 fixPrecision : function(value)
44431 if(this.thousandsDelimiter) {
44433 r = new RegExp(",", "g");
44434 value = value.replace(r, "");
44437 var nan = isNaN(value);
44439 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44440 return nan ? '' : value;
44442 return parseFloat(value).toFixed(this.decimalPrecision);
44445 decimalPrecisionFcn : function(v)
44447 return Math.floor(v);
44450 validateValue : function(value)
44452 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44456 var num = this.parseValue(value);
44459 this.markInvalid(String.format(this.nanText, value));
44463 if(num < this.minValue){
44464 this.markInvalid(String.format(this.minText, this.minValue));
44468 if(num > this.maxValue){
44469 this.markInvalid(String.format(this.maxText, this.maxValue));
44476 validate : function()
44478 if(this.disabled || this.allowBlank){
44483 var currency = this.getCurrency();
44485 if(this.validateValue(this.getRawValue()) && currency.length){
44490 this.markInvalid();
44494 getName: function()
44499 beforeBlur : function()
44505 var v = this.parseValue(this.getRawValue());
44512 onBlur : function()
44516 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44517 //this.el.removeClass(this.focusClass);
44520 this.hasFocus = false;
44522 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44526 var v = this.getValue();
44528 if(String(v) !== String(this.startValue)){
44529 this.fireEvent('change', this, v, this.startValue);
44532 this.fireEvent("blur", this);
44535 inputEl : function()
44537 return this.el.select('.roo-money-amount-input', true).first();
44540 currencyEl : function()
44542 return this.el.select('.roo-money-currency-input', true).first();
44545 hiddenEl : function()
44547 return this.el.select('input.hidden-number-input',true).first();
44551 * @class Roo.bootstrap.BezierSignature
44552 * @extends Roo.bootstrap.Component
44553 * Bootstrap BezierSignature class
44554 * This script refer to:
44555 * Title: Signature Pad
44557 * Availability: https://github.com/szimek/signature_pad
44560 * Create a new BezierSignature
44561 * @param {Object} config The config object
44564 Roo.bootstrap.BezierSignature = function(config){
44565 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44571 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44578 mouse_btn_down: true,
44581 * @cfg {int} canvas height
44583 canvas_height: '200px',
44586 * @cfg {float|function} Radius of a single dot.
44591 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44596 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44601 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44606 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44611 * @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.
44613 bg_color: 'rgba(0, 0, 0, 0)',
44616 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44618 dot_color: 'black',
44621 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44623 velocity_filter_weight: 0.7,
44626 * @cfg {function} Callback when stroke begin.
44631 * @cfg {function} Callback when stroke end.
44635 getAutoCreate : function()
44637 var cls = 'roo-signature column';
44640 cls += ' ' + this.cls;
44650 for(var i = 0; i < col_sizes.length; i++) {
44651 if(this[col_sizes[i]]) {
44652 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44662 cls: 'roo-signature-body',
44666 cls: 'roo-signature-body-canvas',
44667 height: this.canvas_height,
44668 width: this.canvas_width
44675 style: 'display: none'
44683 initEvents: function()
44685 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44687 var canvas = this.canvasEl();
44689 // mouse && touch event swapping...
44690 canvas.dom.style.touchAction = 'none';
44691 canvas.dom.style.msTouchAction = 'none';
44693 this.mouse_btn_down = false;
44694 canvas.on('mousedown', this._handleMouseDown, this);
44695 canvas.on('mousemove', this._handleMouseMove, this);
44696 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44698 if (window.PointerEvent) {
44699 canvas.on('pointerdown', this._handleMouseDown, this);
44700 canvas.on('pointermove', this._handleMouseMove, this);
44701 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44704 if ('ontouchstart' in window) {
44705 canvas.on('touchstart', this._handleTouchStart, this);
44706 canvas.on('touchmove', this._handleTouchMove, this);
44707 canvas.on('touchend', this._handleTouchEnd, this);
44710 Roo.EventManager.onWindowResize(this.resize, this, true);
44712 // file input event
44713 this.fileEl().on('change', this.uploadImage, this);
44720 resize: function(){
44722 var canvas = this.canvasEl().dom;
44723 var ctx = this.canvasElCtx();
44724 var img_data = false;
44726 if(canvas.width > 0) {
44727 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44729 // setting canvas width will clean img data
44732 var style = window.getComputedStyle ?
44733 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44735 var padding_left = parseInt(style.paddingLeft) || 0;
44736 var padding_right = parseInt(style.paddingRight) || 0;
44738 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44741 ctx.putImageData(img_data, 0, 0);
44745 _handleMouseDown: function(e)
44747 if (e.browserEvent.which === 1) {
44748 this.mouse_btn_down = true;
44749 this.strokeBegin(e);
44753 _handleMouseMove: function (e)
44755 if (this.mouse_btn_down) {
44756 this.strokeMoveUpdate(e);
44760 _handleMouseUp: function (e)
44762 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44763 this.mouse_btn_down = false;
44768 _handleTouchStart: function (e) {
44770 e.preventDefault();
44771 if (e.browserEvent.targetTouches.length === 1) {
44772 // var touch = e.browserEvent.changedTouches[0];
44773 // this.strokeBegin(touch);
44775 this.strokeBegin(e); // assume e catching the correct xy...
44779 _handleTouchMove: function (e) {
44780 e.preventDefault();
44781 // var touch = event.targetTouches[0];
44782 // _this._strokeMoveUpdate(touch);
44783 this.strokeMoveUpdate(e);
44786 _handleTouchEnd: function (e) {
44787 var wasCanvasTouched = e.target === this.canvasEl().dom;
44788 if (wasCanvasTouched) {
44789 e.preventDefault();
44790 // var touch = event.changedTouches[0];
44791 // _this._strokeEnd(touch);
44796 reset: function () {
44797 this._lastPoints = [];
44798 this._lastVelocity = 0;
44799 this._lastWidth = (this.min_width + this.max_width) / 2;
44800 this.canvasElCtx().fillStyle = this.dot_color;
44803 strokeMoveUpdate: function(e)
44805 this.strokeUpdate(e);
44807 if (this.throttle) {
44808 this.throttleStroke(this.strokeUpdate, this.throttle);
44811 this.strokeUpdate(e);
44815 strokeBegin: function(e)
44817 var newPointGroup = {
44818 color: this.dot_color,
44822 if (typeof this.onBegin === 'function') {
44826 this.curve_data.push(newPointGroup);
44828 this.strokeUpdate(e);
44831 strokeUpdate: function(e)
44833 var rect = this.canvasEl().dom.getBoundingClientRect();
44834 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44835 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44836 var lastPoints = lastPointGroup.points;
44837 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44838 var isLastPointTooClose = lastPoint
44839 ? point.distanceTo(lastPoint) <= this.min_distance
44841 var color = lastPointGroup.color;
44842 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44843 var curve = this.addPoint(point);
44845 this.drawDot({color: color, point: point});
44848 this.drawCurve({color: color, curve: curve});
44858 strokeEnd: function(e)
44860 this.strokeUpdate(e);
44861 if (typeof this.onEnd === 'function') {
44866 addPoint: function (point) {
44867 var _lastPoints = this._lastPoints;
44868 _lastPoints.push(point);
44869 if (_lastPoints.length > 2) {
44870 if (_lastPoints.length === 3) {
44871 _lastPoints.unshift(_lastPoints[0]);
44873 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44874 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44875 _lastPoints.shift();
44881 calculateCurveWidths: function (startPoint, endPoint) {
44882 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44883 (1 - this.velocity_filter_weight) * this._lastVelocity;
44885 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44888 start: this._lastWidth
44891 this._lastVelocity = velocity;
44892 this._lastWidth = newWidth;
44896 drawDot: function (_a) {
44897 var color = _a.color, point = _a.point;
44898 var ctx = this.canvasElCtx();
44899 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44901 this.drawCurveSegment(point.x, point.y, width);
44903 ctx.fillStyle = color;
44907 drawCurve: function (_a) {
44908 var color = _a.color, curve = _a.curve;
44909 var ctx = this.canvasElCtx();
44910 var widthDelta = curve.endWidth - curve.startWidth;
44911 var drawSteps = Math.floor(curve.length()) * 2;
44913 ctx.fillStyle = color;
44914 for (var i = 0; i < drawSteps; i += 1) {
44915 var t = i / drawSteps;
44921 var x = uuu * curve.startPoint.x;
44922 x += 3 * uu * t * curve.control1.x;
44923 x += 3 * u * tt * curve.control2.x;
44924 x += ttt * curve.endPoint.x;
44925 var y = uuu * curve.startPoint.y;
44926 y += 3 * uu * t * curve.control1.y;
44927 y += 3 * u * tt * curve.control2.y;
44928 y += ttt * curve.endPoint.y;
44929 var width = curve.startWidth + ttt * widthDelta;
44930 this.drawCurveSegment(x, y, width);
44936 drawCurveSegment: function (x, y, width) {
44937 var ctx = this.canvasElCtx();
44939 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44940 this.is_empty = false;
44945 var ctx = this.canvasElCtx();
44946 var canvas = this.canvasEl().dom;
44947 ctx.fillStyle = this.bg_color;
44948 ctx.clearRect(0, 0, canvas.width, canvas.height);
44949 ctx.fillRect(0, 0, canvas.width, canvas.height);
44950 this.curve_data = [];
44952 this.is_empty = true;
44957 return this.el.select('input',true).first();
44960 canvasEl: function()
44962 return this.el.select('canvas',true).first();
44965 canvasElCtx: function()
44967 return this.el.select('canvas',true).first().dom.getContext('2d');
44970 getImage: function(type)
44972 if(this.is_empty) {
44977 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44980 drawFromImage: function(img_src)
44982 var img = new Image();
44984 img.onload = function(){
44985 this.canvasElCtx().drawImage(img, 0, 0);
44990 this.is_empty = false;
44993 selectImage: function()
44995 this.fileEl().dom.click();
44998 uploadImage: function(e)
45000 var reader = new FileReader();
45002 reader.onload = function(e){
45003 var img = new Image();
45004 img.onload = function(){
45006 this.canvasElCtx().drawImage(img, 0, 0);
45008 img.src = e.target.result;
45011 reader.readAsDataURL(e.target.files[0]);
45014 // Bezier Point Constructor
45015 Point: (function () {
45016 function Point(x, y, time) {
45019 this.time = time || Date.now();
45021 Point.prototype.distanceTo = function (start) {
45022 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45024 Point.prototype.equals = function (other) {
45025 return this.x === other.x && this.y === other.y && this.time === other.time;
45027 Point.prototype.velocityFrom = function (start) {
45028 return this.time !== start.time
45029 ? this.distanceTo(start) / (this.time - start.time)
45036 // Bezier Constructor
45037 Bezier: (function () {
45038 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45039 this.startPoint = startPoint;
45040 this.control2 = control2;
45041 this.control1 = control1;
45042 this.endPoint = endPoint;
45043 this.startWidth = startWidth;
45044 this.endWidth = endWidth;
45046 Bezier.fromPoints = function (points, widths, scope) {
45047 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45048 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45049 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45051 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45052 var dx1 = s1.x - s2.x;
45053 var dy1 = s1.y - s2.y;
45054 var dx2 = s2.x - s3.x;
45055 var dy2 = s2.y - s3.y;
45056 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45057 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45058 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45059 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45060 var dxm = m1.x - m2.x;
45061 var dym = m1.y - m2.y;
45062 var k = l2 / (l1 + l2);
45063 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45064 var tx = s2.x - cm.x;
45065 var ty = s2.y - cm.y;
45067 c1: new scope.Point(m1.x + tx, m1.y + ty),
45068 c2: new scope.Point(m2.x + tx, m2.y + ty)
45071 Bezier.prototype.length = function () {
45076 for (var i = 0; i <= steps; i += 1) {
45078 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45079 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45081 var xdiff = cx - px;
45082 var ydiff = cy - py;
45083 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45090 Bezier.prototype.point = function (t, start, c1, c2, end) {
45091 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45092 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45093 + (3.0 * c2 * (1.0 - t) * t * t)
45094 + (end * t * t * t);
45099 throttleStroke: function(fn, wait) {
45100 if (wait === void 0) { wait = 250; }
45102 var timeout = null;
45106 var later = function () {
45107 previous = Date.now();
45109 result = fn.apply(storedContext, storedArgs);
45111 storedContext = null;
45115 return function wrapper() {
45117 for (var _i = 0; _i < arguments.length; _i++) {
45118 args[_i] = arguments[_i];
45120 var now = Date.now();
45121 var remaining = wait - (now - previous);
45122 storedContext = this;
45124 if (remaining <= 0 || remaining > wait) {
45126 clearTimeout(timeout);
45130 result = fn.apply(storedContext, storedArgs);
45132 storedContext = null;
45136 else if (!timeout) {
45137 timeout = window.setTimeout(later, remaining);