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)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<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>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <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>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7311 * @extends Roo.dd.DDProxy
7312 * @class Roo.grid.SplitDragZone
7313 * Support for Column Header resizing
7315 * @param {Object} config
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321 this.view = grid.getView();
7322 this.proxy = this.view.resizeProxy;
7323 Roo.grid.SplitDragZone.superclass.constructor.call(
7326 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328 dragElId : Roo.id(this.proxy.dom),
7333 this.setHandleElId(Roo.id(hd));
7334 if (hd2 !== false) {
7335 this.setOuterHandleElId(Roo.id(hd2));
7338 this.scroll = false;
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341 fly: Roo.Element.fly,
7343 b4StartDrag : function(x, y){
7344 this.view.headersDisabled = true;
7345 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348 this.proxy.setHeight(h);
7350 // for old system colWidth really stored the actual width?
7351 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352 // which in reality did not work.. - it worked only for fixed sizes
7353 // for resizable we need to use actual sizes.
7354 var w = this.cm.getColumnWidth(this.cellIndex);
7355 if (!this.view.mainWrap) {
7357 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362 // this was w-this.grid.minColumnWidth;
7363 // doesnt really make sense? - w = thie curren width or the rendered one?
7364 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365 this.resetConstraints();
7366 this.setXConstraint(minw, 1000);
7367 this.setYConstraint(0, 0);
7368 this.minX = x - minw;
7369 this.maxX = x + 1000;
7371 if (!this.view.mainWrap) { // this is Bootstrap code..
7372 this.getDragEl().style.display='block';
7375 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379 handleMouseDown : function(e){
7380 ev = Roo.EventObject.setEvent(e);
7381 var t = this.fly(ev.getTarget());
7382 if(t.hasClass("x-grid-split")){
7383 this.cellIndex = this.view.getCellIndex(t.dom);
7385 this.cm = this.grid.colModel;
7386 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392 endDrag : function(e){
7393 this.view.headersDisabled = false;
7394 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395 var diff = endX - this.startPos;
7396 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
7399 autoOffset : function(){
7404 * Ext JS Library 1.1.1
7405 * Copyright(c) 2006-2007, Ext JS, LLC.
7407 * Originally Released Under LGPL - original licence link has changed is not relivant.
7410 * <script type="text/javascript">
7414 * @class Roo.grid.AbstractSelectionModel
7415 * @extends Roo.util.Observable
7416 * Abstract base class for grid SelectionModels. It provides the interface that should be
7417 * implemented by descendant classes. This class should not be directly instantiated.
7420 Roo.grid.AbstractSelectionModel = function(){
7421 this.locked = false;
7422 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7425 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7426 /** @ignore Called by the grid automatically. Do not call directly. */
7427 init : function(grid){
7433 * Locks the selections.
7440 * Unlocks the selections.
7442 unlock : function(){
7443 this.locked = false;
7447 * Returns true if the selections are locked.
7450 isLocked : function(){
7455 * Ext JS Library 1.1.1
7456 * Copyright(c) 2006-2007, Ext JS, LLC.
7458 * Originally Released Under LGPL - original licence link has changed is not relivant.
7461 * <script type="text/javascript">
7464 * @extends Roo.grid.AbstractSelectionModel
7465 * @class Roo.grid.RowSelectionModel
7466 * The default SelectionModel used by {@link Roo.grid.Grid}.
7467 * It supports multiple selections and keyboard selection/navigation.
7469 * @param {Object} config
7471 Roo.grid.RowSelectionModel = function(config){
7472 Roo.apply(this, config);
7473 this.selections = new Roo.util.MixedCollection(false, function(o){
7478 this.lastActive = false;
7482 * @event selectionchange
7483 * Fires when the selection changes
7484 * @param {SelectionModel} this
7486 "selectionchange" : true,
7488 * @event afterselectionchange
7489 * Fires after the selection changes (eg. by key press or clicking)
7490 * @param {SelectionModel} this
7492 "afterselectionchange" : true,
7494 * @event beforerowselect
7495 * Fires when a row is selected being selected, return false to cancel.
7496 * @param {SelectionModel} this
7497 * @param {Number} rowIndex The selected index
7498 * @param {Boolean} keepExisting False if other selections will be cleared
7500 "beforerowselect" : true,
7503 * Fires when a row is selected.
7504 * @param {SelectionModel} this
7505 * @param {Number} rowIndex The selected index
7506 * @param {Roo.data.Record} r The record
7510 * @event rowdeselect
7511 * Fires when a row is deselected.
7512 * @param {SelectionModel} this
7513 * @param {Number} rowIndex The selected index
7515 "rowdeselect" : true
7517 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7518 this.locked = false;
7521 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7523 * @cfg {Boolean} singleSelect
7524 * True to allow selection of only one row at a time (defaults to false)
7526 singleSelect : false,
7529 initEvents : function(){
7531 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7532 this.grid.on("mousedown", this.handleMouseDown, this);
7533 }else{ // allow click to work like normal
7534 this.grid.on("rowclick", this.handleDragableRowClick, this);
7536 // bootstrap does not have a view..
7537 var view = this.grid.view ? this.grid.view : this.grid;
7538 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7541 this.selectPrevious(e.shiftKey);
7542 }else if(this.last !== false && this.lastActive !== false){
7543 var last = this.last;
7544 this.selectRange(this.last, this.lastActive-1);
7545 view.focusRow(this.lastActive);
7550 this.selectFirstRow();
7552 this.fireEvent("afterselectionchange", this);
7554 "down" : function(e){
7556 this.selectNext(e.shiftKey);
7557 }else if(this.last !== false && this.lastActive !== false){
7558 var last = this.last;
7559 this.selectRange(this.last, this.lastActive+1);
7560 view.focusRow(this.lastActive);
7565 this.selectFirstRow();
7567 this.fireEvent("afterselectionchange", this);
7573 view.on("refresh", this.onRefresh, this);
7574 view.on("rowupdated", this.onRowUpdated, this);
7575 view.on("rowremoved", this.onRemove, this);
7579 onRefresh : function(){
7580 var ds = this.grid.ds, i, v = this.grid.view;
7581 var s = this.selections;
7583 if((i = ds.indexOfId(r.id)) != -1){
7585 s.add(ds.getAt(i)); // updating the selection relate data
7593 onRemove : function(v, index, r){
7594 this.selections.remove(r);
7598 onRowUpdated : function(v, index, r){
7599 if(this.isSelected(r)){
7600 v.onRowSelect(index);
7606 * @param {Array} records The records to select
7607 * @param {Boolean} keepExisting (optional) True to keep existing selections
7609 selectRecords : function(records, keepExisting){
7611 this.clearSelections();
7613 var ds = this.grid.ds;
7614 for(var i = 0, len = records.length; i < len; i++){
7615 this.selectRow(ds.indexOf(records[i]), true);
7620 * Gets the number of selected rows.
7623 getCount : function(){
7624 return this.selections.length;
7628 * Selects the first row in the grid.
7630 selectFirstRow : function(){
7635 * Select the last row.
7636 * @param {Boolean} keepExisting (optional) True to keep existing selections
7638 selectLastRow : function(keepExisting){
7639 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7643 * Selects the row immediately following the last selected row.
7644 * @param {Boolean} keepExisting (optional) True to keep existing selections
7646 selectNext : function(keepExisting){
7647 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7648 this.selectRow(this.last+1, keepExisting);
7649 var view = this.grid.view ? this.grid.view : this.grid;
7650 view.focusRow(this.last);
7655 * Selects the row that precedes the last selected row.
7656 * @param {Boolean} keepExisting (optional) True to keep existing selections
7658 selectPrevious : function(keepExisting){
7660 this.selectRow(this.last-1, keepExisting);
7661 var view = this.grid.view ? this.grid.view : this.grid;
7662 view.focusRow(this.last);
7667 * Returns the selected records
7668 * @return {Array} Array of selected records
7670 getSelections : function(){
7671 return [].concat(this.selections.items);
7675 * Returns the first selected record.
7678 getSelected : function(){
7679 return this.selections.itemAt(0);
7684 * Clears all selections.
7686 clearSelections : function(fast){
7691 var ds = this.grid.ds;
7692 var s = this.selections;
7694 this.deselectRow(ds.indexOfId(r.id));
7698 this.selections.clear();
7707 selectAll : function(){
7711 this.selections.clear();
7712 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7713 this.selectRow(i, true);
7718 * Returns True if there is a selection.
7721 hasSelection : function(){
7722 return this.selections.length > 0;
7726 * Returns True if the specified row is selected.
7727 * @param {Number/Record} record The record or index of the record to check
7730 isSelected : function(index){
7731 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7732 return (r && this.selections.key(r.id) ? true : false);
7736 * Returns True if the specified record id is selected.
7737 * @param {String} id The id of record to check
7740 isIdSelected : function(id){
7741 return (this.selections.key(id) ? true : false);
7745 handleMouseDown : function(e, t)
7747 var view = this.grid.view ? this.grid.view : this.grid;
7749 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7752 if(e.shiftKey && this.last !== false){
7753 var last = this.last;
7754 this.selectRange(last, rowIndex, e.ctrlKey);
7755 this.last = last; // reset the last
7756 view.focusRow(rowIndex);
7758 var isSelected = this.isSelected(rowIndex);
7759 if(e.button !== 0 && isSelected){
7760 view.focusRow(rowIndex);
7761 }else if(e.ctrlKey && isSelected){
7762 this.deselectRow(rowIndex);
7763 }else if(!isSelected){
7764 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7765 view.focusRow(rowIndex);
7768 this.fireEvent("afterselectionchange", this);
7771 handleDragableRowClick : function(grid, rowIndex, e)
7773 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7774 this.selectRow(rowIndex, false);
7775 var view = this.grid.view ? this.grid.view : this.grid;
7776 view.focusRow(rowIndex);
7777 this.fireEvent("afterselectionchange", this);
7782 * Selects multiple rows.
7783 * @param {Array} rows Array of the indexes of the row to select
7784 * @param {Boolean} keepExisting (optional) True to keep existing selections
7786 selectRows : function(rows, keepExisting){
7788 this.clearSelections();
7790 for(var i = 0, len = rows.length; i < len; i++){
7791 this.selectRow(rows[i], true);
7796 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7797 * @param {Number} startRow The index of the first row in the range
7798 * @param {Number} endRow The index of the last row in the range
7799 * @param {Boolean} keepExisting (optional) True to retain existing selections
7801 selectRange : function(startRow, endRow, keepExisting){
7806 this.clearSelections();
7808 if(startRow <= endRow){
7809 for(var i = startRow; i <= endRow; i++){
7810 this.selectRow(i, true);
7813 for(var i = startRow; i >= endRow; i--){
7814 this.selectRow(i, true);
7820 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7821 * @param {Number} startRow The index of the first row in the range
7822 * @param {Number} endRow The index of the last row in the range
7824 deselectRange : function(startRow, endRow, preventViewNotify){
7828 for(var i = startRow; i <= endRow; i++){
7829 this.deselectRow(i, preventViewNotify);
7835 * @param {Number} row The index of the row to select
7836 * @param {Boolean} keepExisting (optional) True to keep existing selections
7838 selectRow : function(index, keepExisting, preventViewNotify){
7839 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7842 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7843 if(!keepExisting || this.singleSelect){
7844 this.clearSelections();
7846 var r = this.grid.ds.getAt(index);
7847 this.selections.add(r);
7848 this.last = this.lastActive = index;
7849 if(!preventViewNotify){
7850 var view = this.grid.view ? this.grid.view : this.grid;
7851 view.onRowSelect(index);
7853 this.fireEvent("rowselect", this, index, r);
7854 this.fireEvent("selectionchange", this);
7860 * @param {Number} row The index of the row to deselect
7862 deselectRow : function(index, preventViewNotify){
7866 if(this.last == index){
7869 if(this.lastActive == index){
7870 this.lastActive = false;
7872 var r = this.grid.ds.getAt(index);
7873 this.selections.remove(r);
7874 if(!preventViewNotify){
7875 var view = this.grid.view ? this.grid.view : this.grid;
7876 view.onRowDeselect(index);
7878 this.fireEvent("rowdeselect", this, index);
7879 this.fireEvent("selectionchange", this);
7883 restoreLast : function(){
7885 this.last = this._last;
7890 acceptsNav : function(row, col, cm){
7891 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7895 onEditorKey : function(field, e){
7896 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7901 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7903 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7905 }else if(k == e.ENTER && !e.ctrlKey){
7909 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7911 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7913 }else if(k == e.ESC){
7917 g.startEditing(newCell[0], newCell[1]);
7922 * Ext JS Library 1.1.1
7923 * Copyright(c) 2006-2007, Ext JS, LLC.
7925 * Originally Released Under LGPL - original licence link has changed is not relivant.
7928 * <script type="text/javascript">
7933 * @class Roo.grid.ColumnModel
7934 * @extends Roo.util.Observable
7935 * This is the default implementation of a ColumnModel used by the Grid. It defines
7936 * the columns in the grid.
7939 var colModel = new Roo.grid.ColumnModel([
7940 {header: "Ticker", width: 60, sortable: true, locked: true},
7941 {header: "Company Name", width: 150, sortable: true},
7942 {header: "Market Cap.", width: 100, sortable: true},
7943 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7944 {header: "Employees", width: 100, sortable: true, resizable: false}
7949 * The config options listed for this class are options which may appear in each
7950 * individual column definition.
7951 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7953 * @param {Object} config An Array of column config objects. See this class's
7954 * config objects for details.
7956 Roo.grid.ColumnModel = function(config){
7958 * The config passed into the constructor
7960 this.config = []; //config;
7963 // if no id, create one
7964 // if the column does not have a dataIndex mapping,
7965 // map it to the order it is in the config
7966 for(var i = 0, len = config.length; i < len; i++){
7967 this.addColumn(config[i]);
7972 * The width of columns which have no width specified (defaults to 100)
7975 this.defaultWidth = 100;
7978 * Default sortable of columns which have no sortable specified (defaults to false)
7981 this.defaultSortable = false;
7985 * @event widthchange
7986 * Fires when the width of a column changes.
7987 * @param {ColumnModel} this
7988 * @param {Number} columnIndex The column index
7989 * @param {Number} newWidth The new width
7991 "widthchange": true,
7993 * @event headerchange
7994 * Fires when the text of a header changes.
7995 * @param {ColumnModel} this
7996 * @param {Number} columnIndex The column index
7997 * @param {Number} newText The new header text
7999 "headerchange": true,
8001 * @event hiddenchange
8002 * Fires when a column is hidden or "unhidden".
8003 * @param {ColumnModel} this
8004 * @param {Number} columnIndex The column index
8005 * @param {Boolean} hidden true if hidden, false otherwise
8007 "hiddenchange": true,
8009 * @event columnmoved
8010 * Fires when a column is moved.
8011 * @param {ColumnModel} this
8012 * @param {Number} oldIndex
8013 * @param {Number} newIndex
8015 "columnmoved" : true,
8017 * @event columlockchange
8018 * Fires when a column's locked state is changed
8019 * @param {ColumnModel} this
8020 * @param {Number} colIndex
8021 * @param {Boolean} locked true if locked
8023 "columnlockchange" : true
8025 Roo.grid.ColumnModel.superclass.constructor.call(this);
8027 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8029 * @cfg {String} header The header text to display in the Grid view.
8032 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8033 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8034 * specified, the column's index is used as an index into the Record's data Array.
8037 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8038 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8041 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8042 * Defaults to the value of the {@link #defaultSortable} property.
8043 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8046 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8049 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8052 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8055 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8058 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8059 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8060 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8061 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8064 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8067 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8070 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8073 * @cfg {String} cursor (Optional)
8076 * @cfg {String} tooltip (Optional)
8079 * @cfg {Number} xs (Optional)
8082 * @cfg {Number} sm (Optional)
8085 * @cfg {Number} md (Optional)
8088 * @cfg {Number} lg (Optional)
8091 * Returns the id of the column at the specified index.
8092 * @param {Number} index The column index
8093 * @return {String} the id
8095 getColumnId : function(index){
8096 return this.config[index].id;
8100 * Returns the column for a specified id.
8101 * @param {String} id The column id
8102 * @return {Object} the column
8104 getColumnById : function(id){
8105 return this.lookup[id];
8110 * Returns the column Object for a specified dataIndex.
8111 * @param {String} dataIndex The column dataIndex
8112 * @return {Object|Boolean} the column or false if not found
8114 getColumnByDataIndex: function(dataIndex){
8115 var index = this.findColumnIndex(dataIndex);
8116 return index > -1 ? this.config[index] : false;
8120 * Returns the index for a specified column id.
8121 * @param {String} id The column id
8122 * @return {Number} the index, or -1 if not found
8124 getIndexById : function(id){
8125 for(var i = 0, len = this.config.length; i < len; i++){
8126 if(this.config[i].id == id){
8134 * Returns the index for a specified column dataIndex.
8135 * @param {String} dataIndex The column dataIndex
8136 * @return {Number} the index, or -1 if not found
8139 findColumnIndex : function(dataIndex){
8140 for(var i = 0, len = this.config.length; i < len; i++){
8141 if(this.config[i].dataIndex == dataIndex){
8149 moveColumn : function(oldIndex, newIndex){
8150 var c = this.config[oldIndex];
8151 this.config.splice(oldIndex, 1);
8152 this.config.splice(newIndex, 0, c);
8153 this.dataMap = null;
8154 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8157 isLocked : function(colIndex){
8158 return this.config[colIndex].locked === true;
8161 setLocked : function(colIndex, value, suppressEvent){
8162 if(this.isLocked(colIndex) == value){
8165 this.config[colIndex].locked = value;
8167 this.fireEvent("columnlockchange", this, colIndex, value);
8171 getTotalLockedWidth : function(){
8173 for(var i = 0; i < this.config.length; i++){
8174 if(this.isLocked(i) && !this.isHidden(i)){
8175 this.totalWidth += this.getColumnWidth(i);
8181 getLockedCount : function(){
8182 for(var i = 0, len = this.config.length; i < len; i++){
8183 if(!this.isLocked(i)){
8188 return this.config.length;
8192 * Returns the number of columns.
8195 getColumnCount : function(visibleOnly){
8196 if(visibleOnly === true){
8198 for(var i = 0, len = this.config.length; i < len; i++){
8199 if(!this.isHidden(i)){
8205 return this.config.length;
8209 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8210 * @param {Function} fn
8211 * @param {Object} scope (optional)
8212 * @return {Array} result
8214 getColumnsBy : function(fn, scope){
8216 for(var i = 0, len = this.config.length; i < len; i++){
8217 var c = this.config[i];
8218 if(fn.call(scope||this, c, i) === true){
8226 * Returns true if the specified column is sortable.
8227 * @param {Number} col The column index
8230 isSortable : function(col){
8231 if(typeof this.config[col].sortable == "undefined"){
8232 return this.defaultSortable;
8234 return this.config[col].sortable;
8238 * Returns the rendering (formatting) function defined for the column.
8239 * @param {Number} col The column index.
8240 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8242 getRenderer : function(col){
8243 if(!this.config[col].renderer){
8244 return Roo.grid.ColumnModel.defaultRenderer;
8246 return this.config[col].renderer;
8250 * Sets the rendering (formatting) function for a column.
8251 * @param {Number} col The column index
8252 * @param {Function} fn The function to use to process the cell's raw data
8253 * to return HTML markup for the grid view. The render function is called with
8254 * the following parameters:<ul>
8255 * <li>Data value.</li>
8256 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8257 * <li>css A CSS style string to apply to the table cell.</li>
8258 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8259 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8260 * <li>Row index</li>
8261 * <li>Column index</li>
8262 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8264 setRenderer : function(col, fn){
8265 this.config[col].renderer = fn;
8269 * Returns the width for the specified column.
8270 * @param {Number} col The column index
8273 getColumnWidth : function(col){
8274 return this.config[col].width * 1 || this.defaultWidth;
8278 * Sets the width for a column.
8279 * @param {Number} col The column index
8280 * @param {Number} width The new width
8282 setColumnWidth : function(col, width, suppressEvent){
8283 this.config[col].width = width;
8284 this.totalWidth = null;
8286 this.fireEvent("widthchange", this, col, width);
8291 * Returns the total width of all columns.
8292 * @param {Boolean} includeHidden True to include hidden column widths
8295 getTotalWidth : function(includeHidden){
8296 if(!this.totalWidth){
8297 this.totalWidth = 0;
8298 for(var i = 0, len = this.config.length; i < len; i++){
8299 if(includeHidden || !this.isHidden(i)){
8300 this.totalWidth += this.getColumnWidth(i);
8304 return this.totalWidth;
8308 * Returns the header for the specified column.
8309 * @param {Number} col The column index
8312 getColumnHeader : function(col){
8313 return this.config[col].header;
8317 * Sets the header for a column.
8318 * @param {Number} col The column index
8319 * @param {String} header The new header
8321 setColumnHeader : function(col, header){
8322 this.config[col].header = header;
8323 this.fireEvent("headerchange", this, col, header);
8327 * Returns the tooltip for the specified column.
8328 * @param {Number} col The column index
8331 getColumnTooltip : function(col){
8332 return this.config[col].tooltip;
8335 * Sets the tooltip for a column.
8336 * @param {Number} col The column index
8337 * @param {String} tooltip The new tooltip
8339 setColumnTooltip : function(col, tooltip){
8340 this.config[col].tooltip = tooltip;
8344 * Returns the dataIndex for the specified column.
8345 * @param {Number} col The column index
8348 getDataIndex : function(col){
8349 return this.config[col].dataIndex;
8353 * Sets the dataIndex for a column.
8354 * @param {Number} col The column index
8355 * @param {Number} dataIndex The new dataIndex
8357 setDataIndex : function(col, dataIndex){
8358 this.config[col].dataIndex = dataIndex;
8364 * Returns true if the cell is editable.
8365 * @param {Number} colIndex The column index
8366 * @param {Number} rowIndex The row index - this is nto actually used..?
8369 isCellEditable : function(colIndex, rowIndex){
8370 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8374 * Returns the editor defined for the cell/column.
8375 * return false or null to disable editing.
8376 * @param {Number} colIndex The column index
8377 * @param {Number} rowIndex The row index
8380 getCellEditor : function(colIndex, rowIndex){
8381 return this.config[colIndex].editor;
8385 * Sets if a column is editable.
8386 * @param {Number} col The column index
8387 * @param {Boolean} editable True if the column is editable
8389 setEditable : function(col, editable){
8390 this.config[col].editable = editable;
8395 * Returns true if the column is hidden.
8396 * @param {Number} colIndex The column index
8399 isHidden : function(colIndex){
8400 return this.config[colIndex].hidden;
8405 * Returns true if the column width cannot be changed
8407 isFixed : function(colIndex){
8408 return this.config[colIndex].fixed;
8412 * Returns true if the column can be resized
8415 isResizable : function(colIndex){
8416 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8419 * Sets if a column is hidden.
8420 * @param {Number} colIndex The column index
8421 * @param {Boolean} hidden True if the column is hidden
8423 setHidden : function(colIndex, hidden){
8424 this.config[colIndex].hidden = hidden;
8425 this.totalWidth = null;
8426 this.fireEvent("hiddenchange", this, colIndex, hidden);
8430 * Sets the editor for a column.
8431 * @param {Number} col The column index
8432 * @param {Object} editor The editor object
8434 setEditor : function(col, editor){
8435 this.config[col].editor = editor;
8438 * Add a column (experimental...) - defaults to adding to the end..
8439 * @param {Object} config
8441 addColumn : function(c)
8444 var i = this.config.length;
8447 if(typeof c.dataIndex == "undefined"){
8450 if(typeof c.renderer == "string"){
8451 c.renderer = Roo.util.Format[c.renderer];
8453 if(typeof c.id == "undefined"){
8456 if(c.editor && c.editor.xtype){
8457 c.editor = Roo.factory(c.editor, Roo.grid);
8459 if(c.editor && c.editor.isFormField){
8460 c.editor = new Roo.grid.GridEditor(c.editor);
8462 this.lookup[c.id] = c;
8467 Roo.grid.ColumnModel.defaultRenderer = function(value)
8469 if(typeof value == "object") {
8472 if(typeof value == "string" && value.length < 1){
8476 return String.format("{0}", value);
8479 // Alias for backwards compatibility
8480 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8483 * Ext JS Library 1.1.1
8484 * Copyright(c) 2006-2007, Ext JS, LLC.
8486 * Originally Released Under LGPL - original licence link has changed is not relivant.
8489 * <script type="text/javascript">
8493 * @class Roo.LoadMask
8494 * A simple utility class for generically masking elements while loading data. If the element being masked has
8495 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8496 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8497 * element's UpdateManager load indicator and will be destroyed after the initial load.
8499 * Create a new LoadMask
8500 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8501 * @param {Object} config The config object
8503 Roo.LoadMask = function(el, config){
8504 this.el = Roo.get(el);
8505 Roo.apply(this, config);
8507 this.store.on('beforeload', this.onBeforeLoad, this);
8508 this.store.on('load', this.onLoad, this);
8509 this.store.on('loadexception', this.onLoadException, this);
8510 this.removeMask = false;
8512 var um = this.el.getUpdateManager();
8513 um.showLoadIndicator = false; // disable the default indicator
8514 um.on('beforeupdate', this.onBeforeLoad, this);
8515 um.on('update', this.onLoad, this);
8516 um.on('failure', this.onLoad, this);
8517 this.removeMask = true;
8521 Roo.LoadMask.prototype = {
8523 * @cfg {Boolean} removeMask
8524 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8525 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8529 * The text to display in a centered loading message box (defaults to 'Loading...')
8533 * @cfg {String} msgCls
8534 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8536 msgCls : 'x-mask-loading',
8539 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8545 * Disables the mask to prevent it from being displayed
8547 disable : function(){
8548 this.disabled = true;
8552 * Enables the mask so that it can be displayed
8554 enable : function(){
8555 this.disabled = false;
8558 onLoadException : function()
8562 if (typeof(arguments[3]) != 'undefined') {
8563 Roo.MessageBox.alert("Error loading",arguments[3]);
8567 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8568 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8575 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8580 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8584 onBeforeLoad : function(){
8586 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8591 destroy : function(){
8593 this.store.un('beforeload', this.onBeforeLoad, this);
8594 this.store.un('load', this.onLoad, this);
8595 this.store.un('loadexception', this.onLoadException, this);
8597 var um = this.el.getUpdateManager();
8598 um.un('beforeupdate', this.onBeforeLoad, this);
8599 um.un('update', this.onLoad, this);
8600 um.un('failure', this.onLoad, this);
8604 * @class Roo.bootstrap.Table
8606 * @extends Roo.bootstrap.Component
8607 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8608 * Similar to Roo.grid.Grid
8610 var table = Roo.factory({
8612 xns : Roo.bootstrap,
8613 autoSizeColumns: true,
8620 sortInfo : { direction : 'ASC', field: 'name' },
8622 xtype : 'HttpProxy',
8625 url : 'https://example.com/some.data.url.json'
8628 xtype : 'JsonReader',
8630 fields : [ 'id', 'name', whatever' ],
8637 xtype : 'ColumnModel',
8641 dataIndex : 'is_in_group',
8644 renderer : function(v, x , r) {
8646 return String.format("{0}", v)
8652 xtype : 'RowSelectionModel',
8653 xns : Roo.bootstrap.Table
8654 // you can add listeners to catch selection change here....
8660 grid.render(Roo.get("some-div"));
8663 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8668 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8669 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8670 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8672 * @cfg {String} cls table class
8675 * @cfg {boolean} striped Should the rows be alternative striped
8676 * @cfg {boolean} bordered Add borders to the table
8677 * @cfg {boolean} hover Add hover highlighting
8678 * @cfg {boolean} condensed Format condensed
8679 * @cfg {boolean} responsive Format condensed
8680 * @cfg {Boolean} loadMask (true|false) default false
8681 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8682 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8683 * @cfg {Boolean} rowSelection (true|false) default false
8684 * @cfg {Boolean} cellSelection (true|false) default false
8685 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8686 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8687 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8688 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8689 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8690 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8693 * Create a new Table
8694 * @param {Object} config The config object
8697 Roo.bootstrap.Table = function(config)
8699 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8702 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8703 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8704 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8705 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8707 this.view = this; // compat with grid.
8709 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8711 this.sm.grid = this;
8712 this.selModel = Roo.factory(this.sm, Roo.grid);
8713 this.sm = this.selModel;
8714 this.sm.xmodule = this.xmodule || false;
8717 if (this.cm && typeof(this.cm.config) == 'undefined') {
8718 this.colModel = new Roo.grid.ColumnModel(this.cm);
8719 this.cm = this.colModel;
8720 this.cm.xmodule = this.xmodule || false;
8723 this.store= Roo.factory(this.store, Roo.data);
8724 this.ds = this.store;
8725 this.ds.xmodule = this.xmodule || false;
8728 if (this.footer && this.store) {
8729 this.footer.dataSource = this.ds;
8730 this.footer = Roo.factory(this.footer);
8737 * Fires when a cell is clicked
8738 * @param {Roo.bootstrap.Table} this
8739 * @param {Roo.Element} el
8740 * @param {Number} rowIndex
8741 * @param {Number} columnIndex
8742 * @param {Roo.EventObject} e
8746 * @event celldblclick
8747 * Fires when a cell is double clicked
8748 * @param {Roo.bootstrap.Table} this
8749 * @param {Roo.Element} el
8750 * @param {Number} rowIndex
8751 * @param {Number} columnIndex
8752 * @param {Roo.EventObject} e
8754 "celldblclick" : true,
8757 * Fires when a row is clicked
8758 * @param {Roo.bootstrap.Table} this
8759 * @param {Roo.Element} el
8760 * @param {Number} rowIndex
8761 * @param {Roo.EventObject} e
8765 * @event rowdblclick
8766 * Fires when a row is double clicked
8767 * @param {Roo.bootstrap.Table} this
8768 * @param {Roo.Element} el
8769 * @param {Number} rowIndex
8770 * @param {Roo.EventObject} e
8772 "rowdblclick" : true,
8775 * Fires when a mouseover occur
8776 * @param {Roo.bootstrap.Table} this
8777 * @param {Roo.Element} el
8778 * @param {Number} rowIndex
8779 * @param {Number} columnIndex
8780 * @param {Roo.EventObject} e
8785 * Fires when a mouseout occur
8786 * @param {Roo.bootstrap.Table} this
8787 * @param {Roo.Element} el
8788 * @param {Number} rowIndex
8789 * @param {Number} columnIndex
8790 * @param {Roo.EventObject} e
8795 * Fires when a row is rendered, so you can change add a style to it.
8796 * @param {Roo.bootstrap.Table} this
8797 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8801 * @event rowsrendered
8802 * Fires when all the rows have been rendered
8803 * @param {Roo.bootstrap.Table} this
8805 'rowsrendered' : true,
8807 * @event contextmenu
8808 * The raw contextmenu event for the entire grid.
8809 * @param {Roo.EventObject} e
8811 "contextmenu" : true,
8813 * @event rowcontextmenu
8814 * Fires when a row is right clicked
8815 * @param {Roo.bootstrap.Table} this
8816 * @param {Number} rowIndex
8817 * @param {Roo.EventObject} e
8819 "rowcontextmenu" : true,
8821 * @event cellcontextmenu
8822 * Fires when a cell is right clicked
8823 * @param {Roo.bootstrap.Table} this
8824 * @param {Number} rowIndex
8825 * @param {Number} cellIndex
8826 * @param {Roo.EventObject} e
8828 "cellcontextmenu" : true,
8830 * @event headercontextmenu
8831 * Fires when a header is right clicked
8832 * @param {Roo.bootstrap.Table} this
8833 * @param {Number} columnIndex
8834 * @param {Roo.EventObject} e
8836 "headercontextmenu" : true,
8839 * The raw mousedown event for the entire grid.
8840 * @param {Roo.EventObject} e
8847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8863 enableColumnResize: true,
8865 rowSelection : false,
8866 cellSelection : false,
8869 minColumnWidth : 50,
8871 // Roo.Element - the tbody
8872 bodyEl: false, // <tbody> Roo.Element - thead element
8873 headEl: false, // <thead> Roo.Element - thead element
8874 resizeProxy : false, // proxy element for dragging?
8878 container: false, // used by gridpanel...
8884 auto_hide_footer : false,
8886 view: false, // actually points to this..
8888 getAutoCreate : function()
8890 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8897 // this get's auto added by panel.Grid
8898 if (this.scrollBody) {
8899 cfg.cls += ' table-body-fixed';
8902 cfg.cls += ' table-striped';
8906 cfg.cls += ' table-hover';
8908 if (this.bordered) {
8909 cfg.cls += ' table-bordered';
8911 if (this.condensed) {
8912 cfg.cls += ' table-condensed';
8915 if (this.responsive) {
8916 cfg.cls += ' table-responsive';
8920 cfg.cls+= ' ' +this.cls;
8926 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8929 if(this.store || this.cm){
8930 if(this.headerShow){
8931 cfg.cn.push(this.renderHeader());
8934 cfg.cn.push(this.renderBody());
8936 if(this.footerShow){
8937 cfg.cn.push(this.renderFooter());
8939 // where does this come from?
8940 //cfg.cls+= ' TableGrid';
8943 return { cn : [ cfg ] };
8946 initEvents : function()
8948 if(!this.store || !this.cm){
8951 if (this.selModel) {
8952 this.selModel.initEvents();
8956 //Roo.log('initEvents with ds!!!!');
8958 this.bodyEl = this.el.select('tbody', true).first();
8959 this.headEl = this.el.select('thead', true).first();
8960 this.mainFoot = this.el.select('tfoot', true).first();
8965 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8966 e.on('click', this.sort, this);
8970 // why is this done????? = it breaks dialogs??
8971 //this.parent().el.setStyle('position', 'relative');
8975 this.footer.parentId = this.id;
8976 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8979 this.el.select('tfoot tr td').first().addClass('hide');
8984 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8987 this.store.on('load', this.onLoad, this);
8988 this.store.on('beforeload', this.onBeforeLoad, this);
8989 this.store.on('update', this.onUpdate, this);
8990 this.store.on('add', this.onAdd, this);
8991 this.store.on("clear", this.clear, this);
8993 this.el.on("contextmenu", this.onContextMenu, this);
8996 this.cm.on("headerchange", this.onHeaderChange, this);
8997 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8999 //?? does bodyEl get replaced on render?
9000 this.bodyEl.on("click", this.onClick, this);
9001 this.bodyEl.on("dblclick", this.onDblClick, this);
9002 this.bodyEl.on('scroll', this.onBodyScroll, this);
9004 // guessing mainbody will work - this relays usually caught by selmodel at present.
9005 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9008 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9011 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9012 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9017 // Compatibility with grid - we implement all the view features at present.
9018 getView : function()
9023 initCSS : function()
9027 var cm = this.cm, styles = [];
9028 this.CSS.removeStyleSheet(this.id + '-cssrules');
9030 // we can honour xs/sm/md/xl as widths...
9031 // we first have to decide what widht we are currently at...
9032 var sz = Roo.bootstrap.getGridSize();
9036 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9040 hidden = 'display:none;';
9042 // we can honour xs/sm/md/xl ?
9043 var w = cm.getColumnWidth(i, sz);
9046 var width = "width:" + (cm.getColumnWidth(i) || 100) + "px;";
9050 '#' , this.id , ' .x-col-' , i, " {\n", cm.config[i].css, width, "\n}\n",
9051 '#' , this.id , ' .x-hcol-' , i, " {\n", width, "}\n"
9054 Roo.log(styles.join(''));
9055 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9061 onContextMenu : function(e, t)
9063 this.processEvent("contextmenu", e);
9066 processEvent : function(name, e)
9068 if (name != 'touchstart' ) {
9069 this.fireEvent(name, e);
9072 var t = e.getTarget();
9074 var cell = Roo.get(t);
9080 if(cell.findParent('tfoot', false, true)){
9084 if(cell.findParent('thead', false, true)){
9086 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9087 cell = Roo.get(t).findParent('th', false, true);
9089 Roo.log("failed to find th in thead?");
9090 Roo.log(e.getTarget());
9095 var cellIndex = cell.dom.cellIndex;
9097 var ename = name == 'touchstart' ? 'click' : name;
9098 this.fireEvent("header" + ename, this, cellIndex, e);
9103 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9104 cell = Roo.get(t).findParent('td', false, true);
9106 Roo.log("failed to find th in tbody?");
9107 Roo.log(e.getTarget());
9112 var row = cell.findParent('tr', false, true);
9113 var cellIndex = cell.dom.cellIndex;
9114 var rowIndex = row.dom.rowIndex - 1;
9118 this.fireEvent("row" + name, this, rowIndex, e);
9122 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9128 onMouseover : function(e, el)
9130 var cell = Roo.get(el);
9136 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9137 cell = cell.findParent('td', false, true);
9140 var row = cell.findParent('tr', false, true);
9141 var cellIndex = cell.dom.cellIndex;
9142 var rowIndex = row.dom.rowIndex - 1; // start from 0
9144 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9148 onMouseout : function(e, el)
9150 var cell = Roo.get(el);
9156 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9157 cell = cell.findParent('td', false, true);
9160 var row = cell.findParent('tr', false, true);
9161 var cellIndex = cell.dom.cellIndex;
9162 var rowIndex = row.dom.rowIndex - 1; // start from 0
9164 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9168 onClick : function(e, el)
9170 var cell = Roo.get(el);
9172 if(!cell || (!this.cellSelection && !this.rowSelection)){
9176 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9177 cell = cell.findParent('td', false, true);
9180 if(!cell || typeof(cell) == 'undefined'){
9184 var row = cell.findParent('tr', false, true);
9186 if(!row || typeof(row) == 'undefined'){
9190 var cellIndex = cell.dom.cellIndex;
9191 var rowIndex = this.getRowIndex(row);
9193 // why??? - should these not be based on SelectionModel?
9194 //if(this.cellSelection){
9195 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9198 //if(this.rowSelection){
9199 this.fireEvent('rowclick', this, row, rowIndex, e);
9204 onDblClick : function(e,el)
9206 var cell = Roo.get(el);
9208 if(!cell || (!this.cellSelection && !this.rowSelection)){
9212 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9213 cell = cell.findParent('td', false, true);
9216 if(!cell || typeof(cell) == 'undefined'){
9220 var row = cell.findParent('tr', false, true);
9222 if(!row || typeof(row) == 'undefined'){
9226 var cellIndex = cell.dom.cellIndex;
9227 var rowIndex = this.getRowIndex(row);
9229 if(this.cellSelection){
9230 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9233 if(this.rowSelection){
9234 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9237 findRowIndex : function(el)
9239 var cell = Roo.get(el);
9243 var row = cell.findParent('tr', false, true);
9245 if(!row || typeof(row) == 'undefined'){
9248 return this.getRowIndex(row);
9250 sort : function(e,el)
9252 var col = Roo.get(el);
9254 if(!col.hasClass('sortable')){
9258 var sort = col.attr('sort');
9261 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9265 this.store.sortInfo = {field : sort, direction : dir};
9268 Roo.log("calling footer first");
9269 this.footer.onClick('first');
9272 this.store.load({ params : { start : 0 } });
9276 renderHeader : function()
9284 this.totalWidth = 0;
9286 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9288 var config = cm.config[i];
9292 cls : 'x-hcol-' + i,
9295 html: cm.getColumnHeader(i)
9298 var tooltip = cm.getColumnTooltip(i);
9300 c.tooltip = tooltip;
9306 if(typeof(config.sortable) != 'undefined' && config.sortable){
9307 c.cls += ' sortable';
9308 c.html = '<i class="fa"></i>' + c.html;
9311 // could use BS4 hidden-..-down
9313 if(typeof(config.lgHeader) != 'undefined'){
9314 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9317 if(typeof(config.mdHeader) != 'undefined'){
9318 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9321 if(typeof(config.smHeader) != 'undefined'){
9322 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9325 if(typeof(config.xsHeader) != 'undefined'){
9326 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9333 if(typeof(config.tooltip) != 'undefined'){
9334 c.tooltip = config.tooltip;
9337 if(typeof(config.colspan) != 'undefined'){
9338 c.colspan = config.colspan;
9341 // hidden is handled by CSS now
9343 if(typeof(config.dataIndex) != 'undefined'){
9344 c.sort = config.dataIndex;
9349 if(typeof(config.align) != 'undefined' && config.align.length){
9350 c.style += ' text-align:' + config.align + ';';
9353 if(typeof(config.width) != 'undefined'){
9354 c.style += ' width:' + config.width + 'px;';
9355 this.totalWidth += config.width;
9357 this.totalWidth += 100; // assume minimum of 100 per column?
9360 if(typeof(config.cls) != 'undefined'){
9361 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9363 // this is the bit that doesnt reall work at all...
9367 ['xs','sm','md','lg'].map(function(size){
9369 if(typeof(config[size]) == 'undefined'){
9373 if (!config[size]) { // 0 = hidden
9374 // BS 4 '0' is treated as hide that column and below.
9375 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9379 c.cls += ' col-' + size + '-' + config[size] + (
9380 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9388 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9399 renderBody : function()
9409 colspan : this.cm.getColumnCount()
9419 renderFooter : function()
9429 colspan : this.cm.getColumnCount()
9443 // Roo.log('ds onload');
9448 var ds = this.store;
9450 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9451 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9452 if (_this.store.sortInfo) {
9454 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9455 e.select('i', true).addClass(['fa-arrow-up']);
9458 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9459 e.select('i', true).addClass(['fa-arrow-down']);
9464 var tbody = this.bodyEl;
9466 if(ds.getCount() > 0){
9467 ds.data.each(function(d,rowIndex){
9468 var row = this.renderRow(cm, ds, rowIndex);
9470 tbody.createChild(row);
9474 if(row.cellObjects.length){
9475 Roo.each(row.cellObjects, function(r){
9476 _this.renderCellObject(r);
9483 var tfoot = this.el.select('tfoot', true).first();
9485 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9487 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9489 var total = this.ds.getTotalCount();
9491 if(this.footer.pageSize < total){
9492 this.mainFoot.show();
9496 Roo.each(this.el.select('tbody td', true).elements, function(e){
9497 e.on('mouseover', _this.onMouseover, _this);
9500 Roo.each(this.el.select('tbody td', true).elements, function(e){
9501 e.on('mouseout', _this.onMouseout, _this);
9503 this.fireEvent('rowsrendered', this);
9509 onUpdate : function(ds,record)
9511 this.refreshRow(record);
9515 onRemove : function(ds, record, index, isUpdate){
9516 if(isUpdate !== true){
9517 this.fireEvent("beforerowremoved", this, index, record);
9519 var bt = this.bodyEl.dom;
9521 var rows = this.el.select('tbody > tr', true).elements;
9523 if(typeof(rows[index]) != 'undefined'){
9524 bt.removeChild(rows[index].dom);
9527 // if(bt.rows[index]){
9528 // bt.removeChild(bt.rows[index]);
9531 if(isUpdate !== true){
9532 //this.stripeRows(index);
9533 //this.syncRowHeights(index, index);
9535 this.fireEvent("rowremoved", this, index, record);
9539 onAdd : function(ds, records, rowIndex)
9541 //Roo.log('on Add called');
9542 // - note this does not handle multiple adding very well..
9543 var bt = this.bodyEl.dom;
9544 for (var i =0 ; i < records.length;i++) {
9545 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9546 //Roo.log(records[i]);
9547 //Roo.log(this.store.getAt(rowIndex+i));
9548 this.insertRow(this.store, rowIndex + i, false);
9555 refreshRow : function(record){
9556 var ds = this.store, index;
9557 if(typeof record == 'number'){
9559 record = ds.getAt(index);
9561 index = ds.indexOf(record);
9563 return; // should not happen - but seems to
9566 this.insertRow(ds, index, true);
9568 this.onRemove(ds, record, index+1, true);
9570 //this.syncRowHeights(index, index);
9572 this.fireEvent("rowupdated", this, index, record);
9575 onRowSelect : function(rowIndex){
9576 var row = this.getRowDom(rowIndex);
9577 row.addClass(['bg-info','info']);
9580 onRowDeselect : function(rowIndex){
9581 var row = this.getRowDom(rowIndex);
9582 row.removeClass(['bg-info','info']);
9585 * Focuses the specified row.
9586 * @param {Number} row The row index
9588 focusRow : function(row)
9590 //Roo.log('GridView.focusRow');
9591 var x = this.bodyEl.dom.scrollLeft;
9592 this.focusCell(row, 0, false);
9593 this.bodyEl.dom.scrollLeft = x;
9597 * Focuses the specified cell.
9598 * @param {Number} row The row index
9599 * @param {Number} col The column index
9600 * @param {Boolean} hscroll false to disable horizontal scrolling
9602 focusCell : function(row, col, hscroll)
9604 //Roo.log('GridView.focusCell');
9605 var el = this.ensureVisible(row, col, hscroll);
9606 // not sure what focusEL achives = it's a <a> pos relative
9607 //this.focusEl.alignTo(el, "tl-tl");
9609 // this.focusEl.focus();
9611 // this.focusEl.focus.defer(1, this.focusEl);
9616 * Scrolls the specified cell into view
9617 * @param {Number} row The row index
9618 * @param {Number} col The column index
9619 * @param {Boolean} hscroll false to disable horizontal scrolling
9621 ensureVisible : function(row, col, hscroll)
9623 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9624 //return null; //disable for testing.
9625 if(typeof row != "number"){
9628 if(row < 0 && row >= this.ds.getCount()){
9631 col = (col !== undefined ? col : 0);
9633 while(cm.isHidden(col)){
9637 var el = this.getCellDom(row, col);
9641 var c = this.bodyEl.dom;
9643 var ctop = parseInt(el.offsetTop, 10);
9644 var cleft = parseInt(el.offsetLeft, 10);
9645 var cbot = ctop + el.offsetHeight;
9646 var cright = cleft + el.offsetWidth;
9648 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9649 var ch = 0; //?? header is not withing the area?
9650 var stop = parseInt(c.scrollTop, 10);
9651 var sleft = parseInt(c.scrollLeft, 10);
9652 var sbot = stop + ch;
9653 var sright = sleft + c.clientWidth;
9655 Roo.log('GridView.ensureVisible:' +
9657 ' c.clientHeight:' + c.clientHeight +
9658 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9667 //Roo.log("set scrolltop to ctop DISABLE?");
9668 }else if(cbot > sbot){
9669 //Roo.log("set scrolltop to cbot-ch");
9670 c.scrollTop = cbot-ch;
9673 if(hscroll !== false){
9675 c.scrollLeft = cleft;
9676 }else if(cright > sright){
9677 c.scrollLeft = cright-c.clientWidth;
9685 insertRow : function(dm, rowIndex, isUpdate){
9688 this.fireEvent("beforerowsinserted", this, rowIndex);
9690 //var s = this.getScrollState();
9691 var row = this.renderRow(this.cm, this.store, rowIndex);
9692 // insert before rowIndex..
9693 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9697 if(row.cellObjects.length){
9698 Roo.each(row.cellObjects, function(r){
9699 _this.renderCellObject(r);
9704 this.fireEvent("rowsinserted", this, rowIndex);
9705 //this.syncRowHeights(firstRow, lastRow);
9706 //this.stripeRows(firstRow);
9713 getRowDom : function(rowIndex)
9715 var rows = this.el.select('tbody > tr', true).elements;
9717 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9720 getCellDom : function(rowIndex, colIndex)
9722 var row = this.getRowDom(rowIndex);
9723 if (row === false) {
9726 var cols = row.select('td', true).elements;
9727 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9731 // returns the object tree for a tr..
9734 renderRow : function(cm, ds, rowIndex)
9736 var d = ds.getAt(rowIndex);
9740 cls : 'x-row-' + rowIndex,
9744 var cellObjects = [];
9746 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9747 var config = cm.config[i];
9749 var renderer = cm.getRenderer(i);
9753 if(typeof(renderer) !== 'undefined'){
9754 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9756 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9757 // and are rendered into the cells after the row is rendered - using the id for the element.
9759 if(typeof(value) === 'object'){
9769 rowIndex : rowIndex,
9774 this.fireEvent('rowclass', this, rowcfg);
9778 // this might end up displaying HTML?
9779 // this is too messy... - better to only do it on columsn you know are going to be too long
9780 //tooltip : (typeof(value) === 'object') ? '' : value,
9781 cls : rowcfg.rowClass + ' x-col-' + i,
9783 html: (typeof(value) === 'object') ? '' : value
9790 if(typeof(config.colspan) != 'undefined'){
9791 td.colspan = config.colspan;
9796 if(typeof(config.align) != 'undefined' && config.align.length){
9797 td.style += ' text-align:' + config.align + ';';
9799 if(typeof(config.valign) != 'undefined' && config.valign.length){
9800 td.style += ' vertical-align:' + config.valign + ';';
9803 if(typeof(config.width) != 'undefined'){
9804 td.style += ' width:' + config.width + 'px;';
9807 if(typeof(config.cursor) != 'undefined'){
9808 td.style += ' cursor:' + config.cursor + ';';
9811 if(typeof(config.cls) != 'undefined'){
9812 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9815 ['xs','sm','md','lg'].map(function(size){
9817 if(typeof(config[size]) == 'undefined'){
9823 if (!config[size]) { // 0 = hidden
9824 // BS 4 '0' is treated as hide that column and below.
9825 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9829 td.cls += ' col-' + size + '-' + config[size] + (
9830 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9840 row.cellObjects = cellObjects;
9848 onBeforeLoad : function()
9857 this.el.select('tbody', true).first().dom.innerHTML = '';
9860 * Show or hide a row.
9861 * @param {Number} rowIndex to show or hide
9862 * @param {Boolean} state hide
9864 setRowVisibility : function(rowIndex, state)
9866 var bt = this.bodyEl.dom;
9868 var rows = this.el.select('tbody > tr', true).elements;
9870 if(typeof(rows[rowIndex]) == 'undefined'){
9873 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9878 getSelectionModel : function(){
9880 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9882 return this.selModel;
9885 * Render the Roo.bootstrap object from renderder
9887 renderCellObject : function(r)
9891 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9893 var t = r.cfg.render(r.container);
9896 Roo.each(r.cfg.cn, function(c){
9898 container: t.getChildContainer(),
9901 _this.renderCellObject(child);
9906 * get the Row Index from a dom element.
9907 * @param {Roo.Element} row The row to look for
9908 * @returns {Number} the row
9910 getRowIndex : function(row)
9914 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9925 * get the header TH element for columnIndex
9926 * @param {Number} columnIndex
9927 * @returns {Roo.Element}
9929 getHeaderIndex: function(colIndex)
9931 var cols = this.headEl.select('th', true).elements;
9932 return cols[colIndex];
9935 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
9936 * @param {domElement} cell to look for
9937 * @returns {Number} the column
9939 getCellIndex : function(cell)
9941 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
9943 return parseInt(id[1], 10);
9948 * Returns the grid's underlying element = used by panel.Grid
9949 * @return {Element} The element
9951 getGridEl : function(){
9955 * Forces a resize - used by panel.Grid
9956 * @return {Element} The element
9958 autoSize : function()
9960 //var ctr = Roo.get(this.container.dom.parentElement);
9961 var ctr = Roo.get(this.el.dom);
9963 var thd = this.getGridEl().select('thead',true).first();
9964 var tbd = this.getGridEl().select('tbody', true).first();
9965 var tfd = this.getGridEl().select('tfoot', true).first();
9967 var cw = ctr.getWidth();
9968 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9972 tbd.setWidth(ctr.getWidth());
9973 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9974 // this needs fixing for various usage - currently only hydra job advers I think..
9976 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9978 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9981 cw = Math.max(cw, this.totalWidth);
9982 this.getGridEl().select('tbody tr',true).setWidth(cw);
9984 // resize 'expandable coloumn?
9986 return; // we doe not have a view in this design..
9989 onBodyScroll: function()
9991 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9993 this.headEl.setStyle({
9994 'position' : 'relative',
9995 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10001 var scrollHeight = this.bodyEl.dom.scrollHeight;
10003 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10005 var height = this.bodyEl.getHeight();
10007 if(scrollHeight - height == scrollTop) {
10009 var total = this.ds.getTotalCount();
10011 if(this.footer.cursor + this.footer.pageSize < total){
10013 this.footer.ds.load({
10015 start : this.footer.cursor + this.footer.pageSize,
10016 limit : this.footer.pageSize
10025 onColumnSplitterMoved : function(i, w)
10027 this.userResized = true;
10029 var cm = this.colModel;
10031 cm.setColumnWidth(i, w, true);
10032 //var cid = cm.getColumnId(i); << not used in this version?
10033 Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10035 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10036 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10038 //this.updateSplitters();
10039 //this.layout(); << ??
10040 this.fireEvent("columnresize", i, w);
10042 onHeaderChange : function()
10044 var header = this.renderHeader();
10045 var table = this.el.select('table', true).first();
10047 this.headEl.remove();
10048 this.headEl = table.createChild(header, this.bodyEl, false);
10050 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10051 e.on('click', this.sort, this);
10054 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10055 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10060 onHiddenChange : function(colModel, colIndex, hidden)
10062 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10063 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10065 this.CSS.updateRule(thSelector, "display", "");
10066 this.CSS.updateRule(tdSelector, "display", "");
10069 this.CSS.updateRule(thSelector, "display", "none");
10070 this.CSS.updateRule(tdSelector, "display", "none");
10073 this.onHeaderChange();
10077 setColumnWidth: function(col_index, width)
10079 // width = "md-2 xs-2..."
10080 if(!this.colModel.config[col_index]) {
10084 var w = width.split(" ");
10086 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10088 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10091 for(var j = 0; j < w.length; j++) {
10097 var size_cls = w[j].split("-");
10099 if(!Number.isInteger(size_cls[1] * 1)) {
10103 if(!this.colModel.config[col_index][size_cls[0]]) {
10107 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10111 h_row[0].classList.replace(
10112 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10113 "col-"+size_cls[0]+"-"+size_cls[1]
10116 for(var i = 0; i < rows.length; i++) {
10118 var size_cls = w[j].split("-");
10120 if(!Number.isInteger(size_cls[1] * 1)) {
10124 if(!this.colModel.config[col_index][size_cls[0]]) {
10128 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10132 rows[i].classList.replace(
10133 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10134 "col-"+size_cls[0]+"-"+size_cls[1]
10138 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10143 // currently only used to find the split on drag..
10144 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10149 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10150 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10159 * @class Roo.bootstrap.TableCell
10160 * @extends Roo.bootstrap.Component
10161 * Bootstrap TableCell class
10162 * @cfg {String} html cell contain text
10163 * @cfg {String} cls cell class
10164 * @cfg {String} tag cell tag (td|th) default td
10165 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10166 * @cfg {String} align Aligns the content in a cell
10167 * @cfg {String} axis Categorizes cells
10168 * @cfg {String} bgcolor Specifies the background color of a cell
10169 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10170 * @cfg {Number} colspan Specifies the number of columns a cell should span
10171 * @cfg {String} headers Specifies one or more header cells a cell is related to
10172 * @cfg {Number} height Sets the height of a cell
10173 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10174 * @cfg {Number} rowspan Sets the number of rows a cell should span
10175 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10176 * @cfg {String} valign Vertical aligns the content in a cell
10177 * @cfg {Number} width Specifies the width of a cell
10180 * Create a new TableCell
10181 * @param {Object} config The config object
10184 Roo.bootstrap.TableCell = function(config){
10185 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10188 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10208 getAutoCreate : function(){
10209 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10216 cfg.tag = this.tag;
10229 cfg.align=this.align
10234 if (this.bgcolor) {
10235 cfg.bgcolor=this.bgcolor
10237 if (this.charoff) {
10238 cfg.charoff=this.charoff
10240 if (this.colspan) {
10241 cfg.colspan=this.colspan
10243 if (this.headers) {
10244 cfg.headers=this.headers
10247 cfg.height=this.height
10250 cfg.nowrap=this.nowrap
10252 if (this.rowspan) {
10253 cfg.rowspan=this.rowspan
10256 cfg.scope=this.scope
10259 cfg.valign=this.valign
10262 cfg.width=this.width
10281 * @class Roo.bootstrap.TableRow
10282 * @extends Roo.bootstrap.Component
10283 * Bootstrap TableRow class
10284 * @cfg {String} cls row class
10285 * @cfg {String} align Aligns the content in a table row
10286 * @cfg {String} bgcolor Specifies a background color for a table row
10287 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10288 * @cfg {String} valign Vertical aligns the content in a table row
10291 * Create a new TableRow
10292 * @param {Object} config The config object
10295 Roo.bootstrap.TableRow = function(config){
10296 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10299 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10307 getAutoCreate : function(){
10308 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10315 cfg.cls = this.cls;
10318 cfg.align = this.align;
10321 cfg.bgcolor = this.bgcolor;
10324 cfg.charoff = this.charoff;
10327 cfg.valign = this.valign;
10345 * @class Roo.bootstrap.TableBody
10346 * @extends Roo.bootstrap.Component
10347 * Bootstrap TableBody class
10348 * @cfg {String} cls element class
10349 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10350 * @cfg {String} align Aligns the content inside the element
10351 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10352 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10355 * Create a new TableBody
10356 * @param {Object} config The config object
10359 Roo.bootstrap.TableBody = function(config){
10360 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10363 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10371 getAutoCreate : function(){
10372 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10382 cfg.tag = this.tag;
10386 cfg.align = this.align;
10389 cfg.charoff = this.charoff;
10392 cfg.valign = this.valign;
10399 // initEvents : function()
10402 // if(!this.store){
10406 // this.store = Roo.factory(this.store, Roo.data);
10407 // this.store.on('load', this.onLoad, this);
10409 // this.store.load();
10413 // onLoad: function ()
10415 // this.fireEvent('load', this);
10425 * Ext JS Library 1.1.1
10426 * Copyright(c) 2006-2007, Ext JS, LLC.
10428 * Originally Released Under LGPL - original licence link has changed is not relivant.
10431 * <script type="text/javascript">
10434 // as we use this in bootstrap.
10435 Roo.namespace('Roo.form');
10437 * @class Roo.form.Action
10438 * Internal Class used to handle form actions
10440 * @param {Roo.form.BasicForm} el The form element or its id
10441 * @param {Object} config Configuration options
10446 // define the action interface
10447 Roo.form.Action = function(form, options){
10449 this.options = options || {};
10452 * Client Validation Failed
10455 Roo.form.Action.CLIENT_INVALID = 'client';
10457 * Server Validation Failed
10460 Roo.form.Action.SERVER_INVALID = 'server';
10462 * Connect to Server Failed
10465 Roo.form.Action.CONNECT_FAILURE = 'connect';
10467 * Reading Data from Server Failed
10470 Roo.form.Action.LOAD_FAILURE = 'load';
10472 Roo.form.Action.prototype = {
10474 failureType : undefined,
10475 response : undefined,
10476 result : undefined,
10478 // interface method
10479 run : function(options){
10483 // interface method
10484 success : function(response){
10488 // interface method
10489 handleResponse : function(response){
10493 // default connection failure
10494 failure : function(response){
10496 this.response = response;
10497 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10498 this.form.afterAction(this, false);
10501 processResponse : function(response){
10502 this.response = response;
10503 if(!response.responseText){
10506 this.result = this.handleResponse(response);
10507 return this.result;
10510 // utility functions used internally
10511 getUrl : function(appendParams){
10512 var url = this.options.url || this.form.url || this.form.el.dom.action;
10514 var p = this.getParams();
10516 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10522 getMethod : function(){
10523 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10526 getParams : function(){
10527 var bp = this.form.baseParams;
10528 var p = this.options.params;
10530 if(typeof p == "object"){
10531 p = Roo.urlEncode(Roo.applyIf(p, bp));
10532 }else if(typeof p == 'string' && bp){
10533 p += '&' + Roo.urlEncode(bp);
10536 p = Roo.urlEncode(bp);
10541 createCallback : function(){
10543 success: this.success,
10544 failure: this.failure,
10546 timeout: (this.form.timeout*1000),
10547 upload: this.form.fileUpload ? this.success : undefined
10552 Roo.form.Action.Submit = function(form, options){
10553 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10556 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10559 haveProgress : false,
10560 uploadComplete : false,
10562 // uploadProgress indicator.
10563 uploadProgress : function()
10565 if (!this.form.progressUrl) {
10569 if (!this.haveProgress) {
10570 Roo.MessageBox.progress("Uploading", "Uploading");
10572 if (this.uploadComplete) {
10573 Roo.MessageBox.hide();
10577 this.haveProgress = true;
10579 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10581 var c = new Roo.data.Connection();
10583 url : this.form.progressUrl,
10588 success : function(req){
10589 //console.log(data);
10593 rdata = Roo.decode(req.responseText)
10595 Roo.log("Invalid data from server..");
10599 if (!rdata || !rdata.success) {
10601 Roo.MessageBox.alert(Roo.encode(rdata));
10604 var data = rdata.data;
10606 if (this.uploadComplete) {
10607 Roo.MessageBox.hide();
10612 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10613 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10616 this.uploadProgress.defer(2000,this);
10619 failure: function(data) {
10620 Roo.log('progress url failed ');
10631 // run get Values on the form, so it syncs any secondary forms.
10632 this.form.getValues();
10634 var o = this.options;
10635 var method = this.getMethod();
10636 var isPost = method == 'POST';
10637 if(o.clientValidation === false || this.form.isValid()){
10639 if (this.form.progressUrl) {
10640 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10641 (new Date() * 1) + '' + Math.random());
10646 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10647 form:this.form.el.dom,
10648 url:this.getUrl(!isPost),
10650 params:isPost ? this.getParams() : null,
10651 isUpload: this.form.fileUpload,
10652 formData : this.form.formData
10655 this.uploadProgress();
10657 }else if (o.clientValidation !== false){ // client validation failed
10658 this.failureType = Roo.form.Action.CLIENT_INVALID;
10659 this.form.afterAction(this, false);
10663 success : function(response)
10665 this.uploadComplete= true;
10666 if (this.haveProgress) {
10667 Roo.MessageBox.hide();
10671 var result = this.processResponse(response);
10672 if(result === true || result.success){
10673 this.form.afterAction(this, true);
10677 this.form.markInvalid(result.errors);
10678 this.failureType = Roo.form.Action.SERVER_INVALID;
10680 this.form.afterAction(this, false);
10682 failure : function(response)
10684 this.uploadComplete= true;
10685 if (this.haveProgress) {
10686 Roo.MessageBox.hide();
10689 this.response = response;
10690 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10691 this.form.afterAction(this, false);
10694 handleResponse : function(response){
10695 if(this.form.errorReader){
10696 var rs = this.form.errorReader.read(response);
10699 for(var i = 0, len = rs.records.length; i < len; i++) {
10700 var r = rs.records[i];
10701 errors[i] = r.data;
10704 if(errors.length < 1){
10708 success : rs.success,
10714 ret = Roo.decode(response.responseText);
10718 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10728 Roo.form.Action.Load = function(form, options){
10729 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10730 this.reader = this.form.reader;
10733 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10738 Roo.Ajax.request(Roo.apply(
10739 this.createCallback(), {
10740 method:this.getMethod(),
10741 url:this.getUrl(false),
10742 params:this.getParams()
10746 success : function(response){
10748 var result = this.processResponse(response);
10749 if(result === true || !result.success || !result.data){
10750 this.failureType = Roo.form.Action.LOAD_FAILURE;
10751 this.form.afterAction(this, false);
10754 this.form.clearInvalid();
10755 this.form.setValues(result.data);
10756 this.form.afterAction(this, true);
10759 handleResponse : function(response){
10760 if(this.form.reader){
10761 var rs = this.form.reader.read(response);
10762 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10764 success : rs.success,
10768 return Roo.decode(response.responseText);
10772 Roo.form.Action.ACTION_TYPES = {
10773 'load' : Roo.form.Action.Load,
10774 'submit' : Roo.form.Action.Submit
10783 * @class Roo.bootstrap.Form
10784 * @extends Roo.bootstrap.Component
10785 * Bootstrap Form class
10786 * @cfg {String} method GET | POST (default POST)
10787 * @cfg {String} labelAlign top | left (default top)
10788 * @cfg {String} align left | right - for navbars
10789 * @cfg {Boolean} loadMask load mask when submit (default true)
10793 * Create a new Form
10794 * @param {Object} config The config object
10798 Roo.bootstrap.Form = function(config){
10800 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10802 Roo.bootstrap.Form.popover.apply();
10806 * @event clientvalidation
10807 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10808 * @param {Form} this
10809 * @param {Boolean} valid true if the form has passed client-side validation
10811 clientvalidation: true,
10813 * @event beforeaction
10814 * Fires before any action is performed. Return false to cancel the action.
10815 * @param {Form} this
10816 * @param {Action} action The action to be performed
10818 beforeaction: true,
10820 * @event actionfailed
10821 * Fires when an action fails.
10822 * @param {Form} this
10823 * @param {Action} action The action that failed
10825 actionfailed : true,
10827 * @event actioncomplete
10828 * Fires when an action is completed.
10829 * @param {Form} this
10830 * @param {Action} action The action that completed
10832 actioncomplete : true
10836 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10839 * @cfg {String} method
10840 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10844 * @cfg {String} url
10845 * The URL to use for form actions if one isn't supplied in the action options.
10848 * @cfg {Boolean} fileUpload
10849 * Set to true if this form is a file upload.
10853 * @cfg {Object} baseParams
10854 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10858 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10862 * @cfg {Sting} align (left|right) for navbar forms
10867 activeAction : null,
10870 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10871 * element by passing it or its id or mask the form itself by passing in true.
10874 waitMsgTarget : false,
10879 * @cfg {Boolean} errorMask (true|false) default false
10884 * @cfg {Number} maskOffset Default 100
10889 * @cfg {Boolean} maskBody
10893 getAutoCreate : function(){
10897 method : this.method || 'POST',
10898 id : this.id || Roo.id(),
10901 if (this.parent().xtype.match(/^Nav/)) {
10902 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10906 if (this.labelAlign == 'left' ) {
10907 cfg.cls += ' form-horizontal';
10913 initEvents : function()
10915 this.el.on('submit', this.onSubmit, this);
10916 // this was added as random key presses on the form where triggering form submit.
10917 this.el.on('keypress', function(e) {
10918 if (e.getCharCode() != 13) {
10921 // we might need to allow it for textareas.. and some other items.
10922 // check e.getTarget().
10924 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10928 Roo.log("keypress blocked");
10930 e.preventDefault();
10936 onSubmit : function(e){
10941 * Returns true if client-side validation on the form is successful.
10944 isValid : function(){
10945 var items = this.getItems();
10947 var target = false;
10949 items.each(function(f){
10955 Roo.log('invalid field: ' + f.name);
10959 if(!target && f.el.isVisible(true)){
10965 if(this.errorMask && !valid){
10966 Roo.bootstrap.Form.popover.mask(this, target);
10973 * Returns true if any fields in this form have changed since their original load.
10976 isDirty : function(){
10978 var items = this.getItems();
10979 items.each(function(f){
10989 * Performs a predefined action (submit or load) or custom actions you define on this form.
10990 * @param {String} actionName The name of the action type
10991 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10992 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10993 * accept other config options):
10995 Property Type Description
10996 ---------------- --------------- ----------------------------------------------------------------------------------
10997 url String The url for the action (defaults to the form's url)
10998 method String The form method to use (defaults to the form's method, or POST if not defined)
10999 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11000 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11001 validate the form on the client (defaults to false)
11003 * @return {BasicForm} this
11005 doAction : function(action, options){
11006 if(typeof action == 'string'){
11007 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11009 if(this.fireEvent('beforeaction', this, action) !== false){
11010 this.beforeAction(action);
11011 action.run.defer(100, action);
11017 beforeAction : function(action){
11018 var o = action.options;
11023 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11025 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11028 // not really supported yet.. ??
11030 //if(this.waitMsgTarget === true){
11031 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11032 //}else if(this.waitMsgTarget){
11033 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11034 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11036 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11042 afterAction : function(action, success){
11043 this.activeAction = null;
11044 var o = action.options;
11049 Roo.get(document.body).unmask();
11055 //if(this.waitMsgTarget === true){
11056 // this.el.unmask();
11057 //}else if(this.waitMsgTarget){
11058 // this.waitMsgTarget.unmask();
11060 // Roo.MessageBox.updateProgress(1);
11061 // Roo.MessageBox.hide();
11068 Roo.callback(o.success, o.scope, [this, action]);
11069 this.fireEvent('actioncomplete', this, action);
11073 // failure condition..
11074 // we have a scenario where updates need confirming.
11075 // eg. if a locking scenario exists..
11076 // we look for { errors : { needs_confirm : true }} in the response.
11078 (typeof(action.result) != 'undefined') &&
11079 (typeof(action.result.errors) != 'undefined') &&
11080 (typeof(action.result.errors.needs_confirm) != 'undefined')
11083 Roo.log("not supported yet");
11086 Roo.MessageBox.confirm(
11087 "Change requires confirmation",
11088 action.result.errorMsg,
11093 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11103 Roo.callback(o.failure, o.scope, [this, action]);
11104 // show an error message if no failed handler is set..
11105 if (!this.hasListener('actionfailed')) {
11106 Roo.log("need to add dialog support");
11108 Roo.MessageBox.alert("Error",
11109 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11110 action.result.errorMsg :
11111 "Saving Failed, please check your entries or try again"
11116 this.fireEvent('actionfailed', this, action);
11121 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11122 * @param {String} id The value to search for
11125 findField : function(id){
11126 var items = this.getItems();
11127 var field = items.get(id);
11129 items.each(function(f){
11130 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11137 return field || null;
11140 * Mark fields in this form invalid in bulk.
11141 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11142 * @return {BasicForm} this
11144 markInvalid : function(errors){
11145 if(errors instanceof Array){
11146 for(var i = 0, len = errors.length; i < len; i++){
11147 var fieldError = errors[i];
11148 var f = this.findField(fieldError.id);
11150 f.markInvalid(fieldError.msg);
11156 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11157 field.markInvalid(errors[id]);
11161 //Roo.each(this.childForms || [], function (f) {
11162 // f.markInvalid(errors);
11169 * Set values for fields in this form in bulk.
11170 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11171 * @return {BasicForm} this
11173 setValues : function(values){
11174 if(values instanceof Array){ // array of objects
11175 for(var i = 0, len = values.length; i < len; i++){
11177 var f = this.findField(v.id);
11179 f.setValue(v.value);
11180 if(this.trackResetOnLoad){
11181 f.originalValue = f.getValue();
11185 }else{ // object hash
11188 if(typeof values[id] != 'function' && (field = this.findField(id))){
11190 if (field.setFromData &&
11191 field.valueField &&
11192 field.displayField &&
11193 // combos' with local stores can
11194 // be queried via setValue()
11195 // to set their value..
11196 (field.store && !field.store.isLocal)
11200 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11201 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11202 field.setFromData(sd);
11204 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11206 field.setFromData(values);
11209 field.setValue(values[id]);
11213 if(this.trackResetOnLoad){
11214 field.originalValue = field.getValue();
11220 //Roo.each(this.childForms || [], function (f) {
11221 // f.setValues(values);
11228 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11229 * they are returned as an array.
11230 * @param {Boolean} asString
11233 getValues : function(asString){
11234 //if (this.childForms) {
11235 // copy values from the child forms
11236 // Roo.each(this.childForms, function (f) {
11237 // this.setValues(f.getValues());
11243 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11244 if(asString === true){
11247 return Roo.urlDecode(fs);
11251 * Returns the fields in this form as an object with key/value pairs.
11252 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11255 getFieldValues : function(with_hidden)
11257 var items = this.getItems();
11259 items.each(function(f){
11261 if (!f.getName()) {
11265 var v = f.getValue();
11267 if (f.inputType =='radio') {
11268 if (typeof(ret[f.getName()]) == 'undefined') {
11269 ret[f.getName()] = ''; // empty..
11272 if (!f.el.dom.checked) {
11276 v = f.el.dom.value;
11280 if(f.xtype == 'MoneyField'){
11281 ret[f.currencyName] = f.getCurrency();
11284 // not sure if this supported any more..
11285 if ((typeof(v) == 'object') && f.getRawValue) {
11286 v = f.getRawValue() ; // dates..
11288 // combo boxes where name != hiddenName...
11289 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11290 ret[f.name] = f.getRawValue();
11292 ret[f.getName()] = v;
11299 * Clears all invalid messages in this form.
11300 * @return {BasicForm} this
11302 clearInvalid : function(){
11303 var items = this.getItems();
11305 items.each(function(f){
11313 * Resets this form.
11314 * @return {BasicForm} this
11316 reset : function(){
11317 var items = this.getItems();
11318 items.each(function(f){
11322 Roo.each(this.childForms || [], function (f) {
11330 getItems : function()
11332 var r=new Roo.util.MixedCollection(false, function(o){
11333 return o.id || (o.id = Roo.id());
11335 var iter = function(el) {
11342 Roo.each(el.items,function(e) {
11351 hideFields : function(items)
11353 Roo.each(items, function(i){
11355 var f = this.findField(i);
11366 showFields : function(items)
11368 Roo.each(items, function(i){
11370 var f = this.findField(i);
11383 Roo.apply(Roo.bootstrap.Form, {
11399 intervalID : false,
11405 if(this.isApplied){
11410 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11411 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11412 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11413 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11416 this.maskEl.top.enableDisplayMode("block");
11417 this.maskEl.left.enableDisplayMode("block");
11418 this.maskEl.bottom.enableDisplayMode("block");
11419 this.maskEl.right.enableDisplayMode("block");
11421 this.toolTip = new Roo.bootstrap.Tooltip({
11422 cls : 'roo-form-error-popover',
11424 'left' : ['r-l', [-2,0], 'right'],
11425 'right' : ['l-r', [2,0], 'left'],
11426 'bottom' : ['tl-bl', [0,2], 'top'],
11427 'top' : [ 'bl-tl', [0,-2], 'bottom']
11431 this.toolTip.render(Roo.get(document.body));
11433 this.toolTip.el.enableDisplayMode("block");
11435 Roo.get(document.body).on('click', function(){
11439 Roo.get(document.body).on('touchstart', function(){
11443 this.isApplied = true
11446 mask : function(form, target)
11450 this.target = target;
11452 if(!this.form.errorMask || !target.el){
11456 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11458 Roo.log(scrollable);
11460 var ot = this.target.el.calcOffsetsTo(scrollable);
11462 var scrollTo = ot[1] - this.form.maskOffset;
11464 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11466 scrollable.scrollTo('top', scrollTo);
11468 var box = this.target.el.getBox();
11470 var zIndex = Roo.bootstrap.Modal.zIndex++;
11473 this.maskEl.top.setStyle('position', 'absolute');
11474 this.maskEl.top.setStyle('z-index', zIndex);
11475 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11476 this.maskEl.top.setLeft(0);
11477 this.maskEl.top.setTop(0);
11478 this.maskEl.top.show();
11480 this.maskEl.left.setStyle('position', 'absolute');
11481 this.maskEl.left.setStyle('z-index', zIndex);
11482 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11483 this.maskEl.left.setLeft(0);
11484 this.maskEl.left.setTop(box.y - this.padding);
11485 this.maskEl.left.show();
11487 this.maskEl.bottom.setStyle('position', 'absolute');
11488 this.maskEl.bottom.setStyle('z-index', zIndex);
11489 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11490 this.maskEl.bottom.setLeft(0);
11491 this.maskEl.bottom.setTop(box.bottom + this.padding);
11492 this.maskEl.bottom.show();
11494 this.maskEl.right.setStyle('position', 'absolute');
11495 this.maskEl.right.setStyle('z-index', zIndex);
11496 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11497 this.maskEl.right.setLeft(box.right + this.padding);
11498 this.maskEl.right.setTop(box.y - this.padding);
11499 this.maskEl.right.show();
11501 this.toolTip.bindEl = this.target.el;
11503 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11505 var tip = this.target.blankText;
11507 if(this.target.getValue() !== '' ) {
11509 if (this.target.invalidText.length) {
11510 tip = this.target.invalidText;
11511 } else if (this.target.regexText.length){
11512 tip = this.target.regexText;
11516 this.toolTip.show(tip);
11518 this.intervalID = window.setInterval(function() {
11519 Roo.bootstrap.Form.popover.unmask();
11522 window.onwheel = function(){ return false;};
11524 (function(){ this.isMasked = true; }).defer(500, this);
11528 unmask : function()
11530 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11534 this.maskEl.top.setStyle('position', 'absolute');
11535 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11536 this.maskEl.top.hide();
11538 this.maskEl.left.setStyle('position', 'absolute');
11539 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11540 this.maskEl.left.hide();
11542 this.maskEl.bottom.setStyle('position', 'absolute');
11543 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11544 this.maskEl.bottom.hide();
11546 this.maskEl.right.setStyle('position', 'absolute');
11547 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11548 this.maskEl.right.hide();
11550 this.toolTip.hide();
11552 this.toolTip.el.hide();
11554 window.onwheel = function(){ return true;};
11556 if(this.intervalID){
11557 window.clearInterval(this.intervalID);
11558 this.intervalID = false;
11561 this.isMasked = false;
11571 * Ext JS Library 1.1.1
11572 * Copyright(c) 2006-2007, Ext JS, LLC.
11574 * Originally Released Under LGPL - original licence link has changed is not relivant.
11577 * <script type="text/javascript">
11580 * @class Roo.form.VTypes
11581 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11584 Roo.form.VTypes = function(){
11585 // closure these in so they are only created once.
11586 var alpha = /^[a-zA-Z_]+$/;
11587 var alphanum = /^[a-zA-Z0-9_]+$/;
11588 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11589 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11591 // All these messages and functions are configurable
11594 * The function used to validate email addresses
11595 * @param {String} value The email address
11597 'email' : function(v){
11598 return email.test(v);
11601 * The error text to display when the email validation function returns false
11604 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11606 * The keystroke filter mask to be applied on email input
11609 'emailMask' : /[a-z0-9_\.\-@]/i,
11612 * The function used to validate URLs
11613 * @param {String} value The URL
11615 'url' : function(v){
11616 return url.test(v);
11619 * The error text to display when the url validation function returns false
11622 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11625 * The function used to validate alpha values
11626 * @param {String} value The value
11628 'alpha' : function(v){
11629 return alpha.test(v);
11632 * The error text to display when the alpha validation function returns false
11635 'alphaText' : 'This field should only contain letters and _',
11637 * The keystroke filter mask to be applied on alpha input
11640 'alphaMask' : /[a-z_]/i,
11643 * The function used to validate alphanumeric values
11644 * @param {String} value The value
11646 'alphanum' : function(v){
11647 return alphanum.test(v);
11650 * The error text to display when the alphanumeric validation function returns false
11653 'alphanumText' : 'This field should only contain letters, numbers and _',
11655 * The keystroke filter mask to be applied on alphanumeric input
11658 'alphanumMask' : /[a-z0-9_]/i
11668 * @class Roo.bootstrap.Input
11669 * @extends Roo.bootstrap.Component
11670 * Bootstrap Input class
11671 * @cfg {Boolean} disabled is it disabled
11672 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11673 * @cfg {String} name name of the input
11674 * @cfg {string} fieldLabel - the label associated
11675 * @cfg {string} placeholder - placeholder to put in text.
11676 * @cfg {string} before - input group add on before
11677 * @cfg {string} after - input group add on after
11678 * @cfg {string} size - (lg|sm) or leave empty..
11679 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11680 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11681 * @cfg {Number} md colspan out of 12 for computer-sized screens
11682 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11683 * @cfg {string} value default value of the input
11684 * @cfg {Number} labelWidth set the width of label
11685 * @cfg {Number} labellg set the width of label (1-12)
11686 * @cfg {Number} labelmd set the width of label (1-12)
11687 * @cfg {Number} labelsm set the width of label (1-12)
11688 * @cfg {Number} labelxs set the width of label (1-12)
11689 * @cfg {String} labelAlign (top|left)
11690 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11691 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11692 * @cfg {String} indicatorpos (left|right) default left
11693 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11694 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11695 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11697 * @cfg {String} align (left|center|right) Default left
11698 * @cfg {Boolean} forceFeedback (true|false) Default false
11701 * Create a new Input
11702 * @param {Object} config The config object
11705 Roo.bootstrap.Input = function(config){
11707 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11712 * Fires when this field receives input focus.
11713 * @param {Roo.form.Field} this
11718 * Fires when this field loses input focus.
11719 * @param {Roo.form.Field} this
11723 * @event specialkey
11724 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11725 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11726 * @param {Roo.form.Field} this
11727 * @param {Roo.EventObject} e The event object
11732 * Fires just before the field blurs if the field value has changed.
11733 * @param {Roo.form.Field} this
11734 * @param {Mixed} newValue The new value
11735 * @param {Mixed} oldValue The original value
11740 * Fires after the field has been marked as invalid.
11741 * @param {Roo.form.Field} this
11742 * @param {String} msg The validation message
11747 * Fires after the field has been validated with no errors.
11748 * @param {Roo.form.Field} this
11753 * Fires after the key up
11754 * @param {Roo.form.Field} this
11755 * @param {Roo.EventObject} e The event Object
11760 * Fires after the user pastes into input
11761 * @param {Roo.form.Field} this
11762 * @param {Roo.EventObject} e The event Object
11768 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11770 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11771 automatic validation (defaults to "keyup").
11773 validationEvent : "keyup",
11775 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11777 validateOnBlur : true,
11779 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11781 validationDelay : 250,
11783 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11785 focusClass : "x-form-focus", // not needed???
11789 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11791 invalidClass : "has-warning",
11794 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11796 validClass : "has-success",
11799 * @cfg {Boolean} hasFeedback (true|false) default true
11801 hasFeedback : true,
11804 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11806 invalidFeedbackClass : "glyphicon-warning-sign",
11809 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11811 validFeedbackClass : "glyphicon-ok",
11814 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11816 selectOnFocus : false,
11819 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11823 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11828 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11830 disableKeyFilter : false,
11833 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11837 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11841 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11843 blankText : "Please complete this mandatory field",
11846 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11850 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11852 maxLength : Number.MAX_VALUE,
11854 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11856 minLengthText : "The minimum length for this field is {0}",
11858 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11860 maxLengthText : "The maximum length for this field is {0}",
11864 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11865 * If available, this function will be called only after the basic validators all return true, and will be passed the
11866 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11870 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11871 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11872 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11876 * @cfg {String} regexText -- Depricated - use Invalid Text
11881 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11887 autocomplete: false,
11891 inputType : 'text',
11894 placeholder: false,
11899 preventMark: false,
11900 isFormField : true,
11903 labelAlign : false,
11906 formatedValue : false,
11907 forceFeedback : false,
11909 indicatorpos : 'left',
11919 parentLabelAlign : function()
11922 while (parent.parent()) {
11923 parent = parent.parent();
11924 if (typeof(parent.labelAlign) !='undefined') {
11925 return parent.labelAlign;
11932 getAutoCreate : function()
11934 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11940 if(this.inputType != 'hidden'){
11941 cfg.cls = 'form-group' //input-group
11947 type : this.inputType,
11948 value : this.value,
11949 cls : 'form-control',
11950 placeholder : this.placeholder || '',
11951 autocomplete : this.autocomplete || 'new-password'
11953 if (this.inputType == 'file') {
11954 input.style = 'overflow:hidden'; // why not in CSS?
11957 if(this.capture.length){
11958 input.capture = this.capture;
11961 if(this.accept.length){
11962 input.accept = this.accept + "/*";
11966 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11969 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11970 input.maxLength = this.maxLength;
11973 if (this.disabled) {
11974 input.disabled=true;
11977 if (this.readOnly) {
11978 input.readonly=true;
11982 input.name = this.name;
11986 input.cls += ' input-' + this.size;
11990 ['xs','sm','md','lg'].map(function(size){
11991 if (settings[size]) {
11992 cfg.cls += ' col-' + size + '-' + settings[size];
11996 var inputblock = input;
12000 cls: 'glyphicon form-control-feedback'
12003 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12006 cls : 'has-feedback',
12014 if (this.before || this.after) {
12017 cls : 'input-group',
12021 if (this.before && typeof(this.before) == 'string') {
12023 inputblock.cn.push({
12025 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12029 if (this.before && typeof(this.before) == 'object') {
12030 this.before = Roo.factory(this.before);
12032 inputblock.cn.push({
12034 cls : 'roo-input-before input-group-prepend input-group-' +
12035 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12039 inputblock.cn.push(input);
12041 if (this.after && typeof(this.after) == 'string') {
12042 inputblock.cn.push({
12044 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12048 if (this.after && typeof(this.after) == 'object') {
12049 this.after = Roo.factory(this.after);
12051 inputblock.cn.push({
12053 cls : 'roo-input-after input-group-append input-group-' +
12054 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12058 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12059 inputblock.cls += ' has-feedback';
12060 inputblock.cn.push(feedback);
12065 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12066 tooltip : 'This field is required'
12068 if (this.allowBlank ) {
12069 indicator.style = this.allowBlank ? ' display:none' : '';
12071 if (align ==='left' && this.fieldLabel.length) {
12073 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12080 cls : 'control-label col-form-label',
12081 html : this.fieldLabel
12092 var labelCfg = cfg.cn[1];
12093 var contentCfg = cfg.cn[2];
12095 if(this.indicatorpos == 'right'){
12100 cls : 'control-label col-form-label',
12104 html : this.fieldLabel
12118 labelCfg = cfg.cn[0];
12119 contentCfg = cfg.cn[1];
12123 if(this.labelWidth > 12){
12124 labelCfg.style = "width: " + this.labelWidth + 'px';
12127 if(this.labelWidth < 13 && this.labelmd == 0){
12128 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12131 if(this.labellg > 0){
12132 labelCfg.cls += ' col-lg-' + this.labellg;
12133 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12136 if(this.labelmd > 0){
12137 labelCfg.cls += ' col-md-' + this.labelmd;
12138 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12141 if(this.labelsm > 0){
12142 labelCfg.cls += ' col-sm-' + this.labelsm;
12143 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12146 if(this.labelxs > 0){
12147 labelCfg.cls += ' col-xs-' + this.labelxs;
12148 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12152 } else if ( this.fieldLabel.length) {
12159 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12160 tooltip : 'This field is required',
12161 style : this.allowBlank ? ' display:none' : ''
12165 //cls : 'input-group-addon',
12166 html : this.fieldLabel
12174 if(this.indicatorpos == 'right'){
12179 //cls : 'input-group-addon',
12180 html : this.fieldLabel
12185 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12186 tooltip : 'This field is required',
12187 style : this.allowBlank ? ' display:none' : ''
12207 if (this.parentType === 'Navbar' && this.parent().bar) {
12208 cfg.cls += ' navbar-form';
12211 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12212 // on BS4 we do this only if not form
12213 cfg.cls += ' navbar-form';
12221 * return the real input element.
12223 inputEl: function ()
12225 return this.el.select('input.form-control',true).first();
12228 tooltipEl : function()
12230 return this.inputEl();
12233 indicatorEl : function()
12235 if (Roo.bootstrap.version == 4) {
12236 return false; // not enabled in v4 yet.
12239 var indicator = this.el.select('i.roo-required-indicator',true).first();
12249 setDisabled : function(v)
12251 var i = this.inputEl().dom;
12253 i.removeAttribute('disabled');
12257 i.setAttribute('disabled','true');
12259 initEvents : function()
12262 this.inputEl().on("keydown" , this.fireKey, this);
12263 this.inputEl().on("focus", this.onFocus, this);
12264 this.inputEl().on("blur", this.onBlur, this);
12266 this.inputEl().relayEvent('keyup', this);
12267 this.inputEl().relayEvent('paste', this);
12269 this.indicator = this.indicatorEl();
12271 if(this.indicator){
12272 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12275 // reference to original value for reset
12276 this.originalValue = this.getValue();
12277 //Roo.form.TextField.superclass.initEvents.call(this);
12278 if(this.validationEvent == 'keyup'){
12279 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12280 this.inputEl().on('keyup', this.filterValidation, this);
12282 else if(this.validationEvent !== false){
12283 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12286 if(this.selectOnFocus){
12287 this.on("focus", this.preFocus, this);
12290 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12291 this.inputEl().on("keypress", this.filterKeys, this);
12293 this.inputEl().relayEvent('keypress', this);
12296 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12297 this.el.on("click", this.autoSize, this);
12300 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12301 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12304 if (typeof(this.before) == 'object') {
12305 this.before.render(this.el.select('.roo-input-before',true).first());
12307 if (typeof(this.after) == 'object') {
12308 this.after.render(this.el.select('.roo-input-after',true).first());
12311 this.inputEl().on('change', this.onChange, this);
12314 filterValidation : function(e){
12315 if(!e.isNavKeyPress()){
12316 this.validationTask.delay(this.validationDelay);
12320 * Validates the field value
12321 * @return {Boolean} True if the value is valid, else false
12323 validate : function(){
12324 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12325 if(this.disabled || this.validateValue(this.getRawValue())){
12330 this.markInvalid();
12336 * Validates a value according to the field's validation rules and marks the field as invalid
12337 * if the validation fails
12338 * @param {Mixed} value The value to validate
12339 * @return {Boolean} True if the value is valid, else false
12341 validateValue : function(value)
12343 if(this.getVisibilityEl().hasClass('hidden')){
12347 if(value.length < 1) { // if it's blank
12348 if(this.allowBlank){
12354 if(value.length < this.minLength){
12357 if(value.length > this.maxLength){
12361 var vt = Roo.form.VTypes;
12362 if(!vt[this.vtype](value, this)){
12366 if(typeof this.validator == "function"){
12367 var msg = this.validator(value);
12371 if (typeof(msg) == 'string') {
12372 this.invalidText = msg;
12376 if(this.regex && !this.regex.test(value)){
12384 fireKey : function(e){
12385 //Roo.log('field ' + e.getKey());
12386 if(e.isNavKeyPress()){
12387 this.fireEvent("specialkey", this, e);
12390 focus : function (selectText){
12392 this.inputEl().focus();
12393 if(selectText === true){
12394 this.inputEl().dom.select();
12400 onFocus : function(){
12401 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12402 // this.el.addClass(this.focusClass);
12404 if(!this.hasFocus){
12405 this.hasFocus = true;
12406 this.startValue = this.getValue();
12407 this.fireEvent("focus", this);
12411 beforeBlur : Roo.emptyFn,
12415 onBlur : function(){
12417 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12418 //this.el.removeClass(this.focusClass);
12420 this.hasFocus = false;
12421 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12424 var v = this.getValue();
12425 if(String(v) !== String(this.startValue)){
12426 this.fireEvent('change', this, v, this.startValue);
12428 this.fireEvent("blur", this);
12431 onChange : function(e)
12433 var v = this.getValue();
12434 if(String(v) !== String(this.startValue)){
12435 this.fireEvent('change', this, v, this.startValue);
12441 * Resets the current field value to the originally loaded value and clears any validation messages
12443 reset : function(){
12444 this.setValue(this.originalValue);
12448 * Returns the name of the field
12449 * @return {Mixed} name The name field
12451 getName: function(){
12455 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12456 * @return {Mixed} value The field value
12458 getValue : function(){
12460 var v = this.inputEl().getValue();
12465 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12466 * @return {Mixed} value The field value
12468 getRawValue : function(){
12469 var v = this.inputEl().getValue();
12475 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12476 * @param {Mixed} value The value to set
12478 setRawValue : function(v){
12479 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12482 selectText : function(start, end){
12483 var v = this.getRawValue();
12485 start = start === undefined ? 0 : start;
12486 end = end === undefined ? v.length : end;
12487 var d = this.inputEl().dom;
12488 if(d.setSelectionRange){
12489 d.setSelectionRange(start, end);
12490 }else if(d.createTextRange){
12491 var range = d.createTextRange();
12492 range.moveStart("character", start);
12493 range.moveEnd("character", v.length-end);
12500 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12501 * @param {Mixed} value The value to set
12503 setValue : function(v){
12506 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12512 processValue : function(value){
12513 if(this.stripCharsRe){
12514 var newValue = value.replace(this.stripCharsRe, '');
12515 if(newValue !== value){
12516 this.setRawValue(newValue);
12523 preFocus : function(){
12525 if(this.selectOnFocus){
12526 this.inputEl().dom.select();
12529 filterKeys : function(e){
12530 var k = e.getKey();
12531 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12534 var c = e.getCharCode(), cc = String.fromCharCode(c);
12535 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12538 if(!this.maskRe.test(cc)){
12543 * Clear any invalid styles/messages for this field
12545 clearInvalid : function(){
12547 if(!this.el || this.preventMark){ // not rendered
12552 this.el.removeClass([this.invalidClass, 'is-invalid']);
12554 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12556 var feedback = this.el.select('.form-control-feedback', true).first();
12559 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12564 if(this.indicator){
12565 this.indicator.removeClass('visible');
12566 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12569 this.fireEvent('valid', this);
12573 * Mark this field as valid
12575 markValid : function()
12577 if(!this.el || this.preventMark){ // not rendered...
12581 this.el.removeClass([this.invalidClass, this.validClass]);
12582 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12584 var feedback = this.el.select('.form-control-feedback', true).first();
12587 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12590 if(this.indicator){
12591 this.indicator.removeClass('visible');
12592 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12600 if(this.allowBlank && !this.getRawValue().length){
12603 if (Roo.bootstrap.version == 3) {
12604 this.el.addClass(this.validClass);
12606 this.inputEl().addClass('is-valid');
12609 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12611 var feedback = this.el.select('.form-control-feedback', true).first();
12614 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12615 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12620 this.fireEvent('valid', this);
12624 * Mark this field as invalid
12625 * @param {String} msg The validation message
12627 markInvalid : function(msg)
12629 if(!this.el || this.preventMark){ // not rendered
12633 this.el.removeClass([this.invalidClass, this.validClass]);
12634 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12636 var feedback = this.el.select('.form-control-feedback', true).first();
12639 this.el.select('.form-control-feedback', true).first().removeClass(
12640 [this.invalidFeedbackClass, this.validFeedbackClass]);
12647 if(this.allowBlank && !this.getRawValue().length){
12651 if(this.indicator){
12652 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12653 this.indicator.addClass('visible');
12655 if (Roo.bootstrap.version == 3) {
12656 this.el.addClass(this.invalidClass);
12658 this.inputEl().addClass('is-invalid');
12663 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12665 var feedback = this.el.select('.form-control-feedback', true).first();
12668 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12670 if(this.getValue().length || this.forceFeedback){
12671 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12678 this.fireEvent('invalid', this, msg);
12681 SafariOnKeyDown : function(event)
12683 // this is a workaround for a password hang bug on chrome/ webkit.
12684 if (this.inputEl().dom.type != 'password') {
12688 var isSelectAll = false;
12690 if(this.inputEl().dom.selectionEnd > 0){
12691 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12693 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12694 event.preventDefault();
12699 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12701 event.preventDefault();
12702 // this is very hacky as keydown always get's upper case.
12704 var cc = String.fromCharCode(event.getCharCode());
12705 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12709 adjustWidth : function(tag, w){
12710 tag = tag.toLowerCase();
12711 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12712 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12713 if(tag == 'input'){
12716 if(tag == 'textarea'){
12719 }else if(Roo.isOpera){
12720 if(tag == 'input'){
12723 if(tag == 'textarea'){
12731 setFieldLabel : function(v)
12733 if(!this.rendered){
12737 if(this.indicatorEl()){
12738 var ar = this.el.select('label > span',true);
12740 if (ar.elements.length) {
12741 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12742 this.fieldLabel = v;
12746 var br = this.el.select('label',true);
12748 if(br.elements.length) {
12749 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12750 this.fieldLabel = v;
12754 Roo.log('Cannot Found any of label > span || label in input');
12758 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12759 this.fieldLabel = v;
12774 * @class Roo.bootstrap.TextArea
12775 * @extends Roo.bootstrap.Input
12776 * Bootstrap TextArea class
12777 * @cfg {Number} cols Specifies the visible width of a text area
12778 * @cfg {Number} rows Specifies the visible number of lines in a text area
12779 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12780 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12781 * @cfg {string} html text
12784 * Create a new TextArea
12785 * @param {Object} config The config object
12788 Roo.bootstrap.TextArea = function(config){
12789 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12793 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12803 getAutoCreate : function(){
12805 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12811 if(this.inputType != 'hidden'){
12812 cfg.cls = 'form-group' //input-group
12820 value : this.value || '',
12821 html: this.html || '',
12822 cls : 'form-control',
12823 placeholder : this.placeholder || ''
12827 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12828 input.maxLength = this.maxLength;
12832 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12836 input.cols = this.cols;
12839 if (this.readOnly) {
12840 input.readonly = true;
12844 input.name = this.name;
12848 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12852 ['xs','sm','md','lg'].map(function(size){
12853 if (settings[size]) {
12854 cfg.cls += ' col-' + size + '-' + settings[size];
12858 var inputblock = input;
12860 if(this.hasFeedback && !this.allowBlank){
12864 cls: 'glyphicon form-control-feedback'
12868 cls : 'has-feedback',
12877 if (this.before || this.after) {
12880 cls : 'input-group',
12884 inputblock.cn.push({
12886 cls : 'input-group-addon',
12891 inputblock.cn.push(input);
12893 if(this.hasFeedback && !this.allowBlank){
12894 inputblock.cls += ' has-feedback';
12895 inputblock.cn.push(feedback);
12899 inputblock.cn.push({
12901 cls : 'input-group-addon',
12908 if (align ==='left' && this.fieldLabel.length) {
12913 cls : 'control-label',
12914 html : this.fieldLabel
12925 if(this.labelWidth > 12){
12926 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12929 if(this.labelWidth < 13 && this.labelmd == 0){
12930 this.labelmd = this.labelWidth;
12933 if(this.labellg > 0){
12934 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12935 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12938 if(this.labelmd > 0){
12939 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12940 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12943 if(this.labelsm > 0){
12944 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12945 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12948 if(this.labelxs > 0){
12949 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12950 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12953 } else if ( this.fieldLabel.length) {
12958 //cls : 'input-group-addon',
12959 html : this.fieldLabel
12977 if (this.disabled) {
12978 input.disabled=true;
12985 * return the real textarea element.
12987 inputEl: function ()
12989 return this.el.select('textarea.form-control',true).first();
12993 * Clear any invalid styles/messages for this field
12995 clearInvalid : function()
12998 if(!this.el || this.preventMark){ // not rendered
13002 var label = this.el.select('label', true).first();
13003 var icon = this.el.select('i.fa-star', true).first();
13008 this.el.removeClass( this.validClass);
13009 this.inputEl().removeClass('is-invalid');
13011 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13013 var feedback = this.el.select('.form-control-feedback', true).first();
13016 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13021 this.fireEvent('valid', this);
13025 * Mark this field as valid
13027 markValid : function()
13029 if(!this.el || this.preventMark){ // not rendered
13033 this.el.removeClass([this.invalidClass, this.validClass]);
13034 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13036 var feedback = this.el.select('.form-control-feedback', true).first();
13039 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13042 if(this.disabled || this.allowBlank){
13046 var label = this.el.select('label', true).first();
13047 var icon = this.el.select('i.fa-star', true).first();
13052 if (Roo.bootstrap.version == 3) {
13053 this.el.addClass(this.validClass);
13055 this.inputEl().addClass('is-valid');
13059 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13061 var feedback = this.el.select('.form-control-feedback', true).first();
13064 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13065 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13070 this.fireEvent('valid', this);
13074 * Mark this field as invalid
13075 * @param {String} msg The validation message
13077 markInvalid : function(msg)
13079 if(!this.el || this.preventMark){ // not rendered
13083 this.el.removeClass([this.invalidClass, this.validClass]);
13084 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13086 var feedback = this.el.select('.form-control-feedback', true).first();
13089 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13092 if(this.disabled || this.allowBlank){
13096 var label = this.el.select('label', true).first();
13097 var icon = this.el.select('i.fa-star', true).first();
13099 if(!this.getValue().length && label && !icon){
13100 this.el.createChild({
13102 cls : 'text-danger fa fa-lg fa-star',
13103 tooltip : 'This field is required',
13104 style : 'margin-right:5px;'
13108 if (Roo.bootstrap.version == 3) {
13109 this.el.addClass(this.invalidClass);
13111 this.inputEl().addClass('is-invalid');
13114 // fixme ... this may be depricated need to test..
13115 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13117 var feedback = this.el.select('.form-control-feedback', true).first();
13120 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13122 if(this.getValue().length || this.forceFeedback){
13123 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13130 this.fireEvent('invalid', this, msg);
13138 * trigger field - base class for combo..
13143 * @class Roo.bootstrap.TriggerField
13144 * @extends Roo.bootstrap.Input
13145 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13146 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13147 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13148 * for which you can provide a custom implementation. For example:
13150 var trigger = new Roo.bootstrap.TriggerField();
13151 trigger.onTriggerClick = myTriggerFn;
13152 trigger.applyTo('my-field');
13155 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13156 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13157 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13158 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13159 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13162 * Create a new TriggerField.
13163 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13164 * to the base TextField)
13166 Roo.bootstrap.TriggerField = function(config){
13167 this.mimicing = false;
13168 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13171 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13173 * @cfg {String} triggerClass A CSS class to apply to the trigger
13176 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13181 * @cfg {Boolean} removable (true|false) special filter default false
13185 /** @cfg {Boolean} grow @hide */
13186 /** @cfg {Number} growMin @hide */
13187 /** @cfg {Number} growMax @hide */
13193 autoSize: Roo.emptyFn,
13197 deferHeight : true,
13200 actionMode : 'wrap',
13205 getAutoCreate : function(){
13207 var align = this.labelAlign || this.parentLabelAlign();
13212 cls: 'form-group' //input-group
13219 type : this.inputType,
13220 cls : 'form-control',
13221 autocomplete: 'new-password',
13222 placeholder : this.placeholder || ''
13226 input.name = this.name;
13229 input.cls += ' input-' + this.size;
13232 if (this.disabled) {
13233 input.disabled=true;
13236 var inputblock = input;
13238 if(this.hasFeedback && !this.allowBlank){
13242 cls: 'glyphicon form-control-feedback'
13245 if(this.removable && !this.editable ){
13247 cls : 'has-feedback',
13253 cls : 'roo-combo-removable-btn close'
13260 cls : 'has-feedback',
13269 if(this.removable && !this.editable ){
13271 cls : 'roo-removable',
13277 cls : 'roo-combo-removable-btn close'
13284 if (this.before || this.after) {
13287 cls : 'input-group',
13291 inputblock.cn.push({
13293 cls : 'input-group-addon input-group-prepend input-group-text',
13298 inputblock.cn.push(input);
13300 if(this.hasFeedback && !this.allowBlank){
13301 inputblock.cls += ' has-feedback';
13302 inputblock.cn.push(feedback);
13306 inputblock.cn.push({
13308 cls : 'input-group-addon input-group-append input-group-text',
13317 var ibwrap = inputblock;
13322 cls: 'roo-select2-choices',
13326 cls: 'roo-select2-search-field',
13338 cls: 'roo-select2-container input-group',
13343 cls: 'form-hidden-field'
13349 if(!this.multiple && this.showToggleBtn){
13355 if (this.caret != false) {
13358 cls: 'fa fa-' + this.caret
13365 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13367 Roo.bootstrap.version == 3 ? caret : '',
13370 cls: 'combobox-clear',
13384 combobox.cls += ' roo-select2-container-multi';
13388 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13389 tooltip : 'This field is required'
13391 if (Roo.bootstrap.version == 4) {
13394 style : 'display:none'
13399 if (align ==='left' && this.fieldLabel.length) {
13401 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13408 cls : 'control-label',
13409 html : this.fieldLabel
13421 var labelCfg = cfg.cn[1];
13422 var contentCfg = cfg.cn[2];
13424 if(this.indicatorpos == 'right'){
13429 cls : 'control-label',
13433 html : this.fieldLabel
13447 labelCfg = cfg.cn[0];
13448 contentCfg = cfg.cn[1];
13451 if(this.labelWidth > 12){
13452 labelCfg.style = "width: " + this.labelWidth + 'px';
13455 if(this.labelWidth < 13 && this.labelmd == 0){
13456 this.labelmd = this.labelWidth;
13459 if(this.labellg > 0){
13460 labelCfg.cls += ' col-lg-' + this.labellg;
13461 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13464 if(this.labelmd > 0){
13465 labelCfg.cls += ' col-md-' + this.labelmd;
13466 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13469 if(this.labelsm > 0){
13470 labelCfg.cls += ' col-sm-' + this.labelsm;
13471 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13474 if(this.labelxs > 0){
13475 labelCfg.cls += ' col-xs-' + this.labelxs;
13476 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13479 } else if ( this.fieldLabel.length) {
13480 // Roo.log(" label");
13485 //cls : 'input-group-addon',
13486 html : this.fieldLabel
13494 if(this.indicatorpos == 'right'){
13502 html : this.fieldLabel
13516 // Roo.log(" no label && no align");
13523 ['xs','sm','md','lg'].map(function(size){
13524 if (settings[size]) {
13525 cfg.cls += ' col-' + size + '-' + settings[size];
13536 onResize : function(w, h){
13537 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13538 // if(typeof w == 'number'){
13539 // var x = w - this.trigger.getWidth();
13540 // this.inputEl().setWidth(this.adjustWidth('input', x));
13541 // this.trigger.setStyle('left', x+'px');
13546 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13549 getResizeEl : function(){
13550 return this.inputEl();
13554 getPositionEl : function(){
13555 return this.inputEl();
13559 alignErrorIcon : function(){
13560 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13564 initEvents : function(){
13568 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13569 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13570 if(!this.multiple && this.showToggleBtn){
13571 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13572 if(this.hideTrigger){
13573 this.trigger.setDisplayed(false);
13575 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13579 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13582 if(this.removable && !this.editable && !this.tickable){
13583 var close = this.closeTriggerEl();
13586 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13587 close.on('click', this.removeBtnClick, this, close);
13591 //this.trigger.addClassOnOver('x-form-trigger-over');
13592 //this.trigger.addClassOnClick('x-form-trigger-click');
13595 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13599 closeTriggerEl : function()
13601 var close = this.el.select('.roo-combo-removable-btn', true).first();
13602 return close ? close : false;
13605 removeBtnClick : function(e, h, el)
13607 e.preventDefault();
13609 if(this.fireEvent("remove", this) !== false){
13611 this.fireEvent("afterremove", this)
13615 createList : function()
13617 this.list = Roo.get(document.body).createChild({
13618 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13619 cls: 'typeahead typeahead-long dropdown-menu shadow',
13620 style: 'display:none'
13623 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13628 initTrigger : function(){
13633 onDestroy : function(){
13635 this.trigger.removeAllListeners();
13636 // this.trigger.remove();
13639 // this.wrap.remove();
13641 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13645 onFocus : function(){
13646 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13648 if(!this.mimicing){
13649 this.wrap.addClass('x-trigger-wrap-focus');
13650 this.mimicing = true;
13651 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13652 if(this.monitorTab){
13653 this.el.on("keydown", this.checkTab, this);
13660 checkTab : function(e){
13661 if(e.getKey() == e.TAB){
13662 this.triggerBlur();
13667 onBlur : function(){
13672 mimicBlur : function(e, t){
13674 if(!this.wrap.contains(t) && this.validateBlur()){
13675 this.triggerBlur();
13681 triggerBlur : function(){
13682 this.mimicing = false;
13683 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13684 if(this.monitorTab){
13685 this.el.un("keydown", this.checkTab, this);
13687 //this.wrap.removeClass('x-trigger-wrap-focus');
13688 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13692 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13693 validateBlur : function(e, t){
13698 onDisable : function(){
13699 this.inputEl().dom.disabled = true;
13700 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13702 // this.wrap.addClass('x-item-disabled');
13707 onEnable : function(){
13708 this.inputEl().dom.disabled = false;
13709 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13711 // this.el.removeClass('x-item-disabled');
13716 onShow : function(){
13717 var ae = this.getActionEl();
13720 ae.dom.style.display = '';
13721 ae.dom.style.visibility = 'visible';
13727 onHide : function(){
13728 var ae = this.getActionEl();
13729 ae.dom.style.display = 'none';
13733 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13734 * by an implementing function.
13736 * @param {EventObject} e
13738 onTriggerClick : Roo.emptyFn
13746 * @class Roo.bootstrap.CardUploader
13747 * @extends Roo.bootstrap.Button
13748 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13749 * @cfg {Number} errorTimeout default 3000
13750 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13751 * @cfg {Array} html The button text.
13755 * Create a new CardUploader
13756 * @param {Object} config The config object
13759 Roo.bootstrap.CardUploader = function(config){
13763 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13766 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13774 * When a image is clicked on - and needs to display a slideshow or similar..
13775 * @param {Roo.bootstrap.Card} this
13776 * @param {Object} The image information data
13782 * When a the download link is clicked
13783 * @param {Roo.bootstrap.Card} this
13784 * @param {Object} The image information data contains
13791 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13794 errorTimeout : 3000,
13798 fileCollection : false,
13801 getAutoCreate : function()
13805 cls :'form-group' ,
13810 //cls : 'input-group-addon',
13811 html : this.fieldLabel
13819 value : this.value,
13820 cls : 'd-none form-control'
13825 multiple : 'multiple',
13827 cls : 'd-none roo-card-upload-selector'
13831 cls : 'roo-card-uploader-button-container w-100 mb-2'
13834 cls : 'card-columns roo-card-uploader-container'
13844 getChildContainer : function() /// what children are added to.
13846 return this.containerEl;
13849 getButtonContainer : function() /// what children are added to.
13851 return this.el.select(".roo-card-uploader-button-container").first();
13854 initEvents : function()
13857 Roo.bootstrap.Input.prototype.initEvents.call(this);
13861 xns: Roo.bootstrap,
13864 container_method : 'getButtonContainer' ,
13865 html : this.html, // fix changable?
13868 'click' : function(btn, e) {
13877 this.urlAPI = (window.createObjectURL && window) ||
13878 (window.URL && URL.revokeObjectURL && URL) ||
13879 (window.webkitURL && webkitURL);
13884 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13886 this.selectorEl.on('change', this.onFileSelected, this);
13889 this.images.forEach(function(img) {
13892 this.images = false;
13894 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13900 onClick : function(e)
13902 e.preventDefault();
13904 this.selectorEl.dom.click();
13908 onFileSelected : function(e)
13910 e.preventDefault();
13912 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13916 Roo.each(this.selectorEl.dom.files, function(file){
13917 this.addFile(file);
13926 addFile : function(file)
13929 if(typeof(file) === 'string'){
13930 throw "Add file by name?"; // should not happen
13934 if(!file || !this.urlAPI){
13944 var url = _this.urlAPI.createObjectURL( file);
13947 id : Roo.bootstrap.CardUploader.ID--,
13948 is_uploaded : false,
13952 mimetype : file.type,
13960 * addCard - add an Attachment to the uploader
13961 * @param data - the data about the image to upload
13965 title : "Title of file",
13966 is_uploaded : false,
13967 src : "http://.....",
13968 srcfile : { the File upload object },
13969 mimetype : file.type,
13972 .. any other data...
13978 addCard : function (data)
13980 // hidden input element?
13981 // if the file is not an image...
13982 //then we need to use something other that and header_image
13987 xns : Roo.bootstrap,
13988 xtype : 'CardFooter',
13991 xns : Roo.bootstrap,
13997 xns : Roo.bootstrap,
13999 html : String.format("<small>{0}</small>", data.title),
14000 cls : 'col-10 text-left',
14005 click : function() {
14007 t.fireEvent( "download", t, data );
14013 xns : Roo.bootstrap,
14015 style: 'max-height: 28px; ',
14021 click : function() {
14022 t.removeCard(data.id)
14034 var cn = this.addxtype(
14037 xns : Roo.bootstrap,
14040 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14041 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14042 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14047 initEvents : function() {
14048 Roo.bootstrap.Card.prototype.initEvents.call(this);
14050 this.imgEl = this.el.select('.card-img-top').first();
14052 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14053 this.imgEl.set({ 'pointer' : 'cursor' });
14056 this.getCardFooter().addClass('p-1');
14063 // dont' really need ot update items.
14064 // this.items.push(cn);
14065 this.fileCollection.add(cn);
14067 if (!data.srcfile) {
14068 this.updateInput();
14073 var reader = new FileReader();
14074 reader.addEventListener("load", function() {
14075 data.srcdata = reader.result;
14078 reader.readAsDataURL(data.srcfile);
14083 removeCard : function(id)
14086 var card = this.fileCollection.get(id);
14087 card.data.is_deleted = 1;
14088 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14089 //this.fileCollection.remove(card);
14090 //this.items = this.items.filter(function(e) { return e != card });
14091 // dont' really need ot update items.
14092 card.el.dom.parentNode.removeChild(card.el.dom);
14093 this.updateInput();
14099 this.fileCollection.each(function(card) {
14100 if (card.el.dom && card.el.dom.parentNode) {
14101 card.el.dom.parentNode.removeChild(card.el.dom);
14104 this.fileCollection.clear();
14105 this.updateInput();
14108 updateInput : function()
14111 this.fileCollection.each(function(e) {
14115 this.inputEl().dom.value = JSON.stringify(data);
14125 Roo.bootstrap.CardUploader.ID = -1;/*
14127 * Ext JS Library 1.1.1
14128 * Copyright(c) 2006-2007, Ext JS, LLC.
14130 * Originally Released Under LGPL - original licence link has changed is not relivant.
14133 * <script type="text/javascript">
14138 * @class Roo.data.SortTypes
14140 * Defines the default sorting (casting?) comparison functions used when sorting data.
14142 Roo.data.SortTypes = {
14144 * Default sort that does nothing
14145 * @param {Mixed} s The value being converted
14146 * @return {Mixed} The comparison value
14148 none : function(s){
14153 * The regular expression used to strip tags
14157 stripTagsRE : /<\/?[^>]+>/gi,
14160 * Strips all HTML tags to sort on text only
14161 * @param {Mixed} s The value being converted
14162 * @return {String} The comparison value
14164 asText : function(s){
14165 return String(s).replace(this.stripTagsRE, "");
14169 * Strips all HTML tags to sort on text only - Case insensitive
14170 * @param {Mixed} s The value being converted
14171 * @return {String} The comparison value
14173 asUCText : function(s){
14174 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14178 * Case insensitive string
14179 * @param {Mixed} s The value being converted
14180 * @return {String} The comparison value
14182 asUCString : function(s) {
14183 return String(s).toUpperCase();
14188 * @param {Mixed} s The value being converted
14189 * @return {Number} The comparison value
14191 asDate : function(s) {
14195 if(s instanceof Date){
14196 return s.getTime();
14198 return Date.parse(String(s));
14203 * @param {Mixed} s The value being converted
14204 * @return {Float} The comparison value
14206 asFloat : function(s) {
14207 var val = parseFloat(String(s).replace(/,/g, ""));
14216 * @param {Mixed} s The value being converted
14217 * @return {Number} The comparison value
14219 asInt : function(s) {
14220 var val = parseInt(String(s).replace(/,/g, ""));
14228 * Ext JS Library 1.1.1
14229 * Copyright(c) 2006-2007, Ext JS, LLC.
14231 * Originally Released Under LGPL - original licence link has changed is not relivant.
14234 * <script type="text/javascript">
14238 * @class Roo.data.Record
14239 * Instances of this class encapsulate both record <em>definition</em> information, and record
14240 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14241 * to access Records cached in an {@link Roo.data.Store} object.<br>
14243 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14244 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14247 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14249 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14250 * {@link #create}. The parameters are the same.
14251 * @param {Array} data An associative Array of data values keyed by the field name.
14252 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14253 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14254 * not specified an integer id is generated.
14256 Roo.data.Record = function(data, id){
14257 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14262 * Generate a constructor for a specific record layout.
14263 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14264 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14265 * Each field definition object may contain the following properties: <ul>
14266 * <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,
14267 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14268 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14269 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14270 * is being used, then this is a string containing the javascript expression to reference the data relative to
14271 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14272 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14273 * this may be omitted.</p></li>
14274 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14275 * <ul><li>auto (Default, implies no conversion)</li>
14280 * <li>date</li></ul></p></li>
14281 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14282 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14283 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14284 * by the Reader into an object that will be stored in the Record. It is passed the
14285 * following parameters:<ul>
14286 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14288 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14290 * <br>usage:<br><pre><code>
14291 var TopicRecord = Roo.data.Record.create(
14292 {name: 'title', mapping: 'topic_title'},
14293 {name: 'author', mapping: 'username'},
14294 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14295 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14296 {name: 'lastPoster', mapping: 'user2'},
14297 {name: 'excerpt', mapping: 'post_text'}
14300 var myNewRecord = new TopicRecord({
14301 title: 'Do my job please',
14304 lastPost: new Date(),
14305 lastPoster: 'Animal',
14306 excerpt: 'No way dude!'
14308 myStore.add(myNewRecord);
14313 Roo.data.Record.create = function(o){
14314 var f = function(){
14315 f.superclass.constructor.apply(this, arguments);
14317 Roo.extend(f, Roo.data.Record);
14318 var p = f.prototype;
14319 p.fields = new Roo.util.MixedCollection(false, function(field){
14322 for(var i = 0, len = o.length; i < len; i++){
14323 p.fields.add(new Roo.data.Field(o[i]));
14325 f.getField = function(name){
14326 return p.fields.get(name);
14331 Roo.data.Record.AUTO_ID = 1000;
14332 Roo.data.Record.EDIT = 'edit';
14333 Roo.data.Record.REJECT = 'reject';
14334 Roo.data.Record.COMMIT = 'commit';
14336 Roo.data.Record.prototype = {
14338 * Readonly flag - true if this record has been modified.
14347 join : function(store){
14348 this.store = store;
14352 * Set the named field to the specified value.
14353 * @param {String} name The name of the field to set.
14354 * @param {Object} value The value to set the field to.
14356 set : function(name, value){
14357 if(this.data[name] == value){
14361 if(!this.modified){
14362 this.modified = {};
14364 if(typeof this.modified[name] == 'undefined'){
14365 this.modified[name] = this.data[name];
14367 this.data[name] = value;
14368 if(!this.editing && this.store){
14369 this.store.afterEdit(this);
14374 * Get the value of the named field.
14375 * @param {String} name The name of the field to get the value of.
14376 * @return {Object} The value of the field.
14378 get : function(name){
14379 return this.data[name];
14383 beginEdit : function(){
14384 this.editing = true;
14385 this.modified = {};
14389 cancelEdit : function(){
14390 this.editing = false;
14391 delete this.modified;
14395 endEdit : function(){
14396 this.editing = false;
14397 if(this.dirty && this.store){
14398 this.store.afterEdit(this);
14403 * Usually called by the {@link Roo.data.Store} which owns the Record.
14404 * Rejects all changes made to the Record since either creation, or the last commit operation.
14405 * Modified fields are reverted to their original values.
14407 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14408 * of reject operations.
14410 reject : function(){
14411 var m = this.modified;
14413 if(typeof m[n] != "function"){
14414 this.data[n] = m[n];
14417 this.dirty = false;
14418 delete this.modified;
14419 this.editing = false;
14421 this.store.afterReject(this);
14426 * Usually called by the {@link Roo.data.Store} which owns the Record.
14427 * Commits all changes made to the Record since either creation, or the last commit operation.
14429 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14430 * of commit operations.
14432 commit : function(){
14433 this.dirty = false;
14434 delete this.modified;
14435 this.editing = false;
14437 this.store.afterCommit(this);
14442 hasError : function(){
14443 return this.error != null;
14447 clearError : function(){
14452 * Creates a copy of this record.
14453 * @param {String} id (optional) A new record id if you don't want to use this record's id
14456 copy : function(newId) {
14457 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14461 * Ext JS Library 1.1.1
14462 * Copyright(c) 2006-2007, Ext JS, LLC.
14464 * Originally Released Under LGPL - original licence link has changed is not relivant.
14467 * <script type="text/javascript">
14473 * @class Roo.data.Store
14474 * @extends Roo.util.Observable
14475 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14476 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14478 * 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
14479 * has no knowledge of the format of the data returned by the Proxy.<br>
14481 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14482 * instances from the data object. These records are cached and made available through accessor functions.
14484 * Creates a new Store.
14485 * @param {Object} config A config object containing the objects needed for the Store to access data,
14486 * and read the data into Records.
14488 Roo.data.Store = function(config){
14489 this.data = new Roo.util.MixedCollection(false);
14490 this.data.getKey = function(o){
14493 this.baseParams = {};
14495 this.paramNames = {
14500 "multisort" : "_multisort"
14503 if(config && config.data){
14504 this.inlineData = config.data;
14505 delete config.data;
14508 Roo.apply(this, config);
14510 if(this.reader){ // reader passed
14511 this.reader = Roo.factory(this.reader, Roo.data);
14512 this.reader.xmodule = this.xmodule || false;
14513 if(!this.recordType){
14514 this.recordType = this.reader.recordType;
14516 if(this.reader.onMetaChange){
14517 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14521 if(this.recordType){
14522 this.fields = this.recordType.prototype.fields;
14524 this.modified = [];
14528 * @event datachanged
14529 * Fires when the data cache has changed, and a widget which is using this Store
14530 * as a Record cache should refresh its view.
14531 * @param {Store} this
14533 datachanged : true,
14535 * @event metachange
14536 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14537 * @param {Store} this
14538 * @param {Object} meta The JSON metadata
14543 * Fires when Records have been added to the Store
14544 * @param {Store} this
14545 * @param {Roo.data.Record[]} records The array of Records added
14546 * @param {Number} index The index at which the record(s) were added
14551 * Fires when a Record has been removed from the Store
14552 * @param {Store} this
14553 * @param {Roo.data.Record} record The Record that was removed
14554 * @param {Number} index The index at which the record was removed
14559 * Fires when a Record has been updated
14560 * @param {Store} this
14561 * @param {Roo.data.Record} record The Record that was updated
14562 * @param {String} operation The update operation being performed. Value may be one of:
14564 Roo.data.Record.EDIT
14565 Roo.data.Record.REJECT
14566 Roo.data.Record.COMMIT
14572 * Fires when the data cache has been cleared.
14573 * @param {Store} this
14577 * @event beforeload
14578 * Fires before a request is made for a new data object. If the beforeload handler returns false
14579 * the load action will be canceled.
14580 * @param {Store} this
14581 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14585 * @event beforeloadadd
14586 * Fires after a new set of Records has been loaded.
14587 * @param {Store} this
14588 * @param {Roo.data.Record[]} records The Records that were loaded
14589 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14591 beforeloadadd : true,
14594 * Fires after a new set of Records has been loaded, before they are added to the store.
14595 * @param {Store} this
14596 * @param {Roo.data.Record[]} records The Records that were loaded
14597 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14598 * @params {Object} return from reader
14602 * @event loadexception
14603 * Fires if an exception occurs in the Proxy during loading.
14604 * Called with the signature of the Proxy's "loadexception" event.
14605 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14608 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14609 * @param {Object} load options
14610 * @param {Object} jsonData from your request (normally this contains the Exception)
14612 loadexception : true
14616 this.proxy = Roo.factory(this.proxy, Roo.data);
14617 this.proxy.xmodule = this.xmodule || false;
14618 this.relayEvents(this.proxy, ["loadexception"]);
14620 this.sortToggle = {};
14621 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14623 Roo.data.Store.superclass.constructor.call(this);
14625 if(this.inlineData){
14626 this.loadData(this.inlineData);
14627 delete this.inlineData;
14631 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14633 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14634 * without a remote query - used by combo/forms at present.
14638 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14641 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14644 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14645 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14648 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14649 * on any HTTP request
14652 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14655 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14659 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14660 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14662 remoteSort : false,
14665 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14666 * loaded or when a record is removed. (defaults to false).
14668 pruneModifiedRecords : false,
14671 lastOptions : null,
14674 * Add Records to the Store and fires the add event.
14675 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14677 add : function(records){
14678 records = [].concat(records);
14679 for(var i = 0, len = records.length; i < len; i++){
14680 records[i].join(this);
14682 var index = this.data.length;
14683 this.data.addAll(records);
14684 this.fireEvent("add", this, records, index);
14688 * Remove a Record from the Store and fires the remove event.
14689 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14691 remove : function(record){
14692 var index = this.data.indexOf(record);
14693 this.data.removeAt(index);
14695 if(this.pruneModifiedRecords){
14696 this.modified.remove(record);
14698 this.fireEvent("remove", this, record, index);
14702 * Remove all Records from the Store and fires the clear event.
14704 removeAll : function(){
14706 if(this.pruneModifiedRecords){
14707 this.modified = [];
14709 this.fireEvent("clear", this);
14713 * Inserts Records to the Store at the given index and fires the add event.
14714 * @param {Number} index The start index at which to insert the passed Records.
14715 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14717 insert : function(index, records){
14718 records = [].concat(records);
14719 for(var i = 0, len = records.length; i < len; i++){
14720 this.data.insert(index, records[i]);
14721 records[i].join(this);
14723 this.fireEvent("add", this, records, index);
14727 * Get the index within the cache of the passed Record.
14728 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14729 * @return {Number} The index of the passed Record. Returns -1 if not found.
14731 indexOf : function(record){
14732 return this.data.indexOf(record);
14736 * Get the index within the cache of the Record with the passed id.
14737 * @param {String} id The id of the Record to find.
14738 * @return {Number} The index of the Record. Returns -1 if not found.
14740 indexOfId : function(id){
14741 return this.data.indexOfKey(id);
14745 * Get the Record with the specified id.
14746 * @param {String} id The id of the Record to find.
14747 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14749 getById : function(id){
14750 return this.data.key(id);
14754 * Get the Record at the specified index.
14755 * @param {Number} index The index of the Record to find.
14756 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14758 getAt : function(index){
14759 return this.data.itemAt(index);
14763 * Returns a range of Records between specified indices.
14764 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14765 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14766 * @return {Roo.data.Record[]} An array of Records
14768 getRange : function(start, end){
14769 return this.data.getRange(start, end);
14773 storeOptions : function(o){
14774 o = Roo.apply({}, o);
14777 this.lastOptions = o;
14781 * Loads the Record cache from the configured Proxy using the configured Reader.
14783 * If using remote paging, then the first load call must specify the <em>start</em>
14784 * and <em>limit</em> properties in the options.params property to establish the initial
14785 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14787 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14788 * and this call will return before the new data has been loaded. Perform any post-processing
14789 * in a callback function, or in a "load" event handler.</strong>
14791 * @param {Object} options An object containing properties which control loading options:<ul>
14792 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14793 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14794 * passed the following arguments:<ul>
14795 * <li>r : Roo.data.Record[]</li>
14796 * <li>options: Options object from the load call</li>
14797 * <li>success: Boolean success indicator</li></ul></li>
14798 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14799 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14802 load : function(options){
14803 options = options || {};
14804 if(this.fireEvent("beforeload", this, options) !== false){
14805 this.storeOptions(options);
14806 var p = Roo.apply(options.params || {}, this.baseParams);
14807 // if meta was not loaded from remote source.. try requesting it.
14808 if (!this.reader.metaFromRemote) {
14809 p._requestMeta = 1;
14811 if(this.sortInfo && this.remoteSort){
14812 var pn = this.paramNames;
14813 p[pn["sort"]] = this.sortInfo.field;
14814 p[pn["dir"]] = this.sortInfo.direction;
14816 if (this.multiSort) {
14817 var pn = this.paramNames;
14818 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14821 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14826 * Reloads the Record cache from the configured Proxy using the configured Reader and
14827 * the options from the last load operation performed.
14828 * @param {Object} options (optional) An object containing properties which may override the options
14829 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14830 * the most recently used options are reused).
14832 reload : function(options){
14833 this.load(Roo.applyIf(options||{}, this.lastOptions));
14837 // Called as a callback by the Reader during a load operation.
14838 loadRecords : function(o, options, success){
14839 if(!o || success === false){
14840 if(success !== false){
14841 this.fireEvent("load", this, [], options, o);
14843 if(options.callback){
14844 options.callback.call(options.scope || this, [], options, false);
14848 // if data returned failure - throw an exception.
14849 if (o.success === false) {
14850 // show a message if no listener is registered.
14851 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14852 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14854 // loadmask wil be hooked into this..
14855 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14858 var r = o.records, t = o.totalRecords || r.length;
14860 this.fireEvent("beforeloadadd", this, r, options, o);
14862 if(!options || options.add !== true){
14863 if(this.pruneModifiedRecords){
14864 this.modified = [];
14866 for(var i = 0, len = r.length; i < len; i++){
14870 this.data = this.snapshot;
14871 delete this.snapshot;
14874 this.data.addAll(r);
14875 this.totalLength = t;
14877 this.fireEvent("datachanged", this);
14879 this.totalLength = Math.max(t, this.data.length+r.length);
14883 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14885 var e = new Roo.data.Record({});
14887 e.set(this.parent.displayField, this.parent.emptyTitle);
14888 e.set(this.parent.valueField, '');
14893 this.fireEvent("load", this, r, options, o);
14894 if(options.callback){
14895 options.callback.call(options.scope || this, r, options, true);
14901 * Loads data from a passed data block. A Reader which understands the format of the data
14902 * must have been configured in the constructor.
14903 * @param {Object} data The data block from which to read the Records. The format of the data expected
14904 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14905 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14907 loadData : function(o, append){
14908 var r = this.reader.readRecords(o);
14909 this.loadRecords(r, {add: append}, true);
14913 * using 'cn' the nested child reader read the child array into it's child stores.
14914 * @param {Object} rec The record with a 'children array
14916 loadDataFromChildren : function(rec)
14918 this.loadData(this.reader.toLoadData(rec));
14923 * Gets the number of cached records.
14925 * <em>If using paging, this may not be the total size of the dataset. If the data object
14926 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14927 * the data set size</em>
14929 getCount : function(){
14930 return this.data.length || 0;
14934 * Gets the total number of records in the dataset as returned by the server.
14936 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14937 * the dataset size</em>
14939 getTotalCount : function(){
14940 return this.totalLength || 0;
14944 * Returns the sort state of the Store as an object with two properties:
14946 field {String} The name of the field by which the Records are sorted
14947 direction {String} The sort order, "ASC" or "DESC"
14950 getSortState : function(){
14951 return this.sortInfo;
14955 applySort : function(){
14956 if(this.sortInfo && !this.remoteSort){
14957 var s = this.sortInfo, f = s.field;
14958 var st = this.fields.get(f).sortType;
14959 var fn = function(r1, r2){
14960 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14961 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14963 this.data.sort(s.direction, fn);
14964 if(this.snapshot && this.snapshot != this.data){
14965 this.snapshot.sort(s.direction, fn);
14971 * Sets the default sort column and order to be used by the next load operation.
14972 * @param {String} fieldName The name of the field to sort by.
14973 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14975 setDefaultSort : function(field, dir){
14976 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14980 * Sort the Records.
14981 * If remote sorting is used, the sort is performed on the server, and the cache is
14982 * reloaded. If local sorting is used, the cache is sorted internally.
14983 * @param {String} fieldName The name of the field to sort by.
14984 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14986 sort : function(fieldName, dir){
14987 var f = this.fields.get(fieldName);
14989 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14991 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14992 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14997 this.sortToggle[f.name] = dir;
14998 this.sortInfo = {field: f.name, direction: dir};
14999 if(!this.remoteSort){
15001 this.fireEvent("datachanged", this);
15003 this.load(this.lastOptions);
15008 * Calls the specified function for each of the Records in the cache.
15009 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15010 * Returning <em>false</em> aborts and exits the iteration.
15011 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15013 each : function(fn, scope){
15014 this.data.each(fn, scope);
15018 * Gets all records modified since the last commit. Modified records are persisted across load operations
15019 * (e.g., during paging).
15020 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15022 getModifiedRecords : function(){
15023 return this.modified;
15027 createFilterFn : function(property, value, anyMatch){
15028 if(!value.exec){ // not a regex
15029 value = String(value);
15030 if(value.length == 0){
15033 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15035 return function(r){
15036 return value.test(r.data[property]);
15041 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15042 * @param {String} property A field on your records
15043 * @param {Number} start The record index to start at (defaults to 0)
15044 * @param {Number} end The last record index to include (defaults to length - 1)
15045 * @return {Number} The sum
15047 sum : function(property, start, end){
15048 var rs = this.data.items, v = 0;
15049 start = start || 0;
15050 end = (end || end === 0) ? end : rs.length-1;
15052 for(var i = start; i <= end; i++){
15053 v += (rs[i].data[property] || 0);
15059 * Filter the records by a specified property.
15060 * @param {String} field A field on your records
15061 * @param {String/RegExp} value Either a string that the field
15062 * should start with or a RegExp to test against the field
15063 * @param {Boolean} anyMatch True to match any part not just the beginning
15065 filter : function(property, value, anyMatch){
15066 var fn = this.createFilterFn(property, value, anyMatch);
15067 return fn ? this.filterBy(fn) : this.clearFilter();
15071 * Filter by a function. The specified function will be called with each
15072 * record in this data source. If the function returns true the record is included,
15073 * otherwise it is filtered.
15074 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15075 * @param {Object} scope (optional) The scope of the function (defaults to this)
15077 filterBy : function(fn, scope){
15078 this.snapshot = this.snapshot || this.data;
15079 this.data = this.queryBy(fn, scope||this);
15080 this.fireEvent("datachanged", this);
15084 * Query the records by a specified property.
15085 * @param {String} field A field on your records
15086 * @param {String/RegExp} value Either a string that the field
15087 * should start with or a RegExp to test against the field
15088 * @param {Boolean} anyMatch True to match any part not just the beginning
15089 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15091 query : function(property, value, anyMatch){
15092 var fn = this.createFilterFn(property, value, anyMatch);
15093 return fn ? this.queryBy(fn) : this.data.clone();
15097 * Query by a function. The specified function will be called with each
15098 * record in this data source. If the function returns true the record is included
15100 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15101 * @param {Object} scope (optional) The scope of the function (defaults to this)
15102 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15104 queryBy : function(fn, scope){
15105 var data = this.snapshot || this.data;
15106 return data.filterBy(fn, scope||this);
15110 * Collects unique values for a particular dataIndex from this store.
15111 * @param {String} dataIndex The property to collect
15112 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15113 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15114 * @return {Array} An array of the unique values
15116 collect : function(dataIndex, allowNull, bypassFilter){
15117 var d = (bypassFilter === true && this.snapshot) ?
15118 this.snapshot.items : this.data.items;
15119 var v, sv, r = [], l = {};
15120 for(var i = 0, len = d.length; i < len; i++){
15121 v = d[i].data[dataIndex];
15123 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15132 * Revert to a view of the Record cache with no filtering applied.
15133 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15135 clearFilter : function(suppressEvent){
15136 if(this.snapshot && this.snapshot != this.data){
15137 this.data = this.snapshot;
15138 delete this.snapshot;
15139 if(suppressEvent !== true){
15140 this.fireEvent("datachanged", this);
15146 afterEdit : function(record){
15147 if(this.modified.indexOf(record) == -1){
15148 this.modified.push(record);
15150 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15154 afterReject : function(record){
15155 this.modified.remove(record);
15156 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15160 afterCommit : function(record){
15161 this.modified.remove(record);
15162 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15166 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15167 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15169 commitChanges : function(){
15170 var m = this.modified.slice(0);
15171 this.modified = [];
15172 for(var i = 0, len = m.length; i < len; i++){
15178 * Cancel outstanding changes on all changed records.
15180 rejectChanges : function(){
15181 var m = this.modified.slice(0);
15182 this.modified = [];
15183 for(var i = 0, len = m.length; i < len; i++){
15188 onMetaChange : function(meta, rtype, o){
15189 this.recordType = rtype;
15190 this.fields = rtype.prototype.fields;
15191 delete this.snapshot;
15192 this.sortInfo = meta.sortInfo || this.sortInfo;
15193 this.modified = [];
15194 this.fireEvent('metachange', this, this.reader.meta);
15197 moveIndex : function(data, type)
15199 var index = this.indexOf(data);
15201 var newIndex = index + type;
15205 this.insert(newIndex, data);
15210 * Ext JS Library 1.1.1
15211 * Copyright(c) 2006-2007, Ext JS, LLC.
15213 * Originally Released Under LGPL - original licence link has changed is not relivant.
15216 * <script type="text/javascript">
15220 * @class Roo.data.SimpleStore
15221 * @extends Roo.data.Store
15222 * Small helper class to make creating Stores from Array data easier.
15223 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15224 * @cfg {Array} fields An array of field definition objects, or field name strings.
15225 * @cfg {Object} an existing reader (eg. copied from another store)
15226 * @cfg {Array} data The multi-dimensional array of data
15228 * @param {Object} config
15230 Roo.data.SimpleStore = function(config)
15232 Roo.data.SimpleStore.superclass.constructor.call(this, {
15234 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15237 Roo.data.Record.create(config.fields)
15239 proxy : new Roo.data.MemoryProxy(config.data)
15243 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15245 * Ext JS Library 1.1.1
15246 * Copyright(c) 2006-2007, Ext JS, LLC.
15248 * Originally Released Under LGPL - original licence link has changed is not relivant.
15251 * <script type="text/javascript">
15256 * @extends Roo.data.Store
15257 * @class Roo.data.JsonStore
15258 * Small helper class to make creating Stores for JSON data easier. <br/>
15260 var store = new Roo.data.JsonStore({
15261 url: 'get-images.php',
15263 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15266 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15267 * JsonReader and HttpProxy (unless inline data is provided).</b>
15268 * @cfg {Array} fields An array of field definition objects, or field name strings.
15270 * @param {Object} config
15272 Roo.data.JsonStore = function(c){
15273 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15274 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15275 reader: new Roo.data.JsonReader(c, c.fields)
15278 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15280 * Ext JS Library 1.1.1
15281 * Copyright(c) 2006-2007, Ext JS, LLC.
15283 * Originally Released Under LGPL - original licence link has changed is not relivant.
15286 * <script type="text/javascript">
15290 Roo.data.Field = function(config){
15291 if(typeof config == "string"){
15292 config = {name: config};
15294 Roo.apply(this, config);
15297 this.type = "auto";
15300 var st = Roo.data.SortTypes;
15301 // named sortTypes are supported, here we look them up
15302 if(typeof this.sortType == "string"){
15303 this.sortType = st[this.sortType];
15306 // set default sortType for strings and dates
15307 if(!this.sortType){
15310 this.sortType = st.asUCString;
15313 this.sortType = st.asDate;
15316 this.sortType = st.none;
15321 var stripRe = /[\$,%]/g;
15323 // prebuilt conversion function for this field, instead of
15324 // switching every time we're reading a value
15326 var cv, dateFormat = this.dateFormat;
15331 cv = function(v){ return v; };
15334 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15338 return v !== undefined && v !== null && v !== '' ?
15339 parseInt(String(v).replace(stripRe, ""), 10) : '';
15344 return v !== undefined && v !== null && v !== '' ?
15345 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15350 cv = function(v){ return v === true || v === "true" || v == 1; };
15357 if(v instanceof Date){
15361 if(dateFormat == "timestamp"){
15362 return new Date(v*1000);
15364 return Date.parseDate(v, dateFormat);
15366 var parsed = Date.parse(v);
15367 return parsed ? new Date(parsed) : null;
15376 Roo.data.Field.prototype = {
15384 * Ext JS Library 1.1.1
15385 * Copyright(c) 2006-2007, Ext JS, LLC.
15387 * Originally Released Under LGPL - original licence link has changed is not relivant.
15390 * <script type="text/javascript">
15393 // Base class for reading structured data from a data source. This class is intended to be
15394 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15397 * @class Roo.data.DataReader
15398 * Base class for reading structured data from a data source. This class is intended to be
15399 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15402 Roo.data.DataReader = function(meta, recordType){
15406 this.recordType = recordType instanceof Array ?
15407 Roo.data.Record.create(recordType) : recordType;
15410 Roo.data.DataReader.prototype = {
15413 readerType : 'Data',
15415 * Create an empty record
15416 * @param {Object} data (optional) - overlay some values
15417 * @return {Roo.data.Record} record created.
15419 newRow : function(d) {
15421 this.recordType.prototype.fields.each(function(c) {
15423 case 'int' : da[c.name] = 0; break;
15424 case 'date' : da[c.name] = new Date(); break;
15425 case 'float' : da[c.name] = 0.0; break;
15426 case 'boolean' : da[c.name] = false; break;
15427 default : da[c.name] = ""; break;
15431 return new this.recordType(Roo.apply(da, d));
15437 * Ext JS Library 1.1.1
15438 * Copyright(c) 2006-2007, Ext JS, LLC.
15440 * Originally Released Under LGPL - original licence link has changed is not relivant.
15443 * <script type="text/javascript">
15447 * @class Roo.data.DataProxy
15448 * @extends Roo.data.Observable
15449 * This class is an abstract base class for implementations which provide retrieval of
15450 * unformatted data objects.<br>
15452 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15453 * (of the appropriate type which knows how to parse the data object) to provide a block of
15454 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15456 * Custom implementations must implement the load method as described in
15457 * {@link Roo.data.HttpProxy#load}.
15459 Roo.data.DataProxy = function(){
15462 * @event beforeload
15463 * Fires before a network request is made to retrieve a data object.
15464 * @param {Object} This DataProxy object.
15465 * @param {Object} params The params parameter to the load function.
15470 * Fires before the load method's callback is called.
15471 * @param {Object} This DataProxy object.
15472 * @param {Object} o The data object.
15473 * @param {Object} arg The callback argument object passed to the load function.
15477 * @event loadexception
15478 * Fires if an Exception occurs during data retrieval.
15479 * @param {Object} This DataProxy object.
15480 * @param {Object} o The data object.
15481 * @param {Object} arg The callback argument object passed to the load function.
15482 * @param {Object} e The Exception.
15484 loadexception : true
15486 Roo.data.DataProxy.superclass.constructor.call(this);
15489 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15492 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15496 * Ext JS Library 1.1.1
15497 * Copyright(c) 2006-2007, Ext JS, LLC.
15499 * Originally Released Under LGPL - original licence link has changed is not relivant.
15502 * <script type="text/javascript">
15505 * @class Roo.data.MemoryProxy
15506 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15507 * to the Reader when its load method is called.
15509 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15511 Roo.data.MemoryProxy = function(data){
15515 Roo.data.MemoryProxy.superclass.constructor.call(this);
15519 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15522 * Load data from the requested source (in this case an in-memory
15523 * data object passed to the constructor), read the data object into
15524 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15525 * process that block using the passed callback.
15526 * @param {Object} params This parameter is not used by the MemoryProxy class.
15527 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15528 * object into a block of Roo.data.Records.
15529 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15530 * The function must be passed <ul>
15531 * <li>The Record block object</li>
15532 * <li>The "arg" argument from the load function</li>
15533 * <li>A boolean success indicator</li>
15535 * @param {Object} scope The scope in which to call the callback
15536 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15538 load : function(params, reader, callback, scope, arg){
15539 params = params || {};
15542 result = reader.readRecords(params.data ? params.data :this.data);
15544 this.fireEvent("loadexception", this, arg, null, e);
15545 callback.call(scope, null, arg, false);
15548 callback.call(scope, result, arg, true);
15552 update : function(params, records){
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 * @class Roo.data.HttpProxy
15567 * @extends Roo.data.DataProxy
15568 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15569 * configured to reference a certain URL.<br><br>
15571 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15572 * from which the running page was served.<br><br>
15574 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15576 * Be aware that to enable the browser to parse an XML document, the server must set
15577 * the Content-Type header in the HTTP response to "text/xml".
15579 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15580 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15581 * will be used to make the request.
15583 Roo.data.HttpProxy = function(conn){
15584 Roo.data.HttpProxy.superclass.constructor.call(this);
15585 // is conn a conn config or a real conn?
15587 this.useAjax = !conn || !conn.events;
15591 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15592 // thse are take from connection...
15595 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15598 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15599 * extra parameters to each request made by this object. (defaults to undefined)
15602 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15603 * to each request made by this object. (defaults to undefined)
15606 * @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)
15609 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15612 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15618 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15622 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15623 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15624 * a finer-grained basis than the DataProxy events.
15626 getConnection : function(){
15627 return this.useAjax ? Roo.Ajax : this.conn;
15631 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15632 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15633 * process that block using the passed callback.
15634 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15635 * for the request to the remote server.
15636 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15637 * object into a block of Roo.data.Records.
15638 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15639 * The function must be passed <ul>
15640 * <li>The Record block object</li>
15641 * <li>The "arg" argument from the load function</li>
15642 * <li>A boolean success indicator</li>
15644 * @param {Object} scope The scope in which to call the callback
15645 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15647 load : function(params, reader, callback, scope, arg){
15648 if(this.fireEvent("beforeload", this, params) !== false){
15650 params : params || {},
15652 callback : callback,
15657 callback : this.loadResponse,
15661 Roo.applyIf(o, this.conn);
15662 if(this.activeRequest){
15663 Roo.Ajax.abort(this.activeRequest);
15665 this.activeRequest = Roo.Ajax.request(o);
15667 this.conn.request(o);
15670 callback.call(scope||this, null, arg, false);
15675 loadResponse : function(o, success, response){
15676 delete this.activeRequest;
15678 this.fireEvent("loadexception", this, o, response);
15679 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15684 result = o.reader.read(response);
15686 this.fireEvent("loadexception", this, o, response, e);
15687 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15691 this.fireEvent("load", this, o, o.request.arg);
15692 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15696 update : function(dataSet){
15701 updateResponse : function(dataSet){
15706 * Ext JS Library 1.1.1
15707 * Copyright(c) 2006-2007, Ext JS, LLC.
15709 * Originally Released Under LGPL - original licence link has changed is not relivant.
15712 * <script type="text/javascript">
15716 * @class Roo.data.ScriptTagProxy
15717 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15718 * other than the originating domain of the running page.<br><br>
15720 * <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
15721 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15723 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15724 * source code that is used as the source inside a <script> tag.<br><br>
15726 * In order for the browser to process the returned data, the server must wrap the data object
15727 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15728 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15729 * depending on whether the callback name was passed:
15732 boolean scriptTag = false;
15733 String cb = request.getParameter("callback");
15736 response.setContentType("text/javascript");
15738 response.setContentType("application/x-json");
15740 Writer out = response.getWriter();
15742 out.write(cb + "(");
15744 out.print(dataBlock.toJsonString());
15751 * @param {Object} config A configuration object.
15753 Roo.data.ScriptTagProxy = function(config){
15754 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15755 Roo.apply(this, config);
15756 this.head = document.getElementsByTagName("head")[0];
15759 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15761 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15763 * @cfg {String} url The URL from which to request the data object.
15766 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15770 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15771 * the server the name of the callback function set up by the load call to process the returned data object.
15772 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15773 * javascript output which calls this named function passing the data object as its only parameter.
15775 callbackParam : "callback",
15777 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15778 * name to the request.
15783 * Load data from the configured URL, read the data object into
15784 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15785 * process that block using the passed callback.
15786 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15787 * for the request to the remote server.
15788 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15789 * object into a block of Roo.data.Records.
15790 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15791 * The function must be passed <ul>
15792 * <li>The Record block object</li>
15793 * <li>The "arg" argument from the load function</li>
15794 * <li>A boolean success indicator</li>
15796 * @param {Object} scope The scope in which to call the callback
15797 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15799 load : function(params, reader, callback, scope, arg){
15800 if(this.fireEvent("beforeload", this, params) !== false){
15802 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15804 var url = this.url;
15805 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15807 url += "&_dc=" + (new Date().getTime());
15809 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15812 cb : "stcCallback"+transId,
15813 scriptId : "stcScript"+transId,
15817 callback : callback,
15823 window[trans.cb] = function(o){
15824 conn.handleResponse(o, trans);
15827 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15829 if(this.autoAbort !== false){
15833 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15835 var script = document.createElement("script");
15836 script.setAttribute("src", url);
15837 script.setAttribute("type", "text/javascript");
15838 script.setAttribute("id", trans.scriptId);
15839 this.head.appendChild(script);
15841 this.trans = trans;
15843 callback.call(scope||this, null, arg, false);
15848 isLoading : function(){
15849 return this.trans ? true : false;
15853 * Abort the current server request.
15855 abort : function(){
15856 if(this.isLoading()){
15857 this.destroyTrans(this.trans);
15862 destroyTrans : function(trans, isLoaded){
15863 this.head.removeChild(document.getElementById(trans.scriptId));
15864 clearTimeout(trans.timeoutId);
15866 window[trans.cb] = undefined;
15868 delete window[trans.cb];
15871 // if hasn't been loaded, wait for load to remove it to prevent script error
15872 window[trans.cb] = function(){
15873 window[trans.cb] = undefined;
15875 delete window[trans.cb];
15882 handleResponse : function(o, trans){
15883 this.trans = false;
15884 this.destroyTrans(trans, true);
15887 result = trans.reader.readRecords(o);
15889 this.fireEvent("loadexception", this, o, trans.arg, e);
15890 trans.callback.call(trans.scope||window, null, trans.arg, false);
15893 this.fireEvent("load", this, o, trans.arg);
15894 trans.callback.call(trans.scope||window, result, trans.arg, true);
15898 handleFailure : function(trans){
15899 this.trans = false;
15900 this.destroyTrans(trans, false);
15901 this.fireEvent("loadexception", this, null, trans.arg);
15902 trans.callback.call(trans.scope||window, null, trans.arg, false);
15906 * Ext JS Library 1.1.1
15907 * Copyright(c) 2006-2007, Ext JS, LLC.
15909 * Originally Released Under LGPL - original licence link has changed is not relivant.
15912 * <script type="text/javascript">
15916 * @class Roo.data.JsonReader
15917 * @extends Roo.data.DataReader
15918 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15919 * based on mappings in a provided Roo.data.Record constructor.
15921 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15922 * in the reply previously.
15927 var RecordDef = Roo.data.Record.create([
15928 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15929 {name: 'occupation'} // This field will use "occupation" as the mapping.
15931 var myReader = new Roo.data.JsonReader({
15932 totalProperty: "results", // The property which contains the total dataset size (optional)
15933 root: "rows", // The property which contains an Array of row objects
15934 id: "id" // The property within each row object that provides an ID for the record (optional)
15938 * This would consume a JSON file like this:
15940 { 'results': 2, 'rows': [
15941 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15942 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15945 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15946 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15947 * paged from the remote server.
15948 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15949 * @cfg {String} root name of the property which contains the Array of row objects.
15950 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15951 * @cfg {Array} fields Array of field definition objects
15953 * Create a new JsonReader
15954 * @param {Object} meta Metadata configuration options
15955 * @param {Object} recordType Either an Array of field definition objects,
15956 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15958 Roo.data.JsonReader = function(meta, recordType){
15961 // set some defaults:
15962 Roo.applyIf(meta, {
15963 totalProperty: 'total',
15964 successProperty : 'success',
15969 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15971 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15973 readerType : 'Json',
15976 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15977 * Used by Store query builder to append _requestMeta to params.
15980 metaFromRemote : false,
15982 * This method is only used by a DataProxy which has retrieved data from a remote server.
15983 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15984 * @return {Object} data A data block which is used by an Roo.data.Store object as
15985 * a cache of Roo.data.Records.
15987 read : function(response){
15988 var json = response.responseText;
15990 var o = /* eval:var:o */ eval("("+json+")");
15992 throw {message: "JsonReader.read: Json object not found"};
15998 this.metaFromRemote = true;
15999 this.meta = o.metaData;
16000 this.recordType = Roo.data.Record.create(o.metaData.fields);
16001 this.onMetaChange(this.meta, this.recordType, o);
16003 return this.readRecords(o);
16006 // private function a store will implement
16007 onMetaChange : function(meta, recordType, o){
16014 simpleAccess: function(obj, subsc) {
16021 getJsonAccessor: function(){
16023 return function(expr) {
16025 return(re.test(expr))
16026 ? new Function("obj", "return obj." + expr)
16031 return Roo.emptyFn;
16036 * Create a data block containing Roo.data.Records from an XML document.
16037 * @param {Object} o An object which contains an Array of row objects in the property specified
16038 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16039 * which contains the total size of the dataset.
16040 * @return {Object} data A data block which is used by an Roo.data.Store object as
16041 * a cache of Roo.data.Records.
16043 readRecords : function(o){
16045 * After any data loads, the raw JSON data is available for further custom processing.
16049 var s = this.meta, Record = this.recordType,
16050 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16052 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16054 if(s.totalProperty) {
16055 this.getTotal = this.getJsonAccessor(s.totalProperty);
16057 if(s.successProperty) {
16058 this.getSuccess = this.getJsonAccessor(s.successProperty);
16060 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16062 var g = this.getJsonAccessor(s.id);
16063 this.getId = function(rec) {
16065 return (r === undefined || r === "") ? null : r;
16068 this.getId = function(){return null;};
16071 for(var jj = 0; jj < fl; jj++){
16073 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16074 this.ef[jj] = this.getJsonAccessor(map);
16078 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16079 if(s.totalProperty){
16080 var vt = parseInt(this.getTotal(o), 10);
16085 if(s.successProperty){
16086 var vs = this.getSuccess(o);
16087 if(vs === false || vs === 'false'){
16092 for(var i = 0; i < c; i++){
16095 var id = this.getId(n);
16096 for(var j = 0; j < fl; j++){
16098 var v = this.ef[j](n);
16100 Roo.log('missing convert for ' + f.name);
16104 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16106 var record = new Record(values, id);
16108 records[i] = record;
16114 totalRecords : totalRecords
16117 // used when loading children.. @see loadDataFromChildren
16118 toLoadData: function(rec)
16120 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16121 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16122 return { data : data, total : data.length };
16127 * Ext JS Library 1.1.1
16128 * Copyright(c) 2006-2007, Ext JS, LLC.
16130 * Originally Released Under LGPL - original licence link has changed is not relivant.
16133 * <script type="text/javascript">
16137 * @class Roo.data.ArrayReader
16138 * @extends Roo.data.DataReader
16139 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16140 * Each element of that Array represents a row of data fields. The
16141 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16142 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16146 var RecordDef = Roo.data.Record.create([
16147 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16148 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16150 var myReader = new Roo.data.ArrayReader({
16151 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16155 * This would consume an Array like this:
16157 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16161 * Create a new JsonReader
16162 * @param {Object} meta Metadata configuration options.
16163 * @param {Object|Array} recordType Either an Array of field definition objects
16165 * @cfg {Array} fields Array of field definition objects
16166 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16167 * as specified to {@link Roo.data.Record#create},
16168 * or an {@link Roo.data.Record} object
16171 * created using {@link Roo.data.Record#create}.
16173 Roo.data.ArrayReader = function(meta, recordType)
16175 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16178 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16181 * Create a data block containing Roo.data.Records from an XML document.
16182 * @param {Object} o An Array of row objects which represents the dataset.
16183 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16184 * a cache of Roo.data.Records.
16186 readRecords : function(o)
16188 var sid = this.meta ? this.meta.id : null;
16189 var recordType = this.recordType, fields = recordType.prototype.fields;
16192 for(var i = 0; i < root.length; i++){
16195 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16196 for(var j = 0, jlen = fields.length; j < jlen; j++){
16197 var f = fields.items[j];
16198 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16199 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16201 values[f.name] = v;
16203 var record = new recordType(values, id);
16205 records[records.length] = record;
16209 totalRecords : records.length
16212 // used when loading children.. @see loadDataFromChildren
16213 toLoadData: function(rec)
16215 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16216 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16227 * @class Roo.bootstrap.ComboBox
16228 * @extends Roo.bootstrap.TriggerField
16229 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16230 * @cfg {Boolean} append (true|false) default false
16231 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16232 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16233 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16234 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16235 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16236 * @cfg {Boolean} animate default true
16237 * @cfg {Boolean} emptyResultText only for touch device
16238 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16239 * @cfg {String} emptyTitle default ''
16240 * @cfg {Number} width fixed with? experimental
16242 * Create a new ComboBox.
16243 * @param {Object} config Configuration options
16245 Roo.bootstrap.ComboBox = function(config){
16246 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16250 * Fires when the dropdown list is expanded
16251 * @param {Roo.bootstrap.ComboBox} combo This combo box
16256 * Fires when the dropdown list is collapsed
16257 * @param {Roo.bootstrap.ComboBox} combo This combo box
16261 * @event beforeselect
16262 * Fires before a list item is selected. Return false to cancel the selection.
16263 * @param {Roo.bootstrap.ComboBox} combo This combo box
16264 * @param {Roo.data.Record} record The data record returned from the underlying store
16265 * @param {Number} index The index of the selected item in the dropdown list
16267 'beforeselect' : true,
16270 * Fires when a list item is selected
16271 * @param {Roo.bootstrap.ComboBox} combo This combo box
16272 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16273 * @param {Number} index The index of the selected item in the dropdown list
16277 * @event beforequery
16278 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16279 * The event object passed has these properties:
16280 * @param {Roo.bootstrap.ComboBox} combo This combo box
16281 * @param {String} query The query
16282 * @param {Boolean} forceAll true to force "all" query
16283 * @param {Boolean} cancel true to cancel the query
16284 * @param {Object} e The query event object
16286 'beforequery': true,
16289 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16290 * @param {Roo.bootstrap.ComboBox} combo This combo box
16295 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16296 * @param {Roo.bootstrap.ComboBox} combo This combo box
16297 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16302 * Fires when the remove value from the combobox array
16303 * @param {Roo.bootstrap.ComboBox} combo This combo box
16307 * @event afterremove
16308 * Fires when the remove value from the combobox array
16309 * @param {Roo.bootstrap.ComboBox} combo This combo box
16311 'afterremove' : true,
16313 * @event specialfilter
16314 * Fires when specialfilter
16315 * @param {Roo.bootstrap.ComboBox} combo This combo box
16317 'specialfilter' : true,
16320 * Fires when tick the element
16321 * @param {Roo.bootstrap.ComboBox} combo This combo box
16325 * @event touchviewdisplay
16326 * Fires when touch view require special display (default is using displayField)
16327 * @param {Roo.bootstrap.ComboBox} combo This combo box
16328 * @param {Object} cfg set html .
16330 'touchviewdisplay' : true
16335 this.tickItems = [];
16337 this.selectedIndex = -1;
16338 if(this.mode == 'local'){
16339 if(config.queryDelay === undefined){
16340 this.queryDelay = 10;
16342 if(config.minChars === undefined){
16348 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16351 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16352 * rendering into an Roo.Editor, defaults to false)
16355 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16356 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16359 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16362 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16363 * the dropdown list (defaults to undefined, with no header element)
16367 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16371 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16373 listWidth: undefined,
16375 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16376 * mode = 'remote' or 'text' if mode = 'local')
16378 displayField: undefined,
16381 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16382 * mode = 'remote' or 'value' if mode = 'local').
16383 * Note: use of a valueField requires the user make a selection
16384 * in order for a value to be mapped.
16386 valueField: undefined,
16388 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16393 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16394 * field's data value (defaults to the underlying DOM element's name)
16396 hiddenName: undefined,
16398 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16402 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16404 selectedClass: 'active',
16407 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16411 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16412 * anchor positions (defaults to 'tl-bl')
16414 listAlign: 'tl-bl?',
16416 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16420 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16421 * query specified by the allQuery config option (defaults to 'query')
16423 triggerAction: 'query',
16425 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16426 * (defaults to 4, does not apply if editable = false)
16430 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16431 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16435 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16436 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16440 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16441 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16445 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16446 * when editable = true (defaults to false)
16448 selectOnFocus:false,
16450 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16452 queryParam: 'query',
16454 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16455 * when mode = 'remote' (defaults to 'Loading...')
16457 loadingText: 'Loading...',
16459 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16463 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16467 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16468 * traditional select (defaults to true)
16472 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16476 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16480 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16481 * listWidth has a higher value)
16485 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16486 * allow the user to set arbitrary text into the field (defaults to false)
16488 forceSelection:false,
16490 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16491 * if typeAhead = true (defaults to 250)
16493 typeAheadDelay : 250,
16495 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16496 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16498 valueNotFoundText : undefined,
16500 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16502 blockFocus : false,
16505 * @cfg {Boolean} disableClear Disable showing of clear button.
16507 disableClear : false,
16509 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16511 alwaysQuery : false,
16514 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16519 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16521 invalidClass : "has-warning",
16524 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16526 validClass : "has-success",
16529 * @cfg {Boolean} specialFilter (true|false) special filter default false
16531 specialFilter : false,
16534 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16536 mobileTouchView : true,
16539 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16541 useNativeIOS : false,
16544 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16546 mobile_restrict_height : false,
16548 ios_options : false,
16560 btnPosition : 'right',
16561 triggerList : true,
16562 showToggleBtn : true,
16564 emptyResultText: 'Empty',
16565 triggerText : 'Select',
16569 // element that contains real text value.. (when hidden is used..)
16571 getAutoCreate : function()
16576 * Render classic select for iso
16579 if(Roo.isIOS && this.useNativeIOS){
16580 cfg = this.getAutoCreateNativeIOS();
16588 if(Roo.isTouch && this.mobileTouchView){
16589 cfg = this.getAutoCreateTouchView();
16596 if(!this.tickable){
16597 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16602 * ComboBox with tickable selections
16605 var align = this.labelAlign || this.parentLabelAlign();
16608 cls : 'form-group roo-combobox-tickable' //input-group
16611 var btn_text_select = '';
16612 var btn_text_done = '';
16613 var btn_text_cancel = '';
16615 if (this.btn_text_show) {
16616 btn_text_select = 'Select';
16617 btn_text_done = 'Done';
16618 btn_text_cancel = 'Cancel';
16623 cls : 'tickable-buttons',
16628 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16629 //html : this.triggerText
16630 html: btn_text_select
16636 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16638 html: btn_text_done
16644 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16646 html: btn_text_cancel
16652 buttons.cn.unshift({
16654 cls: 'roo-select2-search-field-input'
16660 Roo.each(buttons.cn, function(c){
16662 c.cls += ' btn-' + _this.size;
16665 if (_this.disabled) {
16672 style : 'display: contents',
16677 cls: 'form-hidden-field'
16681 cls: 'roo-select2-choices',
16685 cls: 'roo-select2-search-field',
16696 cls: 'roo-select2-container input-group roo-select2-container-multi',
16702 // cls: 'typeahead typeahead-long dropdown-menu',
16703 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16708 if(this.hasFeedback && !this.allowBlank){
16712 cls: 'glyphicon form-control-feedback'
16715 combobox.cn.push(feedback);
16722 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16723 tooltip : 'This field is required'
16725 if (Roo.bootstrap.version == 4) {
16728 style : 'display:none'
16731 if (align ==='left' && this.fieldLabel.length) {
16733 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16740 cls : 'control-label col-form-label',
16741 html : this.fieldLabel
16753 var labelCfg = cfg.cn[1];
16754 var contentCfg = cfg.cn[2];
16757 if(this.indicatorpos == 'right'){
16763 cls : 'control-label col-form-label',
16767 html : this.fieldLabel
16783 labelCfg = cfg.cn[0];
16784 contentCfg = cfg.cn[1];
16788 if(this.labelWidth > 12){
16789 labelCfg.style = "width: " + this.labelWidth + 'px';
16791 if(this.width * 1 > 0){
16792 contentCfg.style = "width: " + this.width + 'px';
16794 if(this.labelWidth < 13 && this.labelmd == 0){
16795 this.labelmd = this.labelWidth;
16798 if(this.labellg > 0){
16799 labelCfg.cls += ' col-lg-' + this.labellg;
16800 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16803 if(this.labelmd > 0){
16804 labelCfg.cls += ' col-md-' + this.labelmd;
16805 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16808 if(this.labelsm > 0){
16809 labelCfg.cls += ' col-sm-' + this.labelsm;
16810 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16813 if(this.labelxs > 0){
16814 labelCfg.cls += ' col-xs-' + this.labelxs;
16815 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16819 } else if ( this.fieldLabel.length) {
16820 // Roo.log(" label");
16825 //cls : 'input-group-addon',
16826 html : this.fieldLabel
16831 if(this.indicatorpos == 'right'){
16835 //cls : 'input-group-addon',
16836 html : this.fieldLabel
16846 // Roo.log(" no label && no align");
16853 ['xs','sm','md','lg'].map(function(size){
16854 if (settings[size]) {
16855 cfg.cls += ' col-' + size + '-' + settings[size];
16863 _initEventsCalled : false,
16866 initEvents: function()
16868 if (this._initEventsCalled) { // as we call render... prevent looping...
16871 this._initEventsCalled = true;
16874 throw "can not find store for combo";
16877 this.indicator = this.indicatorEl();
16879 this.store = Roo.factory(this.store, Roo.data);
16880 this.store.parent = this;
16882 // if we are building from html. then this element is so complex, that we can not really
16883 // use the rendered HTML.
16884 // so we have to trash and replace the previous code.
16885 if (Roo.XComponent.build_from_html) {
16886 // remove this element....
16887 var e = this.el.dom, k=0;
16888 while (e ) { e = e.previousSibling; ++k;}
16893 this.rendered = false;
16895 this.render(this.parent().getChildContainer(true), k);
16898 if(Roo.isIOS && this.useNativeIOS){
16899 this.initIOSView();
16907 if(Roo.isTouch && this.mobileTouchView){
16908 this.initTouchView();
16913 this.initTickableEvents();
16917 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16919 if(this.hiddenName){
16921 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16923 this.hiddenField.dom.value =
16924 this.hiddenValue !== undefined ? this.hiddenValue :
16925 this.value !== undefined ? this.value : '';
16927 // prevent input submission
16928 this.el.dom.removeAttribute('name');
16929 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16934 // this.el.dom.setAttribute('autocomplete', 'off');
16937 var cls = 'x-combo-list';
16939 //this.list = new Roo.Layer({
16940 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16946 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16947 _this.list.setWidth(lw);
16950 this.list.on('mouseover', this.onViewOver, this);
16951 this.list.on('mousemove', this.onViewMove, this);
16952 this.list.on('scroll', this.onViewScroll, this);
16955 this.list.swallowEvent('mousewheel');
16956 this.assetHeight = 0;
16959 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16960 this.assetHeight += this.header.getHeight();
16963 this.innerList = this.list.createChild({cls:cls+'-inner'});
16964 this.innerList.on('mouseover', this.onViewOver, this);
16965 this.innerList.on('mousemove', this.onViewMove, this);
16966 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16968 if(this.allowBlank && !this.pageSize && !this.disableClear){
16969 this.footer = this.list.createChild({cls:cls+'-ft'});
16970 this.pageTb = new Roo.Toolbar(this.footer);
16974 this.footer = this.list.createChild({cls:cls+'-ft'});
16975 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16976 {pageSize: this.pageSize});
16980 if (this.pageTb && this.allowBlank && !this.disableClear) {
16982 this.pageTb.add(new Roo.Toolbar.Fill(), {
16983 cls: 'x-btn-icon x-btn-clear',
16985 handler: function()
16988 _this.clearValue();
16989 _this.onSelect(false, -1);
16994 this.assetHeight += this.footer.getHeight();
16999 this.tpl = Roo.bootstrap.version == 4 ?
17000 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17001 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17004 this.view = new Roo.View(this.list, this.tpl, {
17005 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17007 //this.view.wrapEl.setDisplayed(false);
17008 this.view.on('click', this.onViewClick, this);
17011 this.store.on('beforeload', this.onBeforeLoad, this);
17012 this.store.on('load', this.onLoad, this);
17013 this.store.on('loadexception', this.onLoadException, this);
17015 if(this.resizable){
17016 this.resizer = new Roo.Resizable(this.list, {
17017 pinned:true, handles:'se'
17019 this.resizer.on('resize', function(r, w, h){
17020 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17021 this.listWidth = w;
17022 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17023 this.restrictHeight();
17025 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17028 if(!this.editable){
17029 this.editable = true;
17030 this.setEditable(false);
17035 if (typeof(this.events.add.listeners) != 'undefined') {
17037 this.addicon = this.wrap.createChild(
17038 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17040 this.addicon.on('click', function(e) {
17041 this.fireEvent('add', this);
17044 if (typeof(this.events.edit.listeners) != 'undefined') {
17046 this.editicon = this.wrap.createChild(
17047 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17048 if (this.addicon) {
17049 this.editicon.setStyle('margin-left', '40px');
17051 this.editicon.on('click', function(e) {
17053 // we fire even if inothing is selected..
17054 this.fireEvent('edit', this, this.lastData );
17060 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17061 "up" : function(e){
17062 this.inKeyMode = true;
17066 "down" : function(e){
17067 if(!this.isExpanded()){
17068 this.onTriggerClick();
17070 this.inKeyMode = true;
17075 "enter" : function(e){
17076 // this.onViewClick();
17080 if(this.fireEvent("specialkey", this, e)){
17081 this.onViewClick(false);
17087 "esc" : function(e){
17091 "tab" : function(e){
17094 if(this.fireEvent("specialkey", this, e)){
17095 this.onViewClick(false);
17103 doRelay : function(foo, bar, hname){
17104 if(hname == 'down' || this.scope.isExpanded()){
17105 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17114 this.queryDelay = Math.max(this.queryDelay || 10,
17115 this.mode == 'local' ? 10 : 250);
17118 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17120 if(this.typeAhead){
17121 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17123 if(this.editable !== false){
17124 this.inputEl().on("keyup", this.onKeyUp, this);
17126 if(this.forceSelection){
17127 this.inputEl().on('blur', this.doForce, this);
17131 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17132 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17136 initTickableEvents: function()
17140 if(this.hiddenName){
17142 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17144 this.hiddenField.dom.value =
17145 this.hiddenValue !== undefined ? this.hiddenValue :
17146 this.value !== undefined ? this.value : '';
17148 // prevent input submission
17149 this.el.dom.removeAttribute('name');
17150 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17155 // this.list = this.el.select('ul.dropdown-menu',true).first();
17157 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17158 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17159 if(this.triggerList){
17160 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17163 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17164 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17166 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17167 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17169 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17170 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17172 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17173 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17174 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17177 this.cancelBtn.hide();
17182 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17183 _this.list.setWidth(lw);
17186 this.list.on('mouseover', this.onViewOver, this);
17187 this.list.on('mousemove', this.onViewMove, this);
17189 this.list.on('scroll', this.onViewScroll, this);
17192 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17193 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17196 this.view = new Roo.View(this.list, this.tpl, {
17201 selectedClass: this.selectedClass
17204 //this.view.wrapEl.setDisplayed(false);
17205 this.view.on('click', this.onViewClick, this);
17209 this.store.on('beforeload', this.onBeforeLoad, this);
17210 this.store.on('load', this.onLoad, this);
17211 this.store.on('loadexception', this.onLoadException, this);
17214 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17215 "up" : function(e){
17216 this.inKeyMode = true;
17220 "down" : function(e){
17221 this.inKeyMode = true;
17225 "enter" : function(e){
17226 if(this.fireEvent("specialkey", this, e)){
17227 this.onViewClick(false);
17233 "esc" : function(e){
17234 this.onTickableFooterButtonClick(e, false, false);
17237 "tab" : function(e){
17238 this.fireEvent("specialkey", this, e);
17240 this.onTickableFooterButtonClick(e, false, false);
17247 doRelay : function(e, fn, key){
17248 if(this.scope.isExpanded()){
17249 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17258 this.queryDelay = Math.max(this.queryDelay || 10,
17259 this.mode == 'local' ? 10 : 250);
17262 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17264 if(this.typeAhead){
17265 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17268 if(this.editable !== false){
17269 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17272 this.indicator = this.indicatorEl();
17274 if(this.indicator){
17275 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17276 this.indicator.hide();
17281 onDestroy : function(){
17283 this.view.setStore(null);
17284 this.view.el.removeAllListeners();
17285 this.view.el.remove();
17286 this.view.purgeListeners();
17289 this.list.dom.innerHTML = '';
17293 this.store.un('beforeload', this.onBeforeLoad, this);
17294 this.store.un('load', this.onLoad, this);
17295 this.store.un('loadexception', this.onLoadException, this);
17297 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17301 fireKey : function(e){
17302 if(e.isNavKeyPress() && !this.list.isVisible()){
17303 this.fireEvent("specialkey", this, e);
17308 onResize: function(w, h)
17312 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17314 // if(typeof w != 'number'){
17315 // // we do not handle it!?!?
17318 // var tw = this.trigger.getWidth();
17319 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17320 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17322 // this.inputEl().setWidth( this.adjustWidth('input', x));
17324 // //this.trigger.setStyle('left', x+'px');
17326 // if(this.list && this.listWidth === undefined){
17327 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17328 // this.list.setWidth(lw);
17329 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17337 * Allow or prevent the user from directly editing the field text. If false is passed,
17338 * the user will only be able to select from the items defined in the dropdown list. This method
17339 * is the runtime equivalent of setting the 'editable' config option at config time.
17340 * @param {Boolean} value True to allow the user to directly edit the field text
17342 setEditable : function(value){
17343 if(value == this.editable){
17346 this.editable = value;
17348 this.inputEl().dom.setAttribute('readOnly', true);
17349 this.inputEl().on('mousedown', this.onTriggerClick, this);
17350 this.inputEl().addClass('x-combo-noedit');
17352 this.inputEl().dom.removeAttribute('readOnly');
17353 this.inputEl().un('mousedown', this.onTriggerClick, this);
17354 this.inputEl().removeClass('x-combo-noedit');
17360 onBeforeLoad : function(combo,opts){
17361 if(!this.hasFocus){
17365 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17367 this.restrictHeight();
17368 this.selectedIndex = -1;
17372 onLoad : function(){
17374 this.hasQuery = false;
17376 if(!this.hasFocus){
17380 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17381 this.loading.hide();
17384 if(this.store.getCount() > 0){
17387 this.restrictHeight();
17388 if(this.lastQuery == this.allQuery){
17389 if(this.editable && !this.tickable){
17390 this.inputEl().dom.select();
17394 !this.selectByValue(this.value, true) &&
17397 !this.store.lastOptions ||
17398 typeof(this.store.lastOptions.add) == 'undefined' ||
17399 this.store.lastOptions.add != true
17402 this.select(0, true);
17405 if(this.autoFocus){
17408 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17409 this.taTask.delay(this.typeAheadDelay);
17413 this.onEmptyResults();
17419 onLoadException : function()
17421 this.hasQuery = false;
17423 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17424 this.loading.hide();
17427 if(this.tickable && this.editable){
17432 // only causes errors at present
17433 //Roo.log(this.store.reader.jsonData);
17434 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17436 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17442 onTypeAhead : function(){
17443 if(this.store.getCount() > 0){
17444 var r = this.store.getAt(0);
17445 var newValue = r.data[this.displayField];
17446 var len = newValue.length;
17447 var selStart = this.getRawValue().length;
17449 if(selStart != len){
17450 this.setRawValue(newValue);
17451 this.selectText(selStart, newValue.length);
17457 onSelect : function(record, index){
17459 if(this.fireEvent('beforeselect', this, record, index) !== false){
17461 this.setFromData(index > -1 ? record.data : false);
17464 this.fireEvent('select', this, record, index);
17469 * Returns the currently selected field value or empty string if no value is set.
17470 * @return {String} value The selected value
17472 getValue : function()
17474 if(Roo.isIOS && this.useNativeIOS){
17475 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17479 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17482 if(this.valueField){
17483 return typeof this.value != 'undefined' ? this.value : '';
17485 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17489 getRawValue : function()
17491 if(Roo.isIOS && this.useNativeIOS){
17492 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17495 var v = this.inputEl().getValue();
17501 * Clears any text/value currently set in the field
17503 clearValue : function(){
17505 if(this.hiddenField){
17506 this.hiddenField.dom.value = '';
17509 this.setRawValue('');
17510 this.lastSelectionText = '';
17511 this.lastData = false;
17513 var close = this.closeTriggerEl();
17524 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17525 * will be displayed in the field. If the value does not match the data value of an existing item,
17526 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17527 * Otherwise the field will be blank (although the value will still be set).
17528 * @param {String} value The value to match
17530 setValue : function(v)
17532 if(Roo.isIOS && this.useNativeIOS){
17533 this.setIOSValue(v);
17543 if(this.valueField){
17544 var r = this.findRecord(this.valueField, v);
17546 text = r.data[this.displayField];
17547 }else if(this.valueNotFoundText !== undefined){
17548 text = this.valueNotFoundText;
17551 this.lastSelectionText = text;
17552 if(this.hiddenField){
17553 this.hiddenField.dom.value = v;
17555 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17558 var close = this.closeTriggerEl();
17561 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17567 * @property {Object} the last set data for the element
17572 * Sets the value of the field based on a object which is related to the record format for the store.
17573 * @param {Object} value the value to set as. or false on reset?
17575 setFromData : function(o){
17582 var dv = ''; // display value
17583 var vv = ''; // value value..
17585 if (this.displayField) {
17586 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17588 // this is an error condition!!!
17589 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17592 if(this.valueField){
17593 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17596 var close = this.closeTriggerEl();
17599 if(dv.length || vv * 1 > 0){
17601 this.blockFocus=true;
17607 if(this.hiddenField){
17608 this.hiddenField.dom.value = vv;
17610 this.lastSelectionText = dv;
17611 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17615 // no hidden field.. - we store the value in 'value', but still display
17616 // display field!!!!
17617 this.lastSelectionText = dv;
17618 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17625 reset : function(){
17626 // overridden so that last data is reset..
17633 this.setValue(this.originalValue);
17634 //this.clearInvalid();
17635 this.lastData = false;
17637 this.view.clearSelections();
17643 findRecord : function(prop, value){
17645 if(this.store.getCount() > 0){
17646 this.store.each(function(r){
17647 if(r.data[prop] == value){
17657 getName: function()
17659 // returns hidden if it's set..
17660 if (!this.rendered) {return ''};
17661 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17665 onViewMove : function(e, t){
17666 this.inKeyMode = false;
17670 onViewOver : function(e, t){
17671 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17674 var item = this.view.findItemFromChild(t);
17677 var index = this.view.indexOf(item);
17678 this.select(index, false);
17683 onViewClick : function(view, doFocus, el, e)
17685 var index = this.view.getSelectedIndexes()[0];
17687 var r = this.store.getAt(index);
17691 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17698 Roo.each(this.tickItems, function(v,k){
17700 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17702 _this.tickItems.splice(k, 1);
17704 if(typeof(e) == 'undefined' && view == false){
17705 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17717 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17718 this.tickItems.push(r.data);
17721 if(typeof(e) == 'undefined' && view == false){
17722 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17729 this.onSelect(r, index);
17731 if(doFocus !== false && !this.blockFocus){
17732 this.inputEl().focus();
17737 restrictHeight : function(){
17738 //this.innerList.dom.style.height = '';
17739 //var inner = this.innerList.dom;
17740 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17741 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17742 //this.list.beginUpdate();
17743 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17744 this.list.alignTo(this.inputEl(), this.listAlign);
17745 this.list.alignTo(this.inputEl(), this.listAlign);
17746 //this.list.endUpdate();
17750 onEmptyResults : function(){
17752 if(this.tickable && this.editable){
17753 this.hasFocus = false;
17754 this.restrictHeight();
17762 * Returns true if the dropdown list is expanded, else false.
17764 isExpanded : function(){
17765 return this.list.isVisible();
17769 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17770 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17771 * @param {String} value The data value of the item to select
17772 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17773 * selected item if it is not currently in view (defaults to true)
17774 * @return {Boolean} True if the value matched an item in the list, else false
17776 selectByValue : function(v, scrollIntoView){
17777 if(v !== undefined && v !== null){
17778 var r = this.findRecord(this.valueField || this.displayField, v);
17780 this.select(this.store.indexOf(r), scrollIntoView);
17788 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17789 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17790 * @param {Number} index The zero-based index of the list item to select
17791 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17792 * selected item if it is not currently in view (defaults to true)
17794 select : function(index, scrollIntoView){
17795 this.selectedIndex = index;
17796 this.view.select(index);
17797 if(scrollIntoView !== false){
17798 var el = this.view.getNode(index);
17800 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17803 this.list.scrollChildIntoView(el, false);
17809 selectNext : function(){
17810 var ct = this.store.getCount();
17812 if(this.selectedIndex == -1){
17814 }else if(this.selectedIndex < ct-1){
17815 this.select(this.selectedIndex+1);
17821 selectPrev : function(){
17822 var ct = this.store.getCount();
17824 if(this.selectedIndex == -1){
17826 }else if(this.selectedIndex != 0){
17827 this.select(this.selectedIndex-1);
17833 onKeyUp : function(e){
17834 if(this.editable !== false && !e.isSpecialKey()){
17835 this.lastKey = e.getKey();
17836 this.dqTask.delay(this.queryDelay);
17841 validateBlur : function(){
17842 return !this.list || !this.list.isVisible();
17846 initQuery : function(){
17848 var v = this.getRawValue();
17850 if(this.tickable && this.editable){
17851 v = this.tickableInputEl().getValue();
17858 doForce : function(){
17859 if(this.inputEl().dom.value.length > 0){
17860 this.inputEl().dom.value =
17861 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17867 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17868 * query allowing the query action to be canceled if needed.
17869 * @param {String} query The SQL query to execute
17870 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17871 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17872 * saved in the current store (defaults to false)
17874 doQuery : function(q, forceAll){
17876 if(q === undefined || q === null){
17881 forceAll: forceAll,
17885 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17890 forceAll = qe.forceAll;
17891 if(forceAll === true || (q.length >= this.minChars)){
17893 this.hasQuery = true;
17895 if(this.lastQuery != q || this.alwaysQuery){
17896 this.lastQuery = q;
17897 if(this.mode == 'local'){
17898 this.selectedIndex = -1;
17900 this.store.clearFilter();
17903 if(this.specialFilter){
17904 this.fireEvent('specialfilter', this);
17909 this.store.filter(this.displayField, q);
17912 this.store.fireEvent("datachanged", this.store);
17919 this.store.baseParams[this.queryParam] = q;
17921 var options = {params : this.getParams(q)};
17924 options.add = true;
17925 options.params.start = this.page * this.pageSize;
17928 this.store.load(options);
17931 * this code will make the page width larger, at the beginning, the list not align correctly,
17932 * we should expand the list on onLoad
17933 * so command out it
17938 this.selectedIndex = -1;
17943 this.loadNext = false;
17947 getParams : function(q){
17949 //p[this.queryParam] = q;
17953 p.limit = this.pageSize;
17959 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17961 collapse : function(){
17962 if(!this.isExpanded()){
17968 this.hasFocus = false;
17972 this.cancelBtn.hide();
17973 this.trigger.show();
17976 this.tickableInputEl().dom.value = '';
17977 this.tickableInputEl().blur();
17982 Roo.get(document).un('mousedown', this.collapseIf, this);
17983 Roo.get(document).un('mousewheel', this.collapseIf, this);
17984 if (!this.editable) {
17985 Roo.get(document).un('keydown', this.listKeyPress, this);
17987 this.fireEvent('collapse', this);
17993 collapseIf : function(e){
17994 var in_combo = e.within(this.el);
17995 var in_list = e.within(this.list);
17996 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17998 if (in_combo || in_list || is_list) {
17999 //e.stopPropagation();
18004 this.onTickableFooterButtonClick(e, false, false);
18012 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18014 expand : function(){
18016 if(this.isExpanded() || !this.hasFocus){
18020 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18021 this.list.setWidth(lw);
18027 this.restrictHeight();
18031 this.tickItems = Roo.apply([], this.item);
18034 this.cancelBtn.show();
18035 this.trigger.hide();
18038 this.tickableInputEl().focus();
18043 Roo.get(document).on('mousedown', this.collapseIf, this);
18044 Roo.get(document).on('mousewheel', this.collapseIf, this);
18045 if (!this.editable) {
18046 Roo.get(document).on('keydown', this.listKeyPress, this);
18049 this.fireEvent('expand', this);
18053 // Implements the default empty TriggerField.onTriggerClick function
18054 onTriggerClick : function(e)
18056 Roo.log('trigger click');
18058 if(this.disabled || !this.triggerList){
18063 this.loadNext = false;
18065 if(this.isExpanded()){
18067 if (!this.blockFocus) {
18068 this.inputEl().focus();
18072 this.hasFocus = true;
18073 if(this.triggerAction == 'all') {
18074 this.doQuery(this.allQuery, true);
18076 this.doQuery(this.getRawValue());
18078 if (!this.blockFocus) {
18079 this.inputEl().focus();
18084 onTickableTriggerClick : function(e)
18091 this.loadNext = false;
18092 this.hasFocus = true;
18094 if(this.triggerAction == 'all') {
18095 this.doQuery(this.allQuery, true);
18097 this.doQuery(this.getRawValue());
18101 onSearchFieldClick : function(e)
18103 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18104 this.onTickableFooterButtonClick(e, false, false);
18108 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18113 this.loadNext = false;
18114 this.hasFocus = true;
18116 if(this.triggerAction == 'all') {
18117 this.doQuery(this.allQuery, true);
18119 this.doQuery(this.getRawValue());
18123 listKeyPress : function(e)
18125 //Roo.log('listkeypress');
18126 // scroll to first matching element based on key pres..
18127 if (e.isSpecialKey()) {
18130 var k = String.fromCharCode(e.getKey()).toUpperCase();
18133 var csel = this.view.getSelectedNodes();
18134 var cselitem = false;
18136 var ix = this.view.indexOf(csel[0]);
18137 cselitem = this.store.getAt(ix);
18138 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18144 this.store.each(function(v) {
18146 // start at existing selection.
18147 if (cselitem.id == v.id) {
18153 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18154 match = this.store.indexOf(v);
18160 if (match === false) {
18161 return true; // no more action?
18164 this.view.select(match);
18165 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18166 sn.scrollIntoView(sn.dom.parentNode, false);
18169 onViewScroll : function(e, t){
18171 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){
18175 this.hasQuery = true;
18177 this.loading = this.list.select('.loading', true).first();
18179 if(this.loading === null){
18180 this.list.createChild({
18182 cls: 'loading roo-select2-more-results roo-select2-active',
18183 html: 'Loading more results...'
18186 this.loading = this.list.select('.loading', true).first();
18188 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18190 this.loading.hide();
18193 this.loading.show();
18198 this.loadNext = true;
18200 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18205 addItem : function(o)
18207 var dv = ''; // display value
18209 if (this.displayField) {
18210 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18212 // this is an error condition!!!
18213 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18220 var choice = this.choices.createChild({
18222 cls: 'roo-select2-search-choice',
18231 cls: 'roo-select2-search-choice-close fa fa-times',
18236 }, this.searchField);
18238 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18240 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18248 this.inputEl().dom.value = '';
18253 onRemoveItem : function(e, _self, o)
18255 e.preventDefault();
18257 this.lastItem = Roo.apply([], this.item);
18259 var index = this.item.indexOf(o.data) * 1;
18262 Roo.log('not this item?!');
18266 this.item.splice(index, 1);
18271 this.fireEvent('remove', this, e);
18277 syncValue : function()
18279 if(!this.item.length){
18286 Roo.each(this.item, function(i){
18287 if(_this.valueField){
18288 value.push(i[_this.valueField]);
18295 this.value = value.join(',');
18297 if(this.hiddenField){
18298 this.hiddenField.dom.value = this.value;
18301 this.store.fireEvent("datachanged", this.store);
18306 clearItem : function()
18308 if(!this.multiple){
18314 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18322 if(this.tickable && !Roo.isTouch){
18323 this.view.refresh();
18327 inputEl: function ()
18329 if(Roo.isIOS && this.useNativeIOS){
18330 return this.el.select('select.roo-ios-select', true).first();
18333 if(Roo.isTouch && this.mobileTouchView){
18334 return this.el.select('input.form-control',true).first();
18338 return this.searchField;
18341 return this.el.select('input.form-control',true).first();
18344 onTickableFooterButtonClick : function(e, btn, el)
18346 e.preventDefault();
18348 this.lastItem = Roo.apply([], this.item);
18350 if(btn && btn.name == 'cancel'){
18351 this.tickItems = Roo.apply([], this.item);
18360 Roo.each(this.tickItems, function(o){
18368 validate : function()
18370 if(this.getVisibilityEl().hasClass('hidden')){
18374 var v = this.getRawValue();
18377 v = this.getValue();
18380 if(this.disabled || this.allowBlank || v.length){
18385 this.markInvalid();
18389 tickableInputEl : function()
18391 if(!this.tickable || !this.editable){
18392 return this.inputEl();
18395 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18399 getAutoCreateTouchView : function()
18404 cls: 'form-group' //input-group
18410 type : this.inputType,
18411 cls : 'form-control x-combo-noedit',
18412 autocomplete: 'new-password',
18413 placeholder : this.placeholder || '',
18418 input.name = this.name;
18422 input.cls += ' input-' + this.size;
18425 if (this.disabled) {
18426 input.disabled = true;
18430 cls : 'roo-combobox-wrap',
18437 inputblock.cls += ' input-group';
18439 inputblock.cn.unshift({
18441 cls : 'input-group-addon input-group-prepend input-group-text',
18446 if(this.removable && !this.multiple){
18447 inputblock.cls += ' roo-removable';
18449 inputblock.cn.push({
18452 cls : 'roo-combo-removable-btn close'
18456 if(this.hasFeedback && !this.allowBlank){
18458 inputblock.cls += ' has-feedback';
18460 inputblock.cn.push({
18462 cls: 'glyphicon form-control-feedback'
18469 inputblock.cls += (this.before) ? '' : ' input-group';
18471 inputblock.cn.push({
18473 cls : 'input-group-addon input-group-append input-group-text',
18479 var ibwrap = inputblock;
18484 cls: 'roo-select2-choices',
18488 cls: 'roo-select2-search-field',
18501 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18506 cls: 'form-hidden-field'
18512 if(!this.multiple && this.showToggleBtn){
18518 if (this.caret != false) {
18521 cls: 'fa fa-' + this.caret
18528 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18530 Roo.bootstrap.version == 3 ? caret : '',
18533 cls: 'combobox-clear',
18547 combobox.cls += ' roo-select2-container-multi';
18550 var required = this.allowBlank ? {
18552 style: 'display: none'
18555 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18556 tooltip : 'This field is required'
18559 var align = this.labelAlign || this.parentLabelAlign();
18561 if (align ==='left' && this.fieldLabel.length) {
18567 cls : 'control-label col-form-label',
18568 html : this.fieldLabel
18572 cls : 'roo-combobox-wrap ',
18579 var labelCfg = cfg.cn[1];
18580 var contentCfg = cfg.cn[2];
18583 if(this.indicatorpos == 'right'){
18588 cls : 'control-label col-form-label',
18592 html : this.fieldLabel
18598 cls : "roo-combobox-wrap ",
18606 labelCfg = cfg.cn[0];
18607 contentCfg = cfg.cn[1];
18612 if(this.labelWidth > 12){
18613 labelCfg.style = "width: " + this.labelWidth + 'px';
18616 if(this.labelWidth < 13 && this.labelmd == 0){
18617 this.labelmd = this.labelWidth;
18620 if(this.labellg > 0){
18621 labelCfg.cls += ' col-lg-' + this.labellg;
18622 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18625 if(this.labelmd > 0){
18626 labelCfg.cls += ' col-md-' + this.labelmd;
18627 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18630 if(this.labelsm > 0){
18631 labelCfg.cls += ' col-sm-' + this.labelsm;
18632 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18635 if(this.labelxs > 0){
18636 labelCfg.cls += ' col-xs-' + this.labelxs;
18637 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18641 } else if ( this.fieldLabel.length) {
18646 cls : 'control-label',
18647 html : this.fieldLabel
18658 if(this.indicatorpos == 'right'){
18662 cls : 'control-label',
18663 html : this.fieldLabel,
18681 var settings = this;
18683 ['xs','sm','md','lg'].map(function(size){
18684 if (settings[size]) {
18685 cfg.cls += ' col-' + size + '-' + settings[size];
18692 initTouchView : function()
18694 this.renderTouchView();
18696 this.touchViewEl.on('scroll', function(){
18697 this.el.dom.scrollTop = 0;
18700 this.originalValue = this.getValue();
18702 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18704 this.inputEl().on("click", this.showTouchView, this);
18705 if (this.triggerEl) {
18706 this.triggerEl.on("click", this.showTouchView, this);
18710 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18711 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18713 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18715 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18716 this.store.on('load', this.onTouchViewLoad, this);
18717 this.store.on('loadexception', this.onTouchViewLoadException, this);
18719 if(this.hiddenName){
18721 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18723 this.hiddenField.dom.value =
18724 this.hiddenValue !== undefined ? this.hiddenValue :
18725 this.value !== undefined ? this.value : '';
18727 this.el.dom.removeAttribute('name');
18728 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18732 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18733 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18736 if(this.removable && !this.multiple){
18737 var close = this.closeTriggerEl();
18739 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18740 close.on('click', this.removeBtnClick, this, close);
18744 * fix the bug in Safari iOS8
18746 this.inputEl().on("focus", function(e){
18747 document.activeElement.blur();
18750 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18757 renderTouchView : function()
18759 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18760 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18762 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18763 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18765 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18766 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18767 this.touchViewBodyEl.setStyle('overflow', 'auto');
18769 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18770 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18772 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18773 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18777 showTouchView : function()
18783 this.touchViewHeaderEl.hide();
18785 if(this.modalTitle.length){
18786 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18787 this.touchViewHeaderEl.show();
18790 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18791 this.touchViewEl.show();
18793 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18795 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18796 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18798 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18800 if(this.modalTitle.length){
18801 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18804 this.touchViewBodyEl.setHeight(bodyHeight);
18808 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18810 this.touchViewEl.addClass(['in','show']);
18813 if(this._touchViewMask){
18814 Roo.get(document.body).addClass("x-body-masked");
18815 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18816 this._touchViewMask.setStyle('z-index', 10000);
18817 this._touchViewMask.addClass('show');
18820 this.doTouchViewQuery();
18824 hideTouchView : function()
18826 this.touchViewEl.removeClass(['in','show']);
18830 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18832 this.touchViewEl.setStyle('display', 'none');
18835 if(this._touchViewMask){
18836 this._touchViewMask.removeClass('show');
18837 Roo.get(document.body).removeClass("x-body-masked");
18841 setTouchViewValue : function()
18848 Roo.each(this.tickItems, function(o){
18853 this.hideTouchView();
18856 doTouchViewQuery : function()
18865 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18869 if(!this.alwaysQuery || this.mode == 'local'){
18870 this.onTouchViewLoad();
18877 onTouchViewBeforeLoad : function(combo,opts)
18883 onTouchViewLoad : function()
18885 if(this.store.getCount() < 1){
18886 this.onTouchViewEmptyResults();
18890 this.clearTouchView();
18892 var rawValue = this.getRawValue();
18894 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18896 this.tickItems = [];
18898 this.store.data.each(function(d, rowIndex){
18899 var row = this.touchViewListGroup.createChild(template);
18901 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18902 row.addClass(d.data.cls);
18905 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18908 html : d.data[this.displayField]
18911 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18912 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18915 row.removeClass('selected');
18916 if(!this.multiple && this.valueField &&
18917 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18920 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18921 row.addClass('selected');
18924 if(this.multiple && this.valueField &&
18925 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18929 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18930 this.tickItems.push(d.data);
18933 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18937 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18939 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18941 if(this.modalTitle.length){
18942 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18945 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18947 if(this.mobile_restrict_height && listHeight < bodyHeight){
18948 this.touchViewBodyEl.setHeight(listHeight);
18953 if(firstChecked && listHeight > bodyHeight){
18954 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18959 onTouchViewLoadException : function()
18961 this.hideTouchView();
18964 onTouchViewEmptyResults : function()
18966 this.clearTouchView();
18968 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18970 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18974 clearTouchView : function()
18976 this.touchViewListGroup.dom.innerHTML = '';
18979 onTouchViewClick : function(e, el, o)
18981 e.preventDefault();
18984 var rowIndex = o.rowIndex;
18986 var r = this.store.getAt(rowIndex);
18988 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18990 if(!this.multiple){
18991 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18992 c.dom.removeAttribute('checked');
18995 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18997 this.setFromData(r.data);
18999 var close = this.closeTriggerEl();
19005 this.hideTouchView();
19007 this.fireEvent('select', this, r, rowIndex);
19012 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19013 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19014 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19018 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19019 this.addItem(r.data);
19020 this.tickItems.push(r.data);
19024 getAutoCreateNativeIOS : function()
19027 cls: 'form-group' //input-group,
19032 cls : 'roo-ios-select'
19036 combobox.name = this.name;
19039 if (this.disabled) {
19040 combobox.disabled = true;
19043 var settings = this;
19045 ['xs','sm','md','lg'].map(function(size){
19046 if (settings[size]) {
19047 cfg.cls += ' col-' + size + '-' + settings[size];
19057 initIOSView : function()
19059 this.store.on('load', this.onIOSViewLoad, this);
19064 onIOSViewLoad : function()
19066 if(this.store.getCount() < 1){
19070 this.clearIOSView();
19072 if(this.allowBlank) {
19074 var default_text = '-- SELECT --';
19076 if(this.placeholder.length){
19077 default_text = this.placeholder;
19080 if(this.emptyTitle.length){
19081 default_text += ' - ' + this.emptyTitle + ' -';
19084 var opt = this.inputEl().createChild({
19087 html : default_text
19091 o[this.valueField] = 0;
19092 o[this.displayField] = default_text;
19094 this.ios_options.push({
19101 this.store.data.each(function(d, rowIndex){
19105 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19106 html = d.data[this.displayField];
19111 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19112 value = d.data[this.valueField];
19121 if(this.value == d.data[this.valueField]){
19122 option['selected'] = true;
19125 var opt = this.inputEl().createChild(option);
19127 this.ios_options.push({
19134 this.inputEl().on('change', function(){
19135 this.fireEvent('select', this);
19140 clearIOSView: function()
19142 this.inputEl().dom.innerHTML = '';
19144 this.ios_options = [];
19147 setIOSValue: function(v)
19151 if(!this.ios_options){
19155 Roo.each(this.ios_options, function(opts){
19157 opts.el.dom.removeAttribute('selected');
19159 if(opts.data[this.valueField] != v){
19163 opts.el.dom.setAttribute('selected', true);
19169 * @cfg {Boolean} grow
19173 * @cfg {Number} growMin
19177 * @cfg {Number} growMax
19186 Roo.apply(Roo.bootstrap.ComboBox, {
19190 cls: 'modal-header',
19212 cls: 'list-group-item',
19216 cls: 'roo-combobox-list-group-item-value'
19220 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19234 listItemCheckbox : {
19236 cls: 'list-group-item',
19240 cls: 'roo-combobox-list-group-item-value'
19244 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19260 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19265 cls: 'modal-footer',
19273 cls: 'col-xs-6 text-left',
19276 cls: 'btn btn-danger roo-touch-view-cancel',
19282 cls: 'col-xs-6 text-right',
19285 cls: 'btn btn-success roo-touch-view-ok',
19296 Roo.apply(Roo.bootstrap.ComboBox, {
19298 touchViewTemplate : {
19300 cls: 'modal fade roo-combobox-touch-view',
19304 cls: 'modal-dialog',
19305 style : 'position:fixed', // we have to fix position....
19309 cls: 'modal-content',
19311 Roo.bootstrap.ComboBox.header,
19312 Roo.bootstrap.ComboBox.body,
19313 Roo.bootstrap.ComboBox.footer
19322 * Ext JS Library 1.1.1
19323 * Copyright(c) 2006-2007, Ext JS, LLC.
19325 * Originally Released Under LGPL - original licence link has changed is not relivant.
19328 * <script type="text/javascript">
19333 * @extends Roo.util.Observable
19334 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19335 * This class also supports single and multi selection modes. <br>
19336 * Create a data model bound view:
19338 var store = new Roo.data.Store(...);
19340 var view = new Roo.View({
19342 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19344 singleSelect: true,
19345 selectedClass: "ydataview-selected",
19349 // listen for node click?
19350 view.on("click", function(vw, index, node, e){
19351 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19355 dataModel.load("foobar.xml");
19357 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19359 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19360 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19362 * Note: old style constructor is still suported (container, template, config)
19365 * Create a new View
19366 * @param {Object} config The config object
19369 Roo.View = function(config, depreciated_tpl, depreciated_config){
19371 this.parent = false;
19373 if (typeof(depreciated_tpl) == 'undefined') {
19374 // new way.. - universal constructor.
19375 Roo.apply(this, config);
19376 this.el = Roo.get(this.el);
19379 this.el = Roo.get(config);
19380 this.tpl = depreciated_tpl;
19381 Roo.apply(this, depreciated_config);
19383 this.wrapEl = this.el.wrap().wrap();
19384 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19387 if(typeof(this.tpl) == "string"){
19388 this.tpl = new Roo.Template(this.tpl);
19390 // support xtype ctors..
19391 this.tpl = new Roo.factory(this.tpl, Roo);
19395 this.tpl.compile();
19400 * @event beforeclick
19401 * Fires before a click is processed. Returns false to cancel the default action.
19402 * @param {Roo.View} this
19403 * @param {Number} index The index of the target node
19404 * @param {HTMLElement} node The target node
19405 * @param {Roo.EventObject} e The raw event object
19407 "beforeclick" : true,
19410 * Fires when a template node is clicked.
19411 * @param {Roo.View} this
19412 * @param {Number} index The index of the target node
19413 * @param {HTMLElement} node The target node
19414 * @param {Roo.EventObject} e The raw event object
19419 * Fires when a template node is double clicked.
19420 * @param {Roo.View} this
19421 * @param {Number} index The index of the target node
19422 * @param {HTMLElement} node The target node
19423 * @param {Roo.EventObject} e The raw event object
19427 * @event contextmenu
19428 * Fires when a template node is right clicked.
19429 * @param {Roo.View} this
19430 * @param {Number} index The index of the target node
19431 * @param {HTMLElement} node The target node
19432 * @param {Roo.EventObject} e The raw event object
19434 "contextmenu" : true,
19436 * @event selectionchange
19437 * Fires when the selected nodes change.
19438 * @param {Roo.View} this
19439 * @param {Array} selections Array of the selected nodes
19441 "selectionchange" : true,
19444 * @event beforeselect
19445 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19446 * @param {Roo.View} this
19447 * @param {HTMLElement} node The node to be selected
19448 * @param {Array} selections Array of currently selected nodes
19450 "beforeselect" : true,
19452 * @event preparedata
19453 * Fires on every row to render, to allow you to change the data.
19454 * @param {Roo.View} this
19455 * @param {Object} data to be rendered (change this)
19457 "preparedata" : true
19465 "click": this.onClick,
19466 "dblclick": this.onDblClick,
19467 "contextmenu": this.onContextMenu,
19471 this.selections = [];
19473 this.cmp = new Roo.CompositeElementLite([]);
19475 this.store = Roo.factory(this.store, Roo.data);
19476 this.setStore(this.store, true);
19479 if ( this.footer && this.footer.xtype) {
19481 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19483 this.footer.dataSource = this.store;
19484 this.footer.container = fctr;
19485 this.footer = Roo.factory(this.footer, Roo);
19486 fctr.insertFirst(this.el);
19488 // this is a bit insane - as the paging toolbar seems to detach the el..
19489 // dom.parentNode.parentNode.parentNode
19490 // they get detached?
19494 Roo.View.superclass.constructor.call(this);
19499 Roo.extend(Roo.View, Roo.util.Observable, {
19502 * @cfg {Roo.data.Store} store Data store to load data from.
19507 * @cfg {String|Roo.Element} el The container element.
19512 * @cfg {String|Roo.Template} tpl The template used by this View
19516 * @cfg {String} dataName the named area of the template to use as the data area
19517 * Works with domtemplates roo-name="name"
19521 * @cfg {String} selectedClass The css class to add to selected nodes
19523 selectedClass : "x-view-selected",
19525 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19530 * @cfg {String} text to display on mask (default Loading)
19534 * @cfg {Boolean} multiSelect Allow multiple selection
19536 multiSelect : false,
19538 * @cfg {Boolean} singleSelect Allow single selection
19540 singleSelect: false,
19543 * @cfg {Boolean} toggleSelect - selecting
19545 toggleSelect : false,
19548 * @cfg {Boolean} tickable - selecting
19553 * Returns the element this view is bound to.
19554 * @return {Roo.Element}
19556 getEl : function(){
19557 return this.wrapEl;
19563 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19565 refresh : function(){
19566 //Roo.log('refresh');
19569 // if we are using something like 'domtemplate', then
19570 // the what gets used is:
19571 // t.applySubtemplate(NAME, data, wrapping data..)
19572 // the outer template then get' applied with
19573 // the store 'extra data'
19574 // and the body get's added to the
19575 // roo-name="data" node?
19576 // <span class='roo-tpl-{name}'></span> ?????
19580 this.clearSelections();
19581 this.el.update("");
19583 var records = this.store.getRange();
19584 if(records.length < 1) {
19586 // is this valid?? = should it render a template??
19588 this.el.update(this.emptyText);
19592 if (this.dataName) {
19593 this.el.update(t.apply(this.store.meta)); //????
19594 el = this.el.child('.roo-tpl-' + this.dataName);
19597 for(var i = 0, len = records.length; i < len; i++){
19598 var data = this.prepareData(records[i].data, i, records[i]);
19599 this.fireEvent("preparedata", this, data, i, records[i]);
19601 var d = Roo.apply({}, data);
19604 Roo.apply(d, {'roo-id' : Roo.id()});
19608 Roo.each(this.parent.item, function(item){
19609 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19612 Roo.apply(d, {'roo-data-checked' : 'checked'});
19616 html[html.length] = Roo.util.Format.trim(
19618 t.applySubtemplate(this.dataName, d, this.store.meta) :
19625 el.update(html.join(""));
19626 this.nodes = el.dom.childNodes;
19627 this.updateIndexes(0);
19632 * Function to override to reformat the data that is sent to
19633 * the template for each node.
19634 * DEPRICATED - use the preparedata event handler.
19635 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19636 * a JSON object for an UpdateManager bound view).
19638 prepareData : function(data, index, record)
19640 this.fireEvent("preparedata", this, data, index, record);
19644 onUpdate : function(ds, record){
19645 // Roo.log('on update');
19646 this.clearSelections();
19647 var index = this.store.indexOf(record);
19648 var n = this.nodes[index];
19649 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19650 n.parentNode.removeChild(n);
19651 this.updateIndexes(index, index);
19657 onAdd : function(ds, records, index)
19659 //Roo.log(['on Add', ds, records, index] );
19660 this.clearSelections();
19661 if(this.nodes.length == 0){
19665 var n = this.nodes[index];
19666 for(var i = 0, len = records.length; i < len; i++){
19667 var d = this.prepareData(records[i].data, i, records[i]);
19669 this.tpl.insertBefore(n, d);
19672 this.tpl.append(this.el, d);
19675 this.updateIndexes(index);
19678 onRemove : function(ds, record, index){
19679 // Roo.log('onRemove');
19680 this.clearSelections();
19681 var el = this.dataName ?
19682 this.el.child('.roo-tpl-' + this.dataName) :
19685 el.dom.removeChild(this.nodes[index]);
19686 this.updateIndexes(index);
19690 * Refresh an individual node.
19691 * @param {Number} index
19693 refreshNode : function(index){
19694 this.onUpdate(this.store, this.store.getAt(index));
19697 updateIndexes : function(startIndex, endIndex){
19698 var ns = this.nodes;
19699 startIndex = startIndex || 0;
19700 endIndex = endIndex || ns.length - 1;
19701 for(var i = startIndex; i <= endIndex; i++){
19702 ns[i].nodeIndex = i;
19707 * Changes the data store this view uses and refresh the view.
19708 * @param {Store} store
19710 setStore : function(store, initial){
19711 if(!initial && this.store){
19712 this.store.un("datachanged", this.refresh);
19713 this.store.un("add", this.onAdd);
19714 this.store.un("remove", this.onRemove);
19715 this.store.un("update", this.onUpdate);
19716 this.store.un("clear", this.refresh);
19717 this.store.un("beforeload", this.onBeforeLoad);
19718 this.store.un("load", this.onLoad);
19719 this.store.un("loadexception", this.onLoad);
19723 store.on("datachanged", this.refresh, this);
19724 store.on("add", this.onAdd, this);
19725 store.on("remove", this.onRemove, this);
19726 store.on("update", this.onUpdate, this);
19727 store.on("clear", this.refresh, this);
19728 store.on("beforeload", this.onBeforeLoad, this);
19729 store.on("load", this.onLoad, this);
19730 store.on("loadexception", this.onLoad, this);
19738 * onbeforeLoad - masks the loading area.
19741 onBeforeLoad : function(store,opts)
19743 //Roo.log('onBeforeLoad');
19745 this.el.update("");
19747 this.el.mask(this.mask ? this.mask : "Loading" );
19749 onLoad : function ()
19756 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19757 * @param {HTMLElement} node
19758 * @return {HTMLElement} The template node
19760 findItemFromChild : function(node){
19761 var el = this.dataName ?
19762 this.el.child('.roo-tpl-' + this.dataName,true) :
19765 if(!node || node.parentNode == el){
19768 var p = node.parentNode;
19769 while(p && p != el){
19770 if(p.parentNode == el){
19779 onClick : function(e){
19780 var item = this.findItemFromChild(e.getTarget());
19782 var index = this.indexOf(item);
19783 if(this.onItemClick(item, index, e) !== false){
19784 this.fireEvent("click", this, index, item, e);
19787 this.clearSelections();
19792 onContextMenu : function(e){
19793 var item = this.findItemFromChild(e.getTarget());
19795 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19800 onDblClick : function(e){
19801 var item = this.findItemFromChild(e.getTarget());
19803 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19807 onItemClick : function(item, index, e)
19809 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19812 if (this.toggleSelect) {
19813 var m = this.isSelected(item) ? 'unselect' : 'select';
19816 _t[m](item, true, false);
19819 if(this.multiSelect || this.singleSelect){
19820 if(this.multiSelect && e.shiftKey && this.lastSelection){
19821 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19823 this.select(item, this.multiSelect && e.ctrlKey);
19824 this.lastSelection = item;
19827 if(!this.tickable){
19828 e.preventDefault();
19836 * Get the number of selected nodes.
19839 getSelectionCount : function(){
19840 return this.selections.length;
19844 * Get the currently selected nodes.
19845 * @return {Array} An array of HTMLElements
19847 getSelectedNodes : function(){
19848 return this.selections;
19852 * Get the indexes of the selected nodes.
19855 getSelectedIndexes : function(){
19856 var indexes = [], s = this.selections;
19857 for(var i = 0, len = s.length; i < len; i++){
19858 indexes.push(s[i].nodeIndex);
19864 * Clear all selections
19865 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19867 clearSelections : function(suppressEvent){
19868 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19869 this.cmp.elements = this.selections;
19870 this.cmp.removeClass(this.selectedClass);
19871 this.selections = [];
19872 if(!suppressEvent){
19873 this.fireEvent("selectionchange", this, this.selections);
19879 * Returns true if the passed node is selected
19880 * @param {HTMLElement/Number} node The node or node index
19881 * @return {Boolean}
19883 isSelected : function(node){
19884 var s = this.selections;
19888 node = this.getNode(node);
19889 return s.indexOf(node) !== -1;
19894 * @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
19895 * @param {Boolean} keepExisting (optional) true to keep existing selections
19896 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19898 select : function(nodeInfo, keepExisting, suppressEvent){
19899 if(nodeInfo instanceof Array){
19901 this.clearSelections(true);
19903 for(var i = 0, len = nodeInfo.length; i < len; i++){
19904 this.select(nodeInfo[i], true, true);
19908 var node = this.getNode(nodeInfo);
19909 if(!node || this.isSelected(node)){
19910 return; // already selected.
19913 this.clearSelections(true);
19916 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19917 Roo.fly(node).addClass(this.selectedClass);
19918 this.selections.push(node);
19919 if(!suppressEvent){
19920 this.fireEvent("selectionchange", this, this.selections);
19928 * @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
19929 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19930 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19932 unselect : function(nodeInfo, keepExisting, suppressEvent)
19934 if(nodeInfo instanceof Array){
19935 Roo.each(this.selections, function(s) {
19936 this.unselect(s, nodeInfo);
19940 var node = this.getNode(nodeInfo);
19941 if(!node || !this.isSelected(node)){
19942 //Roo.log("not selected");
19943 return; // not selected.
19947 Roo.each(this.selections, function(s) {
19949 Roo.fly(node).removeClass(this.selectedClass);
19956 this.selections= ns;
19957 this.fireEvent("selectionchange", this, this.selections);
19961 * Gets a template node.
19962 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19963 * @return {HTMLElement} The node or null if it wasn't found
19965 getNode : function(nodeInfo){
19966 if(typeof nodeInfo == "string"){
19967 return document.getElementById(nodeInfo);
19968 }else if(typeof nodeInfo == "number"){
19969 return this.nodes[nodeInfo];
19975 * Gets a range template nodes.
19976 * @param {Number} startIndex
19977 * @param {Number} endIndex
19978 * @return {Array} An array of nodes
19980 getNodes : function(start, end){
19981 var ns = this.nodes;
19982 start = start || 0;
19983 end = typeof end == "undefined" ? ns.length - 1 : end;
19986 for(var i = start; i <= end; i++){
19990 for(var i = start; i >= end; i--){
19998 * Finds the index of the passed node
19999 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20000 * @return {Number} The index of the node or -1
20002 indexOf : function(node){
20003 node = this.getNode(node);
20004 if(typeof node.nodeIndex == "number"){
20005 return node.nodeIndex;
20007 var ns = this.nodes;
20008 for(var i = 0, len = ns.length; i < len; i++){
20019 * based on jquery fullcalendar
20023 Roo.bootstrap = Roo.bootstrap || {};
20025 * @class Roo.bootstrap.Calendar
20026 * @extends Roo.bootstrap.Component
20027 * Bootstrap Calendar class
20028 * @cfg {Boolean} loadMask (true|false) default false
20029 * @cfg {Object} header generate the user specific header of the calendar, default false
20032 * Create a new Container
20033 * @param {Object} config The config object
20038 Roo.bootstrap.Calendar = function(config){
20039 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20043 * Fires when a date is selected
20044 * @param {DatePicker} this
20045 * @param {Date} date The selected date
20049 * @event monthchange
20050 * Fires when the displayed month changes
20051 * @param {DatePicker} this
20052 * @param {Date} date The selected month
20054 'monthchange': true,
20056 * @event evententer
20057 * Fires when mouse over an event
20058 * @param {Calendar} this
20059 * @param {event} Event
20061 'evententer': true,
20063 * @event eventleave
20064 * Fires when the mouse leaves an
20065 * @param {Calendar} this
20068 'eventleave': true,
20070 * @event eventclick
20071 * Fires when the mouse click an
20072 * @param {Calendar} this
20081 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20084 * @cfg {Number} startDay
20085 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20093 getAutoCreate : function(){
20096 var fc_button = function(name, corner, style, content ) {
20097 return Roo.apply({},{
20099 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20101 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20104 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20115 style : 'width:100%',
20122 cls : 'fc-header-left',
20124 fc_button('prev', 'left', 'arrow', '‹' ),
20125 fc_button('next', 'right', 'arrow', '›' ),
20126 { tag: 'span', cls: 'fc-header-space' },
20127 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20135 cls : 'fc-header-center',
20139 cls: 'fc-header-title',
20142 html : 'month / year'
20150 cls : 'fc-header-right',
20152 /* fc_button('month', 'left', '', 'month' ),
20153 fc_button('week', '', '', 'week' ),
20154 fc_button('day', 'right', '', 'day' )
20166 header = this.header;
20169 var cal_heads = function() {
20171 // fixme - handle this.
20173 for (var i =0; i < Date.dayNames.length; i++) {
20174 var d = Date.dayNames[i];
20177 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20178 html : d.substring(0,3)
20182 ret[0].cls += ' fc-first';
20183 ret[6].cls += ' fc-last';
20186 var cal_cell = function(n) {
20189 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20194 cls: 'fc-day-number',
20198 cls: 'fc-day-content',
20202 style: 'position: relative;' // height: 17px;
20214 var cal_rows = function() {
20217 for (var r = 0; r < 6; r++) {
20224 for (var i =0; i < Date.dayNames.length; i++) {
20225 var d = Date.dayNames[i];
20226 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20229 row.cn[0].cls+=' fc-first';
20230 row.cn[0].cn[0].style = 'min-height:90px';
20231 row.cn[6].cls+=' fc-last';
20235 ret[0].cls += ' fc-first';
20236 ret[4].cls += ' fc-prev-last';
20237 ret[5].cls += ' fc-last';
20244 cls: 'fc-border-separate',
20245 style : 'width:100%',
20253 cls : 'fc-first fc-last',
20271 cls : 'fc-content',
20272 style : "position: relative;",
20275 cls : 'fc-view fc-view-month fc-grid',
20276 style : 'position: relative',
20277 unselectable : 'on',
20280 cls : 'fc-event-container',
20281 style : 'position:absolute;z-index:8;top:0;left:0;'
20299 initEvents : function()
20302 throw "can not find store for calendar";
20308 style: "text-align:center",
20312 style: "background-color:white;width:50%;margin:250 auto",
20316 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20327 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20329 var size = this.el.select('.fc-content', true).first().getSize();
20330 this.maskEl.setSize(size.width, size.height);
20331 this.maskEl.enableDisplayMode("block");
20332 if(!this.loadMask){
20333 this.maskEl.hide();
20336 this.store = Roo.factory(this.store, Roo.data);
20337 this.store.on('load', this.onLoad, this);
20338 this.store.on('beforeload', this.onBeforeLoad, this);
20342 this.cells = this.el.select('.fc-day',true);
20343 //Roo.log(this.cells);
20344 this.textNodes = this.el.query('.fc-day-number');
20345 this.cells.addClassOnOver('fc-state-hover');
20347 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20348 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20349 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20350 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20352 this.on('monthchange', this.onMonthChange, this);
20354 this.update(new Date().clearTime());
20357 resize : function() {
20358 var sz = this.el.getSize();
20360 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20361 this.el.select('.fc-day-content div',true).setHeight(34);
20366 showPrevMonth : function(e){
20367 this.update(this.activeDate.add("mo", -1));
20369 showToday : function(e){
20370 this.update(new Date().clearTime());
20373 showNextMonth : function(e){
20374 this.update(this.activeDate.add("mo", 1));
20378 showPrevYear : function(){
20379 this.update(this.activeDate.add("y", -1));
20383 showNextYear : function(){
20384 this.update(this.activeDate.add("y", 1));
20389 update : function(date)
20391 var vd = this.activeDate;
20392 this.activeDate = date;
20393 // if(vd && this.el){
20394 // var t = date.getTime();
20395 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20396 // Roo.log('using add remove');
20398 // this.fireEvent('monthchange', this, date);
20400 // this.cells.removeClass("fc-state-highlight");
20401 // this.cells.each(function(c){
20402 // if(c.dateValue == t){
20403 // c.addClass("fc-state-highlight");
20404 // setTimeout(function(){
20405 // try{c.dom.firstChild.focus();}catch(e){}
20415 var days = date.getDaysInMonth();
20417 var firstOfMonth = date.getFirstDateOfMonth();
20418 var startingPos = firstOfMonth.getDay()-this.startDay;
20420 if(startingPos < this.startDay){
20424 var pm = date.add(Date.MONTH, -1);
20425 var prevStart = pm.getDaysInMonth()-startingPos;
20427 this.cells = this.el.select('.fc-day',true);
20428 this.textNodes = this.el.query('.fc-day-number');
20429 this.cells.addClassOnOver('fc-state-hover');
20431 var cells = this.cells.elements;
20432 var textEls = this.textNodes;
20434 Roo.each(cells, function(cell){
20435 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20438 days += startingPos;
20440 // convert everything to numbers so it's fast
20441 var day = 86400000;
20442 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20445 //Roo.log(prevStart);
20447 var today = new Date().clearTime().getTime();
20448 var sel = date.clearTime().getTime();
20449 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20450 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20451 var ddMatch = this.disabledDatesRE;
20452 var ddText = this.disabledDatesText;
20453 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20454 var ddaysText = this.disabledDaysText;
20455 var format = this.format;
20457 var setCellClass = function(cal, cell){
20461 //Roo.log('set Cell Class');
20463 var t = d.getTime();
20467 cell.dateValue = t;
20469 cell.className += " fc-today";
20470 cell.className += " fc-state-highlight";
20471 cell.title = cal.todayText;
20474 // disable highlight in other month..
20475 //cell.className += " fc-state-highlight";
20480 cell.className = " fc-state-disabled";
20481 cell.title = cal.minText;
20485 cell.className = " fc-state-disabled";
20486 cell.title = cal.maxText;
20490 if(ddays.indexOf(d.getDay()) != -1){
20491 cell.title = ddaysText;
20492 cell.className = " fc-state-disabled";
20495 if(ddMatch && format){
20496 var fvalue = d.dateFormat(format);
20497 if(ddMatch.test(fvalue)){
20498 cell.title = ddText.replace("%0", fvalue);
20499 cell.className = " fc-state-disabled";
20503 if (!cell.initialClassName) {
20504 cell.initialClassName = cell.dom.className;
20507 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20512 for(; i < startingPos; i++) {
20513 textEls[i].innerHTML = (++prevStart);
20514 d.setDate(d.getDate()+1);
20516 cells[i].className = "fc-past fc-other-month";
20517 setCellClass(this, cells[i]);
20522 for(; i < days; i++){
20523 intDay = i - startingPos + 1;
20524 textEls[i].innerHTML = (intDay);
20525 d.setDate(d.getDate()+1);
20527 cells[i].className = ''; // "x-date-active";
20528 setCellClass(this, cells[i]);
20532 for(; i < 42; i++) {
20533 textEls[i].innerHTML = (++extraDays);
20534 d.setDate(d.getDate()+1);
20536 cells[i].className = "fc-future fc-other-month";
20537 setCellClass(this, cells[i]);
20540 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20542 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20544 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20545 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20547 if(totalRows != 6){
20548 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20549 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20552 this.fireEvent('monthchange', this, date);
20556 if(!this.internalRender){
20557 var main = this.el.dom.firstChild;
20558 var w = main.offsetWidth;
20559 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20560 Roo.fly(main).setWidth(w);
20561 this.internalRender = true;
20562 // opera does not respect the auto grow header center column
20563 // then, after it gets a width opera refuses to recalculate
20564 // without a second pass
20565 if(Roo.isOpera && !this.secondPass){
20566 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20567 this.secondPass = true;
20568 this.update.defer(10, this, [date]);
20575 findCell : function(dt) {
20576 dt = dt.clearTime().getTime();
20578 this.cells.each(function(c){
20579 //Roo.log("check " +c.dateValue + '?=' + dt);
20580 if(c.dateValue == dt){
20590 findCells : function(ev) {
20591 var s = ev.start.clone().clearTime().getTime();
20593 var e= ev.end.clone().clearTime().getTime();
20596 this.cells.each(function(c){
20597 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20599 if(c.dateValue > e){
20602 if(c.dateValue < s){
20611 // findBestRow: function(cells)
20615 // for (var i =0 ; i < cells.length;i++) {
20616 // ret = Math.max(cells[i].rows || 0,ret);
20623 addItem : function(ev)
20625 // look for vertical location slot in
20626 var cells = this.findCells(ev);
20628 // ev.row = this.findBestRow(cells);
20630 // work out the location.
20634 for(var i =0; i < cells.length; i++) {
20636 cells[i].row = cells[0].row;
20639 cells[i].row = cells[i].row + 1;
20649 if (crow.start.getY() == cells[i].getY()) {
20651 crow.end = cells[i];
20668 cells[0].events.push(ev);
20670 this.calevents.push(ev);
20673 clearEvents: function() {
20675 if(!this.calevents){
20679 Roo.each(this.cells.elements, function(c){
20685 Roo.each(this.calevents, function(e) {
20686 Roo.each(e.els, function(el) {
20687 el.un('mouseenter' ,this.onEventEnter, this);
20688 el.un('mouseleave' ,this.onEventLeave, this);
20693 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20699 renderEvents: function()
20703 this.cells.each(function(c) {
20712 if(c.row != c.events.length){
20713 r = 4 - (4 - (c.row - c.events.length));
20716 c.events = ev.slice(0, r);
20717 c.more = ev.slice(r);
20719 if(c.more.length && c.more.length == 1){
20720 c.events.push(c.more.pop());
20723 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20727 this.cells.each(function(c) {
20729 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20732 for (var e = 0; e < c.events.length; e++){
20733 var ev = c.events[e];
20734 var rows = ev.rows;
20736 for(var i = 0; i < rows.length; i++) {
20738 // how many rows should it span..
20741 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20742 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20744 unselectable : "on",
20747 cls: 'fc-event-inner',
20751 // cls: 'fc-event-time',
20752 // html : cells.length > 1 ? '' : ev.time
20756 cls: 'fc-event-title',
20757 html : String.format('{0}', ev.title)
20764 cls: 'ui-resizable-handle ui-resizable-e',
20765 html : '  '
20772 cfg.cls += ' fc-event-start';
20774 if ((i+1) == rows.length) {
20775 cfg.cls += ' fc-event-end';
20778 var ctr = _this.el.select('.fc-event-container',true).first();
20779 var cg = ctr.createChild(cfg);
20781 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20782 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20784 var r = (c.more.length) ? 1 : 0;
20785 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20786 cg.setWidth(ebox.right - sbox.x -2);
20788 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20789 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20790 cg.on('click', _this.onEventClick, _this, ev);
20801 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20802 style : 'position: absolute',
20803 unselectable : "on",
20806 cls: 'fc-event-inner',
20810 cls: 'fc-event-title',
20818 cls: 'ui-resizable-handle ui-resizable-e',
20819 html : '  '
20825 var ctr = _this.el.select('.fc-event-container',true).first();
20826 var cg = ctr.createChild(cfg);
20828 var sbox = c.select('.fc-day-content',true).first().getBox();
20829 var ebox = c.select('.fc-day-content',true).first().getBox();
20831 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20832 cg.setWidth(ebox.right - sbox.x -2);
20834 cg.on('click', _this.onMoreEventClick, _this, c.more);
20844 onEventEnter: function (e, el,event,d) {
20845 this.fireEvent('evententer', this, el, event);
20848 onEventLeave: function (e, el,event,d) {
20849 this.fireEvent('eventleave', this, el, event);
20852 onEventClick: function (e, el,event,d) {
20853 this.fireEvent('eventclick', this, el, event);
20856 onMonthChange: function () {
20860 onMoreEventClick: function(e, el, more)
20864 this.calpopover.placement = 'right';
20865 this.calpopover.setTitle('More');
20867 this.calpopover.setContent('');
20869 var ctr = this.calpopover.el.select('.popover-content', true).first();
20871 Roo.each(more, function(m){
20873 cls : 'fc-event-hori fc-event-draggable',
20876 var cg = ctr.createChild(cfg);
20878 cg.on('click', _this.onEventClick, _this, m);
20881 this.calpopover.show(el);
20886 onLoad: function ()
20888 this.calevents = [];
20891 if(this.store.getCount() > 0){
20892 this.store.data.each(function(d){
20895 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20896 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20897 time : d.data.start_time,
20898 title : d.data.title,
20899 description : d.data.description,
20900 venue : d.data.venue
20905 this.renderEvents();
20907 if(this.calevents.length && this.loadMask){
20908 this.maskEl.hide();
20912 onBeforeLoad: function()
20914 this.clearEvents();
20916 this.maskEl.show();
20930 * @class Roo.bootstrap.Popover
20931 * @extends Roo.bootstrap.Component
20932 * Bootstrap Popover class
20933 * @cfg {String} html contents of the popover (or false to use children..)
20934 * @cfg {String} title of popover (or false to hide)
20935 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20936 * @cfg {String} trigger click || hover (or false to trigger manually)
20937 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20938 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20939 * - if false and it has a 'parent' then it will be automatically added to that element
20940 * - if string - Roo.get will be called
20941 * @cfg {Number} delay - delay before showing
20944 * Create a new Popover
20945 * @param {Object} config The config object
20948 Roo.bootstrap.Popover = function(config){
20949 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20955 * After the popover show
20957 * @param {Roo.bootstrap.Popover} this
20962 * After the popover hide
20964 * @param {Roo.bootstrap.Popover} this
20970 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20975 placement : 'right',
20976 trigger : 'hover', // hover
20982 can_build_overlaid : false,
20984 maskEl : false, // the mask element
20987 alignEl : false, // when show is called with an element - this get's stored.
20989 getChildContainer : function()
20991 return this.contentEl;
20994 getPopoverHeader : function()
20996 this.title = true; // flag not to hide it..
20997 this.headerEl.addClass('p-0');
20998 return this.headerEl
21002 getAutoCreate : function(){
21005 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21006 style: 'display:block',
21012 cls : 'popover-inner ',
21016 cls: 'popover-title popover-header',
21017 html : this.title === false ? '' : this.title
21020 cls : 'popover-content popover-body ' + (this.cls || ''),
21021 html : this.html || ''
21032 * @param {string} the title
21034 setTitle: function(str)
21038 this.headerEl.dom.innerHTML = str;
21043 * @param {string} the body content
21045 setContent: function(str)
21048 if (this.contentEl) {
21049 this.contentEl.dom.innerHTML = str;
21053 // as it get's added to the bottom of the page.
21054 onRender : function(ct, position)
21056 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21061 var cfg = Roo.apply({}, this.getAutoCreate());
21065 cfg.cls += ' ' + this.cls;
21068 cfg.style = this.style;
21070 //Roo.log("adding to ");
21071 this.el = Roo.get(document.body).createChild(cfg, position);
21072 // Roo.log(this.el);
21075 this.contentEl = this.el.select('.popover-content',true).first();
21076 this.headerEl = this.el.select('.popover-title',true).first();
21079 if(typeof(this.items) != 'undefined'){
21080 var items = this.items;
21083 for(var i =0;i < items.length;i++) {
21084 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21088 this.items = nitems;
21090 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21091 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21098 resizeMask : function()
21100 this.maskEl.setSize(
21101 Roo.lib.Dom.getViewWidth(true),
21102 Roo.lib.Dom.getViewHeight(true)
21106 initEvents : function()
21110 Roo.bootstrap.Popover.register(this);
21113 this.arrowEl = this.el.select('.arrow',true).first();
21114 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21115 this.el.enableDisplayMode('block');
21119 if (this.over === false && !this.parent()) {
21122 if (this.triggers === false) {
21127 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21128 var triggers = this.trigger ? this.trigger.split(' ') : [];
21129 Roo.each(triggers, function(trigger) {
21131 if (trigger == 'click') {
21132 on_el.on('click', this.toggle, this);
21133 } else if (trigger != 'manual') {
21134 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21135 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21137 on_el.on(eventIn ,this.enter, this);
21138 on_el.on(eventOut, this.leave, this);
21148 toggle : function () {
21149 this.hoverState == 'in' ? this.leave() : this.enter();
21152 enter : function () {
21154 clearTimeout(this.timeout);
21156 this.hoverState = 'in';
21158 if (!this.delay || !this.delay.show) {
21163 this.timeout = setTimeout(function () {
21164 if (_t.hoverState == 'in') {
21167 }, this.delay.show)
21170 leave : function() {
21171 clearTimeout(this.timeout);
21173 this.hoverState = 'out';
21175 if (!this.delay || !this.delay.hide) {
21180 this.timeout = setTimeout(function () {
21181 if (_t.hoverState == 'out') {
21184 }, this.delay.hide)
21188 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21189 * @param {string} (left|right|top|bottom) position
21191 show : function (on_el, placement)
21193 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21194 on_el = on_el || false; // default to false
21197 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21198 on_el = this.parent().el;
21199 } else if (this.over) {
21200 on_el = Roo.get(this.over);
21205 this.alignEl = Roo.get( on_el );
21208 this.render(document.body);
21214 if (this.title === false) {
21215 this.headerEl.hide();
21220 this.el.dom.style.display = 'block';
21223 if (this.alignEl) {
21224 this.updatePosition(this.placement, true);
21227 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21228 var es = this.el.getSize();
21229 var x = Roo.lib.Dom.getViewWidth()/2;
21230 var y = Roo.lib.Dom.getViewHeight()/2;
21231 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21236 //var arrow = this.el.select('.arrow',true).first();
21237 //arrow.set(align[2],
21239 this.el.addClass('in');
21243 this.hoverState = 'in';
21246 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21247 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21248 this.maskEl.dom.style.display = 'block';
21249 this.maskEl.addClass('show');
21251 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21253 this.fireEvent('show', this);
21257 * fire this manually after loading a grid in the table for example
21258 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21259 * @param {Boolean} try and move it if we cant get right position.
21261 updatePosition : function(placement, try_move)
21263 // allow for calling with no parameters
21264 placement = placement ? placement : this.placement;
21265 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21267 this.el.removeClass([
21268 'fade','top','bottom', 'left', 'right','in',
21269 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21271 this.el.addClass(placement + ' bs-popover-' + placement);
21273 if (!this.alignEl ) {
21277 switch (placement) {
21279 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21280 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21281 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21282 //normal display... or moved up/down.
21283 this.el.setXY(offset);
21284 var xy = this.alignEl.getAnchorXY('tr', false);
21286 this.arrowEl.setXY(xy);
21289 // continue through...
21290 return this.updatePosition('left', false);
21294 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21295 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21296 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21297 //normal display... or moved up/down.
21298 this.el.setXY(offset);
21299 var xy = this.alignEl.getAnchorXY('tl', false);
21300 xy[0]-=10;xy[1]+=5; // << fix me
21301 this.arrowEl.setXY(xy);
21305 return this.updatePosition('right', false);
21308 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21309 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21310 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21311 //normal display... or moved up/down.
21312 this.el.setXY(offset);
21313 var xy = this.alignEl.getAnchorXY('t', false);
21314 xy[1]-=10; // << fix me
21315 this.arrowEl.setXY(xy);
21319 return this.updatePosition('bottom', false);
21322 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21323 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21324 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21325 //normal display... or moved up/down.
21326 this.el.setXY(offset);
21327 var xy = this.alignEl.getAnchorXY('b', false);
21328 xy[1]+=2; // << fix me
21329 this.arrowEl.setXY(xy);
21333 return this.updatePosition('top', false);
21344 this.el.setXY([0,0]);
21345 this.el.removeClass('in');
21347 this.hoverState = null;
21348 this.maskEl.hide(); // always..
21349 this.fireEvent('hide', this);
21355 Roo.apply(Roo.bootstrap.Popover, {
21358 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21359 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21360 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21361 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21366 clickHander : false,
21370 onMouseDown : function(e)
21372 if (this.popups.length && !e.getTarget(".roo-popover")) {
21373 /// what is nothing is showing..
21382 register : function(popup)
21384 if (!Roo.bootstrap.Popover.clickHandler) {
21385 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21387 // hide other popups.
21388 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21389 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21390 this.hideAll(); //<< why?
21391 //this.popups.push(popup);
21393 hideAll : function()
21395 this.popups.forEach(function(p) {
21399 onShow : function() {
21400 Roo.bootstrap.Popover.popups.push(this);
21402 onHide : function() {
21403 Roo.bootstrap.Popover.popups.remove(this);
21409 * Card header - holder for the card header elements.
21414 * @class Roo.bootstrap.PopoverNav
21415 * @extends Roo.bootstrap.NavGroup
21416 * Bootstrap Popover header navigation class
21418 * Create a new Popover Header Navigation
21419 * @param {Object} config The config object
21422 Roo.bootstrap.PopoverNav = function(config){
21423 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21426 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21429 container_method : 'getPopoverHeader'
21447 * @class Roo.bootstrap.Progress
21448 * @extends Roo.bootstrap.Component
21449 * Bootstrap Progress class
21450 * @cfg {Boolean} striped striped of the progress bar
21451 * @cfg {Boolean} active animated of the progress bar
21455 * Create a new Progress
21456 * @param {Object} config The config object
21459 Roo.bootstrap.Progress = function(config){
21460 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21463 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21468 getAutoCreate : function(){
21476 cfg.cls += ' progress-striped';
21480 cfg.cls += ' active';
21499 * @class Roo.bootstrap.ProgressBar
21500 * @extends Roo.bootstrap.Component
21501 * Bootstrap ProgressBar class
21502 * @cfg {Number} aria_valuenow aria-value now
21503 * @cfg {Number} aria_valuemin aria-value min
21504 * @cfg {Number} aria_valuemax aria-value max
21505 * @cfg {String} label label for the progress bar
21506 * @cfg {String} panel (success | info | warning | danger )
21507 * @cfg {String} role role of the progress bar
21508 * @cfg {String} sr_only text
21512 * Create a new ProgressBar
21513 * @param {Object} config The config object
21516 Roo.bootstrap.ProgressBar = function(config){
21517 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21520 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21524 aria_valuemax : 100,
21530 getAutoCreate : function()
21535 cls: 'progress-bar',
21536 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21548 cfg.role = this.role;
21551 if(this.aria_valuenow){
21552 cfg['aria-valuenow'] = this.aria_valuenow;
21555 if(this.aria_valuemin){
21556 cfg['aria-valuemin'] = this.aria_valuemin;
21559 if(this.aria_valuemax){
21560 cfg['aria-valuemax'] = this.aria_valuemax;
21563 if(this.label && !this.sr_only){
21564 cfg.html = this.label;
21568 cfg.cls += ' progress-bar-' + this.panel;
21574 update : function(aria_valuenow)
21576 this.aria_valuenow = aria_valuenow;
21578 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21593 * @class Roo.bootstrap.TabGroup
21594 * @extends Roo.bootstrap.Column
21595 * Bootstrap Column class
21596 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21597 * @cfg {Boolean} carousel true to make the group behave like a carousel
21598 * @cfg {Boolean} bullets show bullets for the panels
21599 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21600 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21601 * @cfg {Boolean} showarrow (true|false) show arrow default true
21604 * Create a new TabGroup
21605 * @param {Object} config The config object
21608 Roo.bootstrap.TabGroup = function(config){
21609 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21611 this.navId = Roo.id();
21614 Roo.bootstrap.TabGroup.register(this);
21618 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21621 transition : false,
21626 slideOnTouch : false,
21629 getAutoCreate : function()
21631 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21633 cfg.cls += ' tab-content';
21635 if (this.carousel) {
21636 cfg.cls += ' carousel slide';
21639 cls : 'carousel-inner',
21643 if(this.bullets && !Roo.isTouch){
21646 cls : 'carousel-bullets',
21650 if(this.bullets_cls){
21651 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21658 cfg.cn[0].cn.push(bullets);
21661 if(this.showarrow){
21662 cfg.cn[0].cn.push({
21664 class : 'carousel-arrow',
21668 class : 'carousel-prev',
21672 class : 'fa fa-chevron-left'
21678 class : 'carousel-next',
21682 class : 'fa fa-chevron-right'
21695 initEvents: function()
21697 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21698 // this.el.on("touchstart", this.onTouchStart, this);
21701 if(this.autoslide){
21704 this.slideFn = window.setInterval(function() {
21705 _this.showPanelNext();
21709 if(this.showarrow){
21710 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21711 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21717 // onTouchStart : function(e, el, o)
21719 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21723 // this.showPanelNext();
21727 getChildContainer : function()
21729 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21733 * register a Navigation item
21734 * @param {Roo.bootstrap.NavItem} the navitem to add
21736 register : function(item)
21738 this.tabs.push( item);
21739 item.navId = this.navId; // not really needed..
21744 getActivePanel : function()
21747 Roo.each(this.tabs, function(t) {
21757 getPanelByName : function(n)
21760 Roo.each(this.tabs, function(t) {
21761 if (t.tabId == n) {
21769 indexOfPanel : function(p)
21772 Roo.each(this.tabs, function(t,i) {
21773 if (t.tabId == p.tabId) {
21782 * show a specific panel
21783 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21784 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21786 showPanel : function (pan)
21788 if(this.transition || typeof(pan) == 'undefined'){
21789 Roo.log("waiting for the transitionend");
21793 if (typeof(pan) == 'number') {
21794 pan = this.tabs[pan];
21797 if (typeof(pan) == 'string') {
21798 pan = this.getPanelByName(pan);
21801 var cur = this.getActivePanel();
21804 Roo.log('pan or acitve pan is undefined');
21808 if (pan.tabId == this.getActivePanel().tabId) {
21812 if (false === cur.fireEvent('beforedeactivate')) {
21816 if(this.bullets > 0 && !Roo.isTouch){
21817 this.setActiveBullet(this.indexOfPanel(pan));
21820 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21822 //class="carousel-item carousel-item-next carousel-item-left"
21824 this.transition = true;
21825 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21826 var lr = dir == 'next' ? 'left' : 'right';
21827 pan.el.addClass(dir); // or prev
21828 pan.el.addClass('carousel-item-' + dir); // or prev
21829 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21830 cur.el.addClass(lr); // or right
21831 pan.el.addClass(lr);
21832 cur.el.addClass('carousel-item-' +lr); // or right
21833 pan.el.addClass('carousel-item-' +lr);
21837 cur.el.on('transitionend', function() {
21838 Roo.log("trans end?");
21840 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21841 pan.setActive(true);
21843 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21844 cur.setActive(false);
21846 _this.transition = false;
21848 }, this, { single: true } );
21853 cur.setActive(false);
21854 pan.setActive(true);
21859 showPanelNext : function()
21861 var i = this.indexOfPanel(this.getActivePanel());
21863 if (i >= this.tabs.length - 1 && !this.autoslide) {
21867 if (i >= this.tabs.length - 1 && this.autoslide) {
21871 this.showPanel(this.tabs[i+1]);
21874 showPanelPrev : function()
21876 var i = this.indexOfPanel(this.getActivePanel());
21878 if (i < 1 && !this.autoslide) {
21882 if (i < 1 && this.autoslide) {
21883 i = this.tabs.length;
21886 this.showPanel(this.tabs[i-1]);
21890 addBullet: function()
21892 if(!this.bullets || Roo.isTouch){
21895 var ctr = this.el.select('.carousel-bullets',true).first();
21896 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21897 var bullet = ctr.createChild({
21898 cls : 'bullet bullet-' + i
21899 },ctr.dom.lastChild);
21904 bullet.on('click', (function(e, el, o, ii, t){
21906 e.preventDefault();
21908 this.showPanel(ii);
21910 if(this.autoslide && this.slideFn){
21911 clearInterval(this.slideFn);
21912 this.slideFn = window.setInterval(function() {
21913 _this.showPanelNext();
21917 }).createDelegate(this, [i, bullet], true));
21922 setActiveBullet : function(i)
21928 Roo.each(this.el.select('.bullet', true).elements, function(el){
21929 el.removeClass('selected');
21932 var bullet = this.el.select('.bullet-' + i, true).first();
21938 bullet.addClass('selected');
21949 Roo.apply(Roo.bootstrap.TabGroup, {
21953 * register a Navigation Group
21954 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21956 register : function(navgrp)
21958 this.groups[navgrp.navId] = navgrp;
21962 * fetch a Navigation Group based on the navigation ID
21963 * if one does not exist , it will get created.
21964 * @param {string} the navgroup to add
21965 * @returns {Roo.bootstrap.NavGroup} the navgroup
21967 get: function(navId) {
21968 if (typeof(this.groups[navId]) == 'undefined') {
21969 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21971 return this.groups[navId] ;
21986 * @class Roo.bootstrap.TabPanel
21987 * @extends Roo.bootstrap.Component
21988 * Bootstrap TabPanel class
21989 * @cfg {Boolean} active panel active
21990 * @cfg {String} html panel content
21991 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21992 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21993 * @cfg {String} href click to link..
21994 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21998 * Create a new TabPanel
21999 * @param {Object} config The config object
22002 Roo.bootstrap.TabPanel = function(config){
22003 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22007 * Fires when the active status changes
22008 * @param {Roo.bootstrap.TabPanel} this
22009 * @param {Boolean} state the new state
22014 * @event beforedeactivate
22015 * Fires before a tab is de-activated - can be used to do validation on a form.
22016 * @param {Roo.bootstrap.TabPanel} this
22017 * @return {Boolean} false if there is an error
22020 'beforedeactivate': true
22023 this.tabId = this.tabId || Roo.id();
22027 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22034 touchSlide : false,
22035 getAutoCreate : function(){
22040 // item is needed for carousel - not sure if it has any effect otherwise
22041 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22042 html: this.html || ''
22046 cfg.cls += ' active';
22050 cfg.tabId = this.tabId;
22058 initEvents: function()
22060 var p = this.parent();
22062 this.navId = this.navId || p.navId;
22064 if (typeof(this.navId) != 'undefined') {
22065 // not really needed.. but just in case.. parent should be a NavGroup.
22066 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22070 var i = tg.tabs.length - 1;
22072 if(this.active && tg.bullets > 0 && i < tg.bullets){
22073 tg.setActiveBullet(i);
22077 this.el.on('click', this.onClick, this);
22079 if(Roo.isTouch && this.touchSlide){
22080 this.el.on("touchstart", this.onTouchStart, this);
22081 this.el.on("touchmove", this.onTouchMove, this);
22082 this.el.on("touchend", this.onTouchEnd, this);
22087 onRender : function(ct, position)
22089 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22092 setActive : function(state)
22094 Roo.log("panel - set active " + this.tabId + "=" + state);
22096 this.active = state;
22098 this.el.removeClass('active');
22100 } else if (!this.el.hasClass('active')) {
22101 this.el.addClass('active');
22104 this.fireEvent('changed', this, state);
22107 onClick : function(e)
22109 e.preventDefault();
22111 if(!this.href.length){
22115 window.location.href = this.href;
22124 onTouchStart : function(e)
22126 this.swiping = false;
22128 this.startX = e.browserEvent.touches[0].clientX;
22129 this.startY = e.browserEvent.touches[0].clientY;
22132 onTouchMove : function(e)
22134 this.swiping = true;
22136 this.endX = e.browserEvent.touches[0].clientX;
22137 this.endY = e.browserEvent.touches[0].clientY;
22140 onTouchEnd : function(e)
22147 var tabGroup = this.parent();
22149 if(this.endX > this.startX){ // swiping right
22150 tabGroup.showPanelPrev();
22154 if(this.startX > this.endX){ // swiping left
22155 tabGroup.showPanelNext();
22174 * @class Roo.bootstrap.DateField
22175 * @extends Roo.bootstrap.Input
22176 * Bootstrap DateField class
22177 * @cfg {Number} weekStart default 0
22178 * @cfg {String} viewMode default empty, (months|years)
22179 * @cfg {String} minViewMode default empty, (months|years)
22180 * @cfg {Number} startDate default -Infinity
22181 * @cfg {Number} endDate default Infinity
22182 * @cfg {Boolean} todayHighlight default false
22183 * @cfg {Boolean} todayBtn default false
22184 * @cfg {Boolean} calendarWeeks default false
22185 * @cfg {Object} daysOfWeekDisabled default empty
22186 * @cfg {Boolean} singleMode default false (true | false)
22188 * @cfg {Boolean} keyboardNavigation default true
22189 * @cfg {String} language default en
22192 * Create a new DateField
22193 * @param {Object} config The config object
22196 Roo.bootstrap.DateField = function(config){
22197 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22201 * Fires when this field show.
22202 * @param {Roo.bootstrap.DateField} this
22203 * @param {Mixed} date The date value
22208 * Fires when this field hide.
22209 * @param {Roo.bootstrap.DateField} this
22210 * @param {Mixed} date The date value
22215 * Fires when select a date.
22216 * @param {Roo.bootstrap.DateField} this
22217 * @param {Mixed} date The date value
22221 * @event beforeselect
22222 * Fires when before select a date.
22223 * @param {Roo.bootstrap.DateField} this
22224 * @param {Mixed} date The date value
22226 beforeselect : true
22230 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22233 * @cfg {String} format
22234 * The default date format string which can be overriden for localization support. The format must be
22235 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22239 * @cfg {String} altFormats
22240 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22241 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22243 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22251 todayHighlight : false,
22257 keyboardNavigation: true,
22259 calendarWeeks: false,
22261 startDate: -Infinity,
22265 daysOfWeekDisabled: [],
22269 singleMode : false,
22271 UTCDate: function()
22273 return new Date(Date.UTC.apply(Date, arguments));
22276 UTCToday: function()
22278 var today = new Date();
22279 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22282 getDate: function() {
22283 var d = this.getUTCDate();
22284 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22287 getUTCDate: function() {
22291 setDate: function(d) {
22292 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22295 setUTCDate: function(d) {
22297 this.setValue(this.formatDate(this.date));
22300 onRender: function(ct, position)
22303 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22305 this.language = this.language || 'en';
22306 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22307 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22309 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22310 this.format = this.format || 'm/d/y';
22311 this.isInline = false;
22312 this.isInput = true;
22313 this.component = this.el.select('.add-on', true).first() || false;
22314 this.component = (this.component && this.component.length === 0) ? false : this.component;
22315 this.hasInput = this.component && this.inputEl().length;
22317 if (typeof(this.minViewMode === 'string')) {
22318 switch (this.minViewMode) {
22320 this.minViewMode = 1;
22323 this.minViewMode = 2;
22326 this.minViewMode = 0;
22331 if (typeof(this.viewMode === 'string')) {
22332 switch (this.viewMode) {
22345 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22347 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22349 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22351 this.picker().on('mousedown', this.onMousedown, this);
22352 this.picker().on('click', this.onClick, this);
22354 this.picker().addClass('datepicker-dropdown');
22356 this.startViewMode = this.viewMode;
22358 if(this.singleMode){
22359 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22360 v.setVisibilityMode(Roo.Element.DISPLAY);
22364 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22365 v.setStyle('width', '189px');
22369 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22370 if(!this.calendarWeeks){
22375 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22376 v.attr('colspan', function(i, val){
22377 return parseInt(val) + 1;
22382 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22384 this.setStartDate(this.startDate);
22385 this.setEndDate(this.endDate);
22387 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22394 if(this.isInline) {
22399 picker : function()
22401 return this.pickerEl;
22402 // return this.el.select('.datepicker', true).first();
22405 fillDow: function()
22407 var dowCnt = this.weekStart;
22416 if(this.calendarWeeks){
22424 while (dowCnt < this.weekStart + 7) {
22428 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22432 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22435 fillMonths: function()
22438 var months = this.picker().select('>.datepicker-months td', true).first();
22440 months.dom.innerHTML = '';
22446 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22449 months.createChild(month);
22456 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;
22458 if (this.date < this.startDate) {
22459 this.viewDate = new Date(this.startDate);
22460 } else if (this.date > this.endDate) {
22461 this.viewDate = new Date(this.endDate);
22463 this.viewDate = new Date(this.date);
22471 var d = new Date(this.viewDate),
22472 year = d.getUTCFullYear(),
22473 month = d.getUTCMonth(),
22474 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22475 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22476 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22477 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22478 currentDate = this.date && this.date.valueOf(),
22479 today = this.UTCToday();
22481 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22483 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22485 // this.picker.select('>tfoot th.today').
22486 // .text(dates[this.language].today)
22487 // .toggle(this.todayBtn !== false);
22489 this.updateNavArrows();
22492 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22494 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22496 prevMonth.setUTCDate(day);
22498 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22500 var nextMonth = new Date(prevMonth);
22502 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22504 nextMonth = nextMonth.valueOf();
22506 var fillMonths = false;
22508 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22510 while(prevMonth.valueOf() <= nextMonth) {
22513 if (prevMonth.getUTCDay() === this.weekStart) {
22515 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22523 if(this.calendarWeeks){
22524 // ISO 8601: First week contains first thursday.
22525 // ISO also states week starts on Monday, but we can be more abstract here.
22527 // Start of current week: based on weekstart/current date
22528 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22529 // Thursday of this week
22530 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22531 // First Thursday of year, year from thursday
22532 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22533 // Calendar week: ms between thursdays, div ms per day, div 7 days
22534 calWeek = (th - yth) / 864e5 / 7 + 1;
22536 fillMonths.cn.push({
22544 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22546 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22549 if (this.todayHighlight &&
22550 prevMonth.getUTCFullYear() == today.getFullYear() &&
22551 prevMonth.getUTCMonth() == today.getMonth() &&
22552 prevMonth.getUTCDate() == today.getDate()) {
22553 clsName += ' today';
22556 if (currentDate && prevMonth.valueOf() === currentDate) {
22557 clsName += ' active';
22560 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22561 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22562 clsName += ' disabled';
22565 fillMonths.cn.push({
22567 cls: 'day ' + clsName,
22568 html: prevMonth.getDate()
22571 prevMonth.setDate(prevMonth.getDate()+1);
22574 var currentYear = this.date && this.date.getUTCFullYear();
22575 var currentMonth = this.date && this.date.getUTCMonth();
22577 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22579 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22580 v.removeClass('active');
22582 if(currentYear === year && k === currentMonth){
22583 v.addClass('active');
22586 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22587 v.addClass('disabled');
22593 year = parseInt(year/10, 10) * 10;
22595 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22597 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22600 for (var i = -1; i < 11; i++) {
22601 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22603 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22611 showMode: function(dir)
22614 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22617 Roo.each(this.picker().select('>div',true).elements, function(v){
22618 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22621 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22626 if(this.isInline) {
22630 this.picker().removeClass(['bottom', 'top']);
22632 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22634 * place to the top of element!
22638 this.picker().addClass('top');
22639 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22644 this.picker().addClass('bottom');
22646 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22649 parseDate : function(value)
22651 if(!value || value instanceof Date){
22654 var v = Date.parseDate(value, this.format);
22655 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22656 v = Date.parseDate(value, 'Y-m-d');
22658 if(!v && this.altFormats){
22659 if(!this.altFormatsArray){
22660 this.altFormatsArray = this.altFormats.split("|");
22662 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22663 v = Date.parseDate(value, this.altFormatsArray[i]);
22669 formatDate : function(date, fmt)
22671 return (!date || !(date instanceof Date)) ?
22672 date : date.dateFormat(fmt || this.format);
22675 onFocus : function()
22677 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22681 onBlur : function()
22683 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22685 var d = this.inputEl().getValue();
22692 showPopup : function()
22694 this.picker().show();
22698 this.fireEvent('showpopup', this, this.date);
22701 hidePopup : function()
22703 if(this.isInline) {
22706 this.picker().hide();
22707 this.viewMode = this.startViewMode;
22710 this.fireEvent('hidepopup', this, this.date);
22714 onMousedown: function(e)
22716 e.stopPropagation();
22717 e.preventDefault();
22722 Roo.bootstrap.DateField.superclass.keyup.call(this);
22726 setValue: function(v)
22728 if(this.fireEvent('beforeselect', this, v) !== false){
22729 var d = new Date(this.parseDate(v) ).clearTime();
22731 if(isNaN(d.getTime())){
22732 this.date = this.viewDate = '';
22733 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22737 v = this.formatDate(d);
22739 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22741 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22745 this.fireEvent('select', this, this.date);
22749 getValue: function()
22751 return this.formatDate(this.date);
22754 fireKey: function(e)
22756 if (!this.picker().isVisible()){
22757 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22763 var dateChanged = false,
22765 newDate, newViewDate;
22770 e.preventDefault();
22774 if (!this.keyboardNavigation) {
22777 dir = e.keyCode == 37 ? -1 : 1;
22780 newDate = this.moveYear(this.date, dir);
22781 newViewDate = this.moveYear(this.viewDate, dir);
22782 } else if (e.shiftKey){
22783 newDate = this.moveMonth(this.date, dir);
22784 newViewDate = this.moveMonth(this.viewDate, dir);
22786 newDate = new Date(this.date);
22787 newDate.setUTCDate(this.date.getUTCDate() + dir);
22788 newViewDate = new Date(this.viewDate);
22789 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22791 if (this.dateWithinRange(newDate)){
22792 this.date = newDate;
22793 this.viewDate = newViewDate;
22794 this.setValue(this.formatDate(this.date));
22796 e.preventDefault();
22797 dateChanged = true;
22802 if (!this.keyboardNavigation) {
22805 dir = e.keyCode == 38 ? -1 : 1;
22807 newDate = this.moveYear(this.date, dir);
22808 newViewDate = this.moveYear(this.viewDate, dir);
22809 } else if (e.shiftKey){
22810 newDate = this.moveMonth(this.date, dir);
22811 newViewDate = this.moveMonth(this.viewDate, dir);
22813 newDate = new Date(this.date);
22814 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22815 newViewDate = new Date(this.viewDate);
22816 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22818 if (this.dateWithinRange(newDate)){
22819 this.date = newDate;
22820 this.viewDate = newViewDate;
22821 this.setValue(this.formatDate(this.date));
22823 e.preventDefault();
22824 dateChanged = true;
22828 this.setValue(this.formatDate(this.date));
22830 e.preventDefault();
22833 this.setValue(this.formatDate(this.date));
22847 onClick: function(e)
22849 e.stopPropagation();
22850 e.preventDefault();
22852 var target = e.getTarget();
22854 if(target.nodeName.toLowerCase() === 'i'){
22855 target = Roo.get(target).dom.parentNode;
22858 var nodeName = target.nodeName;
22859 var className = target.className;
22860 var html = target.innerHTML;
22861 //Roo.log(nodeName);
22863 switch(nodeName.toLowerCase()) {
22865 switch(className) {
22871 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22872 switch(this.viewMode){
22874 this.viewDate = this.moveMonth(this.viewDate, dir);
22878 this.viewDate = this.moveYear(this.viewDate, dir);
22884 var date = new Date();
22885 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22887 this.setValue(this.formatDate(this.date));
22894 if (className.indexOf('disabled') < 0) {
22895 if (!this.viewDate) {
22896 this.viewDate = new Date();
22898 this.viewDate.setUTCDate(1);
22899 if (className.indexOf('month') > -1) {
22900 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22902 var year = parseInt(html, 10) || 0;
22903 this.viewDate.setUTCFullYear(year);
22907 if(this.singleMode){
22908 this.setValue(this.formatDate(this.viewDate));
22919 //Roo.log(className);
22920 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22921 var day = parseInt(html, 10) || 1;
22922 var year = (this.viewDate || new Date()).getUTCFullYear(),
22923 month = (this.viewDate || new Date()).getUTCMonth();
22925 if (className.indexOf('old') > -1) {
22932 } else if (className.indexOf('new') > -1) {
22940 //Roo.log([year,month,day]);
22941 this.date = this.UTCDate(year, month, day,0,0,0,0);
22942 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22944 //Roo.log(this.formatDate(this.date));
22945 this.setValue(this.formatDate(this.date));
22952 setStartDate: function(startDate)
22954 this.startDate = startDate || -Infinity;
22955 if (this.startDate !== -Infinity) {
22956 this.startDate = this.parseDate(this.startDate);
22959 this.updateNavArrows();
22962 setEndDate: function(endDate)
22964 this.endDate = endDate || Infinity;
22965 if (this.endDate !== Infinity) {
22966 this.endDate = this.parseDate(this.endDate);
22969 this.updateNavArrows();
22972 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22974 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22975 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22976 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22978 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22979 return parseInt(d, 10);
22982 this.updateNavArrows();
22985 updateNavArrows: function()
22987 if(this.singleMode){
22991 var d = new Date(this.viewDate),
22992 year = d.getUTCFullYear(),
22993 month = d.getUTCMonth();
22995 Roo.each(this.picker().select('.prev', true).elements, function(v){
22997 switch (this.viewMode) {
23000 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23006 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23013 Roo.each(this.picker().select('.next', true).elements, function(v){
23015 switch (this.viewMode) {
23018 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23024 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23032 moveMonth: function(date, dir)
23037 var new_date = new Date(date.valueOf()),
23038 day = new_date.getUTCDate(),
23039 month = new_date.getUTCMonth(),
23040 mag = Math.abs(dir),
23042 dir = dir > 0 ? 1 : -1;
23045 // If going back one month, make sure month is not current month
23046 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23048 return new_date.getUTCMonth() == month;
23050 // If going forward one month, make sure month is as expected
23051 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23053 return new_date.getUTCMonth() != new_month;
23055 new_month = month + dir;
23056 new_date.setUTCMonth(new_month);
23057 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23058 if (new_month < 0 || new_month > 11) {
23059 new_month = (new_month + 12) % 12;
23062 // For magnitudes >1, move one month at a time...
23063 for (var i=0; i<mag; i++) {
23064 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23065 new_date = this.moveMonth(new_date, dir);
23067 // ...then reset the day, keeping it in the new month
23068 new_month = new_date.getUTCMonth();
23069 new_date.setUTCDate(day);
23071 return new_month != new_date.getUTCMonth();
23074 // Common date-resetting loop -- if date is beyond end of month, make it
23077 new_date.setUTCDate(--day);
23078 new_date.setUTCMonth(new_month);
23083 moveYear: function(date, dir)
23085 return this.moveMonth(date, dir*12);
23088 dateWithinRange: function(date)
23090 return date >= this.startDate && date <= this.endDate;
23096 this.picker().remove();
23099 validateValue : function(value)
23101 if(this.getVisibilityEl().hasClass('hidden')){
23105 if(value.length < 1) {
23106 if(this.allowBlank){
23112 if(value.length < this.minLength){
23115 if(value.length > this.maxLength){
23119 var vt = Roo.form.VTypes;
23120 if(!vt[this.vtype](value, this)){
23124 if(typeof this.validator == "function"){
23125 var msg = this.validator(value);
23131 if(this.regex && !this.regex.test(value)){
23135 if(typeof(this.parseDate(value)) == 'undefined'){
23139 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23143 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23153 this.date = this.viewDate = '';
23155 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23160 Roo.apply(Roo.bootstrap.DateField, {
23171 html: '<i class="fa fa-arrow-left"/>'
23181 html: '<i class="fa fa-arrow-right"/>'
23223 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23224 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23225 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23226 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23227 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23240 navFnc: 'FullYear',
23245 navFnc: 'FullYear',
23250 Roo.apply(Roo.bootstrap.DateField, {
23254 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23258 cls: 'datepicker-days',
23262 cls: 'table-condensed',
23264 Roo.bootstrap.DateField.head,
23268 Roo.bootstrap.DateField.footer
23275 cls: 'datepicker-months',
23279 cls: 'table-condensed',
23281 Roo.bootstrap.DateField.head,
23282 Roo.bootstrap.DateField.content,
23283 Roo.bootstrap.DateField.footer
23290 cls: 'datepicker-years',
23294 cls: 'table-condensed',
23296 Roo.bootstrap.DateField.head,
23297 Roo.bootstrap.DateField.content,
23298 Roo.bootstrap.DateField.footer
23317 * @class Roo.bootstrap.TimeField
23318 * @extends Roo.bootstrap.Input
23319 * Bootstrap DateField class
23323 * Create a new TimeField
23324 * @param {Object} config The config object
23327 Roo.bootstrap.TimeField = function(config){
23328 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23332 * Fires when this field show.
23333 * @param {Roo.bootstrap.DateField} thisthis
23334 * @param {Mixed} date The date value
23339 * Fires when this field hide.
23340 * @param {Roo.bootstrap.DateField} this
23341 * @param {Mixed} date The date value
23346 * Fires when select a date.
23347 * @param {Roo.bootstrap.DateField} this
23348 * @param {Mixed} date The date value
23354 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23357 * @cfg {String} format
23358 * The default time format string which can be overriden for localization support. The format must be
23359 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23363 getAutoCreate : function()
23365 this.after = '<i class="fa far fa-clock"></i>';
23366 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23370 onRender: function(ct, position)
23373 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23375 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23377 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23379 this.pop = this.picker().select('>.datepicker-time',true).first();
23380 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23382 this.picker().on('mousedown', this.onMousedown, this);
23383 this.picker().on('click', this.onClick, this);
23385 this.picker().addClass('datepicker-dropdown');
23390 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23391 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23392 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23393 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23394 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23395 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23399 fireKey: function(e){
23400 if (!this.picker().isVisible()){
23401 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23407 e.preventDefault();
23415 this.onTogglePeriod();
23418 this.onIncrementMinutes();
23421 this.onDecrementMinutes();
23430 onClick: function(e) {
23431 e.stopPropagation();
23432 e.preventDefault();
23435 picker : function()
23437 return this.pickerEl;
23440 fillTime: function()
23442 var time = this.pop.select('tbody', true).first();
23444 time.dom.innerHTML = '';
23459 cls: 'hours-up fa fas fa-chevron-up'
23479 cls: 'minutes-up fa fas fa-chevron-up'
23500 cls: 'timepicker-hour',
23515 cls: 'timepicker-minute',
23530 cls: 'btn btn-primary period',
23552 cls: 'hours-down fa fas fa-chevron-down'
23572 cls: 'minutes-down fa fas fa-chevron-down'
23590 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23597 var hours = this.time.getHours();
23598 var minutes = this.time.getMinutes();
23611 hours = hours - 12;
23615 hours = '0' + hours;
23619 minutes = '0' + minutes;
23622 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23623 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23624 this.pop.select('button', true).first().dom.innerHTML = period;
23630 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23632 var cls = ['bottom'];
23634 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23641 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23645 //this.picker().setXY(20000,20000);
23646 this.picker().addClass(cls.join('-'));
23650 Roo.each(cls, function(c){
23655 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23656 //_this.picker().setTop(_this.inputEl().getHeight());
23660 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23662 //_this.picker().setTop(0 - _this.picker().getHeight());
23667 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23671 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23679 onFocus : function()
23681 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23685 onBlur : function()
23687 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23693 this.picker().show();
23698 this.fireEvent('show', this, this.date);
23703 this.picker().hide();
23706 this.fireEvent('hide', this, this.date);
23709 setTime : function()
23712 this.setValue(this.time.format(this.format));
23714 this.fireEvent('select', this, this.date);
23719 onMousedown: function(e){
23720 e.stopPropagation();
23721 e.preventDefault();
23724 onIncrementHours: function()
23726 Roo.log('onIncrementHours');
23727 this.time = this.time.add(Date.HOUR, 1);
23732 onDecrementHours: function()
23734 Roo.log('onDecrementHours');
23735 this.time = this.time.add(Date.HOUR, -1);
23739 onIncrementMinutes: function()
23741 Roo.log('onIncrementMinutes');
23742 this.time = this.time.add(Date.MINUTE, 1);
23746 onDecrementMinutes: function()
23748 Roo.log('onDecrementMinutes');
23749 this.time = this.time.add(Date.MINUTE, -1);
23753 onTogglePeriod: function()
23755 Roo.log('onTogglePeriod');
23756 this.time = this.time.add(Date.HOUR, 12);
23764 Roo.apply(Roo.bootstrap.TimeField, {
23768 cls: 'datepicker dropdown-menu',
23772 cls: 'datepicker-time',
23776 cls: 'table-condensed',
23805 cls: 'btn btn-info ok',
23833 * @class Roo.bootstrap.MonthField
23834 * @extends Roo.bootstrap.Input
23835 * Bootstrap MonthField class
23837 * @cfg {String} language default en
23840 * Create a new MonthField
23841 * @param {Object} config The config object
23844 Roo.bootstrap.MonthField = function(config){
23845 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23850 * Fires when this field show.
23851 * @param {Roo.bootstrap.MonthField} this
23852 * @param {Mixed} date The date value
23857 * Fires when this field hide.
23858 * @param {Roo.bootstrap.MonthField} this
23859 * @param {Mixed} date The date value
23864 * Fires when select a date.
23865 * @param {Roo.bootstrap.MonthField} this
23866 * @param {String} oldvalue The old value
23867 * @param {String} newvalue The new value
23873 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23875 onRender: function(ct, position)
23878 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23880 this.language = this.language || 'en';
23881 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23882 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23884 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23885 this.isInline = false;
23886 this.isInput = true;
23887 this.component = this.el.select('.add-on', true).first() || false;
23888 this.component = (this.component && this.component.length === 0) ? false : this.component;
23889 this.hasInput = this.component && this.inputEL().length;
23891 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23893 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23895 this.picker().on('mousedown', this.onMousedown, this);
23896 this.picker().on('click', this.onClick, this);
23898 this.picker().addClass('datepicker-dropdown');
23900 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23901 v.setStyle('width', '189px');
23908 if(this.isInline) {
23914 setValue: function(v, suppressEvent)
23916 var o = this.getValue();
23918 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23922 if(suppressEvent !== true){
23923 this.fireEvent('select', this, o, v);
23928 getValue: function()
23933 onClick: function(e)
23935 e.stopPropagation();
23936 e.preventDefault();
23938 var target = e.getTarget();
23940 if(target.nodeName.toLowerCase() === 'i'){
23941 target = Roo.get(target).dom.parentNode;
23944 var nodeName = target.nodeName;
23945 var className = target.className;
23946 var html = target.innerHTML;
23948 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23952 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23954 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23960 picker : function()
23962 return this.pickerEl;
23965 fillMonths: function()
23968 var months = this.picker().select('>.datepicker-months td', true).first();
23970 months.dom.innerHTML = '';
23976 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23979 months.createChild(month);
23988 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23989 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23992 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23993 e.removeClass('active');
23995 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23996 e.addClass('active');
24003 if(this.isInline) {
24007 this.picker().removeClass(['bottom', 'top']);
24009 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24011 * place to the top of element!
24015 this.picker().addClass('top');
24016 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24021 this.picker().addClass('bottom');
24023 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24026 onFocus : function()
24028 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24032 onBlur : function()
24034 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24036 var d = this.inputEl().getValue();
24045 this.picker().show();
24046 this.picker().select('>.datepicker-months', true).first().show();
24050 this.fireEvent('show', this, this.date);
24055 if(this.isInline) {
24058 this.picker().hide();
24059 this.fireEvent('hide', this, this.date);
24063 onMousedown: function(e)
24065 e.stopPropagation();
24066 e.preventDefault();
24071 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24075 fireKey: function(e)
24077 if (!this.picker().isVisible()){
24078 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24089 e.preventDefault();
24093 dir = e.keyCode == 37 ? -1 : 1;
24095 this.vIndex = this.vIndex + dir;
24097 if(this.vIndex < 0){
24101 if(this.vIndex > 11){
24105 if(isNaN(this.vIndex)){
24109 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24115 dir = e.keyCode == 38 ? -1 : 1;
24117 this.vIndex = this.vIndex + dir * 4;
24119 if(this.vIndex < 0){
24123 if(this.vIndex > 11){
24127 if(isNaN(this.vIndex)){
24131 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24136 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24137 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24141 e.preventDefault();
24144 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24145 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24161 this.picker().remove();
24166 Roo.apply(Roo.bootstrap.MonthField, {
24185 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24186 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24191 Roo.apply(Roo.bootstrap.MonthField, {
24195 cls: 'datepicker dropdown-menu roo-dynamic',
24199 cls: 'datepicker-months',
24203 cls: 'table-condensed',
24205 Roo.bootstrap.DateField.content
24225 * @class Roo.bootstrap.CheckBox
24226 * @extends Roo.bootstrap.Input
24227 * Bootstrap CheckBox class
24229 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24230 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24231 * @cfg {String} boxLabel The text that appears beside the checkbox
24232 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24233 * @cfg {Boolean} checked initnal the element
24234 * @cfg {Boolean} inline inline the element (default false)
24235 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24236 * @cfg {String} tooltip label tooltip
24239 * Create a new CheckBox
24240 * @param {Object} config The config object
24243 Roo.bootstrap.CheckBox = function(config){
24244 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24249 * Fires when the element is checked or unchecked.
24250 * @param {Roo.bootstrap.CheckBox} this This input
24251 * @param {Boolean} checked The new checked value
24256 * Fires when the element is click.
24257 * @param {Roo.bootstrap.CheckBox} this This input
24264 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24266 inputType: 'checkbox',
24275 // checkbox success does not make any sense really..
24280 getAutoCreate : function()
24282 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24288 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24291 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24297 type : this.inputType,
24298 value : this.inputValue,
24299 cls : 'roo-' + this.inputType, //'form-box',
24300 placeholder : this.placeholder || ''
24304 if(this.inputType != 'radio'){
24308 cls : 'roo-hidden-value',
24309 value : this.checked ? this.inputValue : this.valueOff
24314 if (this.weight) { // Validity check?
24315 cfg.cls += " " + this.inputType + "-" + this.weight;
24318 if (this.disabled) {
24319 input.disabled=true;
24323 input.checked = this.checked;
24328 input.name = this.name;
24330 if(this.inputType != 'radio'){
24331 hidden.name = this.name;
24332 input.name = '_hidden_' + this.name;
24337 input.cls += ' input-' + this.size;
24342 ['xs','sm','md','lg'].map(function(size){
24343 if (settings[size]) {
24344 cfg.cls += ' col-' + size + '-' + settings[size];
24348 var inputblock = input;
24350 if (this.before || this.after) {
24353 cls : 'input-group',
24358 inputblock.cn.push({
24360 cls : 'input-group-addon',
24365 inputblock.cn.push(input);
24367 if(this.inputType != 'radio'){
24368 inputblock.cn.push(hidden);
24372 inputblock.cn.push({
24374 cls : 'input-group-addon',
24380 var boxLabelCfg = false;
24386 //'for': id, // box label is handled by onclick - so no for...
24388 html: this.boxLabel
24391 boxLabelCfg.tooltip = this.tooltip;
24397 if (align ==='left' && this.fieldLabel.length) {
24398 // Roo.log("left and has label");
24403 cls : 'control-label',
24404 html : this.fieldLabel
24415 cfg.cn[1].cn.push(boxLabelCfg);
24418 if(this.labelWidth > 12){
24419 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24422 if(this.labelWidth < 13 && this.labelmd == 0){
24423 this.labelmd = this.labelWidth;
24426 if(this.labellg > 0){
24427 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24428 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24431 if(this.labelmd > 0){
24432 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24433 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24436 if(this.labelsm > 0){
24437 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24438 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24441 if(this.labelxs > 0){
24442 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24443 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24446 } else if ( this.fieldLabel.length) {
24447 // Roo.log(" label");
24451 tag: this.boxLabel ? 'span' : 'label',
24453 cls: 'control-label box-input-label',
24454 //cls : 'input-group-addon',
24455 html : this.fieldLabel
24462 cfg.cn.push(boxLabelCfg);
24467 // Roo.log(" no label && no align");
24468 cfg.cn = [ inputblock ] ;
24470 cfg.cn.push(boxLabelCfg);
24478 if(this.inputType != 'radio'){
24479 cfg.cn.push(hidden);
24487 * return the real input element.
24489 inputEl: function ()
24491 return this.el.select('input.roo-' + this.inputType,true).first();
24493 hiddenEl: function ()
24495 return this.el.select('input.roo-hidden-value',true).first();
24498 labelEl: function()
24500 return this.el.select('label.control-label',true).first();
24502 /* depricated... */
24506 return this.labelEl();
24509 boxLabelEl: function()
24511 return this.el.select('label.box-label',true).first();
24514 initEvents : function()
24516 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24518 this.inputEl().on('click', this.onClick, this);
24520 if (this.boxLabel) {
24521 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24524 this.startValue = this.getValue();
24527 Roo.bootstrap.CheckBox.register(this);
24531 onClick : function(e)
24533 if(this.fireEvent('click', this, e) !== false){
24534 this.setChecked(!this.checked);
24539 setChecked : function(state,suppressEvent)
24541 this.startValue = this.getValue();
24543 if(this.inputType == 'radio'){
24545 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24546 e.dom.checked = false;
24549 this.inputEl().dom.checked = true;
24551 this.inputEl().dom.value = this.inputValue;
24553 if(suppressEvent !== true){
24554 this.fireEvent('check', this, true);
24562 this.checked = state;
24564 this.inputEl().dom.checked = state;
24567 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24569 if(suppressEvent !== true){
24570 this.fireEvent('check', this, state);
24576 getValue : function()
24578 if(this.inputType == 'radio'){
24579 return this.getGroupValue();
24582 return this.hiddenEl().dom.value;
24586 getGroupValue : function()
24588 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24592 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24595 setValue : function(v,suppressEvent)
24597 if(this.inputType == 'radio'){
24598 this.setGroupValue(v, suppressEvent);
24602 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24607 setGroupValue : function(v, suppressEvent)
24609 this.startValue = this.getValue();
24611 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24612 e.dom.checked = false;
24614 if(e.dom.value == v){
24615 e.dom.checked = true;
24619 if(suppressEvent !== true){
24620 this.fireEvent('check', this, true);
24628 validate : function()
24630 if(this.getVisibilityEl().hasClass('hidden')){
24636 (this.inputType == 'radio' && this.validateRadio()) ||
24637 (this.inputType == 'checkbox' && this.validateCheckbox())
24643 this.markInvalid();
24647 validateRadio : function()
24649 if(this.getVisibilityEl().hasClass('hidden')){
24653 if(this.allowBlank){
24659 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24660 if(!e.dom.checked){
24672 validateCheckbox : function()
24675 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24676 //return (this.getValue() == this.inputValue) ? true : false;
24679 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24687 for(var i in group){
24688 if(group[i].el.isVisible(true)){
24696 for(var i in group){
24701 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24708 * Mark this field as valid
24710 markValid : function()
24714 this.fireEvent('valid', this);
24716 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24719 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24726 if(this.inputType == 'radio'){
24727 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24728 var fg = e.findParent('.form-group', false, true);
24729 if (Roo.bootstrap.version == 3) {
24730 fg.removeClass([_this.invalidClass, _this.validClass]);
24731 fg.addClass(_this.validClass);
24733 fg.removeClass(['is-valid', 'is-invalid']);
24734 fg.addClass('is-valid');
24742 var fg = this.el.findParent('.form-group', false, true);
24743 if (Roo.bootstrap.version == 3) {
24744 fg.removeClass([this.invalidClass, this.validClass]);
24745 fg.addClass(this.validClass);
24747 fg.removeClass(['is-valid', 'is-invalid']);
24748 fg.addClass('is-valid');
24753 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24759 for(var i in group){
24760 var fg = group[i].el.findParent('.form-group', false, true);
24761 if (Roo.bootstrap.version == 3) {
24762 fg.removeClass([this.invalidClass, this.validClass]);
24763 fg.addClass(this.validClass);
24765 fg.removeClass(['is-valid', 'is-invalid']);
24766 fg.addClass('is-valid');
24772 * Mark this field as invalid
24773 * @param {String} msg The validation message
24775 markInvalid : function(msg)
24777 if(this.allowBlank){
24783 this.fireEvent('invalid', this, msg);
24785 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24788 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24792 label.markInvalid();
24795 if(this.inputType == 'radio'){
24797 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24798 var fg = e.findParent('.form-group', false, true);
24799 if (Roo.bootstrap.version == 3) {
24800 fg.removeClass([_this.invalidClass, _this.validClass]);
24801 fg.addClass(_this.invalidClass);
24803 fg.removeClass(['is-invalid', 'is-valid']);
24804 fg.addClass('is-invalid');
24812 var fg = this.el.findParent('.form-group', false, true);
24813 if (Roo.bootstrap.version == 3) {
24814 fg.removeClass([_this.invalidClass, _this.validClass]);
24815 fg.addClass(_this.invalidClass);
24817 fg.removeClass(['is-invalid', 'is-valid']);
24818 fg.addClass('is-invalid');
24823 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24829 for(var i in group){
24830 var fg = group[i].el.findParent('.form-group', false, true);
24831 if (Roo.bootstrap.version == 3) {
24832 fg.removeClass([_this.invalidClass, _this.validClass]);
24833 fg.addClass(_this.invalidClass);
24835 fg.removeClass(['is-invalid', 'is-valid']);
24836 fg.addClass('is-invalid');
24842 clearInvalid : function()
24844 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24846 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24848 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24850 if (label && label.iconEl) {
24851 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24852 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24856 disable : function()
24858 if(this.inputType != 'radio'){
24859 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24866 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24867 _this.getActionEl().addClass(this.disabledClass);
24868 e.dom.disabled = true;
24872 this.disabled = true;
24873 this.fireEvent("disable", this);
24877 enable : function()
24879 if(this.inputType != 'radio'){
24880 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24888 _this.getActionEl().removeClass(this.disabledClass);
24889 e.dom.disabled = false;
24893 this.disabled = false;
24894 this.fireEvent("enable", this);
24898 setBoxLabel : function(v)
24903 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24909 Roo.apply(Roo.bootstrap.CheckBox, {
24914 * register a CheckBox Group
24915 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24917 register : function(checkbox)
24919 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24920 this.groups[checkbox.groupId] = {};
24923 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24927 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24931 * fetch a CheckBox Group based on the group ID
24932 * @param {string} the group ID
24933 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24935 get: function(groupId) {
24936 if (typeof(this.groups[groupId]) == 'undefined') {
24940 return this.groups[groupId] ;
24953 * @class Roo.bootstrap.Radio
24954 * @extends Roo.bootstrap.Component
24955 * Bootstrap Radio class
24956 * @cfg {String} boxLabel - the label associated
24957 * @cfg {String} value - the value of radio
24960 * Create a new Radio
24961 * @param {Object} config The config object
24963 Roo.bootstrap.Radio = function(config){
24964 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24968 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24974 getAutoCreate : function()
24978 cls : 'form-group radio',
24983 html : this.boxLabel
24991 initEvents : function()
24993 this.parent().register(this);
24995 this.el.on('click', this.onClick, this);
24999 onClick : function(e)
25001 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25002 this.setChecked(true);
25006 setChecked : function(state, suppressEvent)
25008 this.parent().setValue(this.value, suppressEvent);
25012 setBoxLabel : function(v)
25017 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25032 * @class Roo.bootstrap.SecurePass
25033 * @extends Roo.bootstrap.Input
25034 * Bootstrap SecurePass class
25038 * Create a new SecurePass
25039 * @param {Object} config The config object
25042 Roo.bootstrap.SecurePass = function (config) {
25043 // these go here, so the translation tool can replace them..
25045 PwdEmpty: "Please type a password, and then retype it to confirm.",
25046 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25047 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25048 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25049 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25050 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25051 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25052 TooWeak: "Your password is Too Weak."
25054 this.meterLabel = "Password strength:";
25055 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25056 this.meterClass = [
25057 "roo-password-meter-tooweak",
25058 "roo-password-meter-weak",
25059 "roo-password-meter-medium",
25060 "roo-password-meter-strong",
25061 "roo-password-meter-grey"
25066 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25069 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25071 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25073 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25074 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25075 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25076 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25077 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25078 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25079 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25089 * @cfg {String/Object} Label for the strength meter (defaults to
25090 * 'Password strength:')
25095 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25096 * ['Weak', 'Medium', 'Strong'])
25099 pwdStrengths: false,
25112 initEvents: function ()
25114 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25116 if (this.el.is('input[type=password]') && Roo.isSafari) {
25117 this.el.on('keydown', this.SafariOnKeyDown, this);
25120 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25123 onRender: function (ct, position)
25125 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25126 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25127 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25129 this.trigger.createChild({
25134 cls: 'roo-password-meter-grey col-xs-12',
25137 //width: this.meterWidth + 'px'
25141 cls: 'roo-password-meter-text'
25147 if (this.hideTrigger) {
25148 this.trigger.setDisplayed(false);
25150 this.setSize(this.width || '', this.height || '');
25153 onDestroy: function ()
25155 if (this.trigger) {
25156 this.trigger.removeAllListeners();
25157 this.trigger.remove();
25160 this.wrap.remove();
25162 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25165 checkStrength: function ()
25167 var pwd = this.inputEl().getValue();
25168 if (pwd == this._lastPwd) {
25173 if (this.ClientSideStrongPassword(pwd)) {
25175 } else if (this.ClientSideMediumPassword(pwd)) {
25177 } else if (this.ClientSideWeakPassword(pwd)) {
25183 Roo.log('strength1: ' + strength);
25185 //var pm = this.trigger.child('div/div/div').dom;
25186 var pm = this.trigger.child('div/div');
25187 pm.removeClass(this.meterClass);
25188 pm.addClass(this.meterClass[strength]);
25191 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25193 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25195 this._lastPwd = pwd;
25199 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25201 this._lastPwd = '';
25203 var pm = this.trigger.child('div/div');
25204 pm.removeClass(this.meterClass);
25205 pm.addClass('roo-password-meter-grey');
25208 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25211 this.inputEl().dom.type='password';
25214 validateValue: function (value)
25216 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25219 if (value.length == 0) {
25220 if (this.allowBlank) {
25221 this.clearInvalid();
25225 this.markInvalid(this.errors.PwdEmpty);
25226 this.errorMsg = this.errors.PwdEmpty;
25234 if (!value.match(/[\x21-\x7e]+/)) {
25235 this.markInvalid(this.errors.PwdBadChar);
25236 this.errorMsg = this.errors.PwdBadChar;
25239 if (value.length < 6) {
25240 this.markInvalid(this.errors.PwdShort);
25241 this.errorMsg = this.errors.PwdShort;
25244 if (value.length > 16) {
25245 this.markInvalid(this.errors.PwdLong);
25246 this.errorMsg = this.errors.PwdLong;
25250 if (this.ClientSideStrongPassword(value)) {
25252 } else if (this.ClientSideMediumPassword(value)) {
25254 } else if (this.ClientSideWeakPassword(value)) {
25261 if (strength < 2) {
25262 //this.markInvalid(this.errors.TooWeak);
25263 this.errorMsg = this.errors.TooWeak;
25268 console.log('strength2: ' + strength);
25270 //var pm = this.trigger.child('div/div/div').dom;
25272 var pm = this.trigger.child('div/div');
25273 pm.removeClass(this.meterClass);
25274 pm.addClass(this.meterClass[strength]);
25276 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25278 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25280 this.errorMsg = '';
25284 CharacterSetChecks: function (type)
25287 this.fResult = false;
25290 isctype: function (character, type)
25293 case this.kCapitalLetter:
25294 if (character >= 'A' && character <= 'Z') {
25299 case this.kSmallLetter:
25300 if (character >= 'a' && character <= 'z') {
25306 if (character >= '0' && character <= '9') {
25311 case this.kPunctuation:
25312 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25323 IsLongEnough: function (pwd, size)
25325 return !(pwd == null || isNaN(size) || pwd.length < size);
25328 SpansEnoughCharacterSets: function (word, nb)
25330 if (!this.IsLongEnough(word, nb))
25335 var characterSetChecks = new Array(
25336 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25337 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25340 for (var index = 0; index < word.length; ++index) {
25341 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25342 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25343 characterSetChecks[nCharSet].fResult = true;
25350 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25351 if (characterSetChecks[nCharSet].fResult) {
25356 if (nCharSets < nb) {
25362 ClientSideStrongPassword: function (pwd)
25364 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25367 ClientSideMediumPassword: function (pwd)
25369 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25372 ClientSideWeakPassword: function (pwd)
25374 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25377 })//<script type="text/javascript">
25380 * Based Ext JS Library 1.1.1
25381 * Copyright(c) 2006-2007, Ext JS, LLC.
25387 * @class Roo.HtmlEditorCore
25388 * @extends Roo.Component
25389 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25391 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25394 Roo.HtmlEditorCore = function(config){
25397 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25402 * @event initialize
25403 * Fires when the editor is fully initialized (including the iframe)
25404 * @param {Roo.HtmlEditorCore} this
25409 * Fires when the editor is first receives the focus. Any insertion must wait
25410 * until after this event.
25411 * @param {Roo.HtmlEditorCore} this
25415 * @event beforesync
25416 * Fires before the textarea is updated with content from the editor iframe. Return false
25417 * to cancel the sync.
25418 * @param {Roo.HtmlEditorCore} this
25419 * @param {String} html
25423 * @event beforepush
25424 * Fires before the iframe editor is updated with content from the textarea. Return false
25425 * to cancel the push.
25426 * @param {Roo.HtmlEditorCore} this
25427 * @param {String} html
25432 * Fires when the textarea is updated with content from the editor iframe.
25433 * @param {Roo.HtmlEditorCore} this
25434 * @param {String} html
25439 * Fires when the iframe editor is updated with content from the textarea.
25440 * @param {Roo.HtmlEditorCore} this
25441 * @param {String} html
25446 * @event editorevent
25447 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25448 * @param {Roo.HtmlEditorCore} this
25454 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25456 // defaults : white / black...
25457 this.applyBlacklists();
25464 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25468 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25474 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25479 * @cfg {Number} height (in pixels)
25483 * @cfg {Number} width (in pixels)
25488 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25491 stylesheets: false,
25496 // private properties
25497 validationEvent : false,
25499 initialized : false,
25501 sourceEditMode : false,
25502 onFocus : Roo.emptyFn,
25504 hideMode:'offsets',
25508 // blacklist + whitelisted elements..
25515 * Protected method that will not generally be called directly. It
25516 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25517 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25519 getDocMarkup : function(){
25523 // inherit styels from page...??
25524 if (this.stylesheets === false) {
25526 Roo.get(document.head).select('style').each(function(node) {
25527 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25530 Roo.get(document.head).select('link').each(function(node) {
25531 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25534 } else if (!this.stylesheets.length) {
25536 st = '<style type="text/css">' +
25537 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25540 for (var i in this.stylesheets) {
25541 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25546 st += '<style type="text/css">' +
25547 'IMG { cursor: pointer } ' +
25550 var cls = 'roo-htmleditor-body';
25552 if(this.bodyCls.length){
25553 cls += ' ' + this.bodyCls;
25556 return '<html><head>' + st +
25557 //<style type="text/css">' +
25558 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25560 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25564 onRender : function(ct, position)
25567 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25568 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25571 this.el.dom.style.border = '0 none';
25572 this.el.dom.setAttribute('tabIndex', -1);
25573 this.el.addClass('x-hidden hide');
25577 if(Roo.isIE){ // fix IE 1px bogus margin
25578 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25582 this.frameId = Roo.id();
25586 var iframe = this.owner.wrap.createChild({
25588 cls: 'form-control', // bootstrap..
25590 name: this.frameId,
25591 frameBorder : 'no',
25592 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25597 this.iframe = iframe.dom;
25599 this.assignDocWin();
25601 this.doc.designMode = 'on';
25604 this.doc.write(this.getDocMarkup());
25608 var task = { // must defer to wait for browser to be ready
25610 //console.log("run task?" + this.doc.readyState);
25611 this.assignDocWin();
25612 if(this.doc.body || this.doc.readyState == 'complete'){
25614 this.doc.designMode="on";
25618 Roo.TaskMgr.stop(task);
25619 this.initEditor.defer(10, this);
25626 Roo.TaskMgr.start(task);
25631 onResize : function(w, h)
25633 Roo.log('resize: ' +w + ',' + h );
25634 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25638 if(typeof w == 'number'){
25640 this.iframe.style.width = w + 'px';
25642 if(typeof h == 'number'){
25644 this.iframe.style.height = h + 'px';
25646 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25653 * Toggles the editor between standard and source edit mode.
25654 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25656 toggleSourceEdit : function(sourceEditMode){
25658 this.sourceEditMode = sourceEditMode === true;
25660 if(this.sourceEditMode){
25662 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25665 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25666 //this.iframe.className = '';
25669 //this.setSize(this.owner.wrap.getSize());
25670 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25677 * Protected method that will not generally be called directly. If you need/want
25678 * custom HTML cleanup, this is the method you should override.
25679 * @param {String} html The HTML to be cleaned
25680 * return {String} The cleaned HTML
25682 cleanHtml : function(html){
25683 html = String(html);
25684 if(html.length > 5){
25685 if(Roo.isSafari){ // strip safari nonsense
25686 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25689 if(html == ' '){
25696 * HTML Editor -> Textarea
25697 * Protected method that will not generally be called directly. Syncs the contents
25698 * of the editor iframe with the textarea.
25700 syncValue : function(){
25701 if(this.initialized){
25702 var bd = (this.doc.body || this.doc.documentElement);
25703 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25704 var html = bd.innerHTML;
25706 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25707 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25709 html = '<div style="'+m[0]+'">' + html + '</div>';
25712 html = this.cleanHtml(html);
25713 // fix up the special chars.. normaly like back quotes in word...
25714 // however we do not want to do this with chinese..
25715 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25717 var cc = match.charCodeAt();
25719 // Get the character value, handling surrogate pairs
25720 if (match.length == 2) {
25721 // It's a surrogate pair, calculate the Unicode code point
25722 var high = match.charCodeAt(0) - 0xD800;
25723 var low = match.charCodeAt(1) - 0xDC00;
25724 cc = (high * 0x400) + low + 0x10000;
25726 (cc >= 0x4E00 && cc < 0xA000 ) ||
25727 (cc >= 0x3400 && cc < 0x4E00 ) ||
25728 (cc >= 0xf900 && cc < 0xfb00 )
25733 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25734 return "&#" + cc + ";";
25741 if(this.owner.fireEvent('beforesync', this, html) !== false){
25742 this.el.dom.value = html;
25743 this.owner.fireEvent('sync', this, html);
25749 * Protected method that will not generally be called directly. Pushes the value of the textarea
25750 * into the iframe editor.
25752 pushValue : function(){
25753 if(this.initialized){
25754 var v = this.el.dom.value.trim();
25756 // if(v.length < 1){
25760 if(this.owner.fireEvent('beforepush', this, v) !== false){
25761 var d = (this.doc.body || this.doc.documentElement);
25763 this.cleanUpPaste();
25764 this.el.dom.value = d.innerHTML;
25765 this.owner.fireEvent('push', this, v);
25771 deferFocus : function(){
25772 this.focus.defer(10, this);
25776 focus : function(){
25777 if(this.win && !this.sourceEditMode){
25784 assignDocWin: function()
25786 var iframe = this.iframe;
25789 this.doc = iframe.contentWindow.document;
25790 this.win = iframe.contentWindow;
25792 // if (!Roo.get(this.frameId)) {
25795 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25796 // this.win = Roo.get(this.frameId).dom.contentWindow;
25798 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25802 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25803 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25808 initEditor : function(){
25809 //console.log("INIT EDITOR");
25810 this.assignDocWin();
25814 this.doc.designMode="on";
25816 this.doc.write(this.getDocMarkup());
25819 var dbody = (this.doc.body || this.doc.documentElement);
25820 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25821 // this copies styles from the containing element into thsi one..
25822 // not sure why we need all of this..
25823 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25825 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25826 //ss['background-attachment'] = 'fixed'; // w3c
25827 dbody.bgProperties = 'fixed'; // ie
25828 //Roo.DomHelper.applyStyles(dbody, ss);
25829 Roo.EventManager.on(this.doc, {
25830 //'mousedown': this.onEditorEvent,
25831 'mouseup': this.onEditorEvent,
25832 'dblclick': this.onEditorEvent,
25833 'click': this.onEditorEvent,
25834 'keyup': this.onEditorEvent,
25839 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25841 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25842 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25844 this.initialized = true;
25846 this.owner.fireEvent('initialize', this);
25851 onDestroy : function(){
25857 //for (var i =0; i < this.toolbars.length;i++) {
25858 // // fixme - ask toolbars for heights?
25859 // this.toolbars[i].onDestroy();
25862 //this.wrap.dom.innerHTML = '';
25863 //this.wrap.remove();
25868 onFirstFocus : function(){
25870 this.assignDocWin();
25873 this.activated = true;
25876 if(Roo.isGecko){ // prevent silly gecko errors
25878 var s = this.win.getSelection();
25879 if(!s.focusNode || s.focusNode.nodeType != 3){
25880 var r = s.getRangeAt(0);
25881 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25886 this.execCmd('useCSS', true);
25887 this.execCmd('styleWithCSS', false);
25890 this.owner.fireEvent('activate', this);
25894 adjustFont: function(btn){
25895 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25896 //if(Roo.isSafari){ // safari
25899 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25900 if(Roo.isSafari){ // safari
25901 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25902 v = (v < 10) ? 10 : v;
25903 v = (v > 48) ? 48 : v;
25904 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25909 v = Math.max(1, v+adjust);
25911 this.execCmd('FontSize', v );
25914 onEditorEvent : function(e)
25916 this.owner.fireEvent('editorevent', this, e);
25917 // this.updateToolbar();
25918 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25921 insertTag : function(tg)
25923 // could be a bit smarter... -> wrap the current selected tRoo..
25924 if (tg.toLowerCase() == 'span' ||
25925 tg.toLowerCase() == 'code' ||
25926 tg.toLowerCase() == 'sup' ||
25927 tg.toLowerCase() == 'sub'
25930 range = this.createRange(this.getSelection());
25931 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25932 wrappingNode.appendChild(range.extractContents());
25933 range.insertNode(wrappingNode);
25940 this.execCmd("formatblock", tg);
25944 insertText : function(txt)
25948 var range = this.createRange();
25949 range.deleteContents();
25950 //alert(Sender.getAttribute('label'));
25952 range.insertNode(this.doc.createTextNode(txt));
25958 * Executes a Midas editor command on the editor document and performs necessary focus and
25959 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25960 * @param {String} cmd The Midas command
25961 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25963 relayCmd : function(cmd, value){
25965 this.execCmd(cmd, value);
25966 this.owner.fireEvent('editorevent', this);
25967 //this.updateToolbar();
25968 this.owner.deferFocus();
25972 * Executes a Midas editor command directly on the editor document.
25973 * For visual commands, you should use {@link #relayCmd} instead.
25974 * <b>This should only be called after the editor is initialized.</b>
25975 * @param {String} cmd The Midas command
25976 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25978 execCmd : function(cmd, value){
25979 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25986 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25988 * @param {String} text | dom node..
25990 insertAtCursor : function(text)
25993 if(!this.activated){
25999 var r = this.doc.selection.createRange();
26010 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26014 // from jquery ui (MIT licenced)
26016 var win = this.win;
26018 if (win.getSelection && win.getSelection().getRangeAt) {
26019 range = win.getSelection().getRangeAt(0);
26020 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26021 range.insertNode(node);
26022 } else if (win.document.selection && win.document.selection.createRange) {
26023 // no firefox support
26024 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26025 win.document.selection.createRange().pasteHTML(txt);
26027 // no firefox support
26028 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26029 this.execCmd('InsertHTML', txt);
26038 mozKeyPress : function(e){
26040 var c = e.getCharCode(), cmd;
26043 c = String.fromCharCode(c).toLowerCase();
26057 this.cleanUpPaste.defer(100, this);
26065 e.preventDefault();
26073 fixKeys : function(){ // load time branching for fastest keydown performance
26075 return function(e){
26076 var k = e.getKey(), r;
26079 r = this.doc.selection.createRange();
26082 r.pasteHTML('    ');
26089 r = this.doc.selection.createRange();
26091 var target = r.parentElement();
26092 if(!target || target.tagName.toLowerCase() != 'li'){
26094 r.pasteHTML('<br />');
26100 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26101 this.cleanUpPaste.defer(100, this);
26107 }else if(Roo.isOpera){
26108 return function(e){
26109 var k = e.getKey();
26113 this.execCmd('InsertHTML','    ');
26116 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26117 this.cleanUpPaste.defer(100, this);
26122 }else if(Roo.isSafari){
26123 return function(e){
26124 var k = e.getKey();
26128 this.execCmd('InsertText','\t');
26132 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26133 this.cleanUpPaste.defer(100, this);
26141 getAllAncestors: function()
26143 var p = this.getSelectedNode();
26146 a.push(p); // push blank onto stack..
26147 p = this.getParentElement();
26151 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26155 a.push(this.doc.body);
26159 lastSelNode : false,
26162 getSelection : function()
26164 this.assignDocWin();
26165 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26168 getSelectedNode: function()
26170 // this may only work on Gecko!!!
26172 // should we cache this!!!!
26177 var range = this.createRange(this.getSelection()).cloneRange();
26180 var parent = range.parentElement();
26182 var testRange = range.duplicate();
26183 testRange.moveToElementText(parent);
26184 if (testRange.inRange(range)) {
26187 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26190 parent = parent.parentElement;
26195 // is ancestor a text element.
26196 var ac = range.commonAncestorContainer;
26197 if (ac.nodeType == 3) {
26198 ac = ac.parentNode;
26201 var ar = ac.childNodes;
26204 var other_nodes = [];
26205 var has_other_nodes = false;
26206 for (var i=0;i<ar.length;i++) {
26207 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26210 // fullly contained node.
26212 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26217 // probably selected..
26218 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26219 other_nodes.push(ar[i]);
26223 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26228 has_other_nodes = true;
26230 if (!nodes.length && other_nodes.length) {
26231 nodes= other_nodes;
26233 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26239 createRange: function(sel)
26241 // this has strange effects when using with
26242 // top toolbar - not sure if it's a great idea.
26243 //this.editor.contentWindow.focus();
26244 if (typeof sel != "undefined") {
26246 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26248 return this.doc.createRange();
26251 return this.doc.createRange();
26254 getParentElement: function()
26257 this.assignDocWin();
26258 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26260 var range = this.createRange(sel);
26263 var p = range.commonAncestorContainer;
26264 while (p.nodeType == 3) { // text node
26275 * Range intersection.. the hard stuff...
26279 * [ -- selected range --- ]
26283 * if end is before start or hits it. fail.
26284 * if start is after end or hits it fail.
26286 * if either hits (but other is outside. - then it's not
26292 // @see http://www.thismuchiknow.co.uk/?p=64.
26293 rangeIntersectsNode : function(range, node)
26295 var nodeRange = node.ownerDocument.createRange();
26297 nodeRange.selectNode(node);
26299 nodeRange.selectNodeContents(node);
26302 var rangeStartRange = range.cloneRange();
26303 rangeStartRange.collapse(true);
26305 var rangeEndRange = range.cloneRange();
26306 rangeEndRange.collapse(false);
26308 var nodeStartRange = nodeRange.cloneRange();
26309 nodeStartRange.collapse(true);
26311 var nodeEndRange = nodeRange.cloneRange();
26312 nodeEndRange.collapse(false);
26314 return rangeStartRange.compareBoundaryPoints(
26315 Range.START_TO_START, nodeEndRange) == -1 &&
26316 rangeEndRange.compareBoundaryPoints(
26317 Range.START_TO_START, nodeStartRange) == 1;
26321 rangeCompareNode : function(range, node)
26323 var nodeRange = node.ownerDocument.createRange();
26325 nodeRange.selectNode(node);
26327 nodeRange.selectNodeContents(node);
26331 range.collapse(true);
26333 nodeRange.collapse(true);
26335 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26336 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26338 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26340 var nodeIsBefore = ss == 1;
26341 var nodeIsAfter = ee == -1;
26343 if (nodeIsBefore && nodeIsAfter) {
26346 if (!nodeIsBefore && nodeIsAfter) {
26347 return 1; //right trailed.
26350 if (nodeIsBefore && !nodeIsAfter) {
26351 return 2; // left trailed.
26357 // private? - in a new class?
26358 cleanUpPaste : function()
26360 // cleans up the whole document..
26361 Roo.log('cleanuppaste');
26363 this.cleanUpChildren(this.doc.body);
26364 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26365 if (clean != this.doc.body.innerHTML) {
26366 this.doc.body.innerHTML = clean;
26371 cleanWordChars : function(input) {// change the chars to hex code
26372 var he = Roo.HtmlEditorCore;
26374 var output = input;
26375 Roo.each(he.swapCodes, function(sw) {
26376 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26378 output = output.replace(swapper, sw[1]);
26385 cleanUpChildren : function (n)
26387 if (!n.childNodes.length) {
26390 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26391 this.cleanUpChild(n.childNodes[i]);
26398 cleanUpChild : function (node)
26401 //console.log(node);
26402 if (node.nodeName == "#text") {
26403 // clean up silly Windows -- stuff?
26406 if (node.nodeName == "#comment") {
26407 node.parentNode.removeChild(node);
26408 // clean up silly Windows -- stuff?
26411 var lcname = node.tagName.toLowerCase();
26412 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26413 // whitelist of tags..
26415 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26417 node.parentNode.removeChild(node);
26422 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26424 // spans with no attributes - just remove them..
26425 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26426 remove_keep_children = true;
26429 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26430 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26432 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26433 // remove_keep_children = true;
26436 if (remove_keep_children) {
26437 this.cleanUpChildren(node);
26438 // inserts everything just before this node...
26439 while (node.childNodes.length) {
26440 var cn = node.childNodes[0];
26441 node.removeChild(cn);
26442 node.parentNode.insertBefore(cn, node);
26444 node.parentNode.removeChild(node);
26448 if (!node.attributes || !node.attributes.length) {
26453 this.cleanUpChildren(node);
26457 function cleanAttr(n,v)
26460 if (v.match(/^\./) || v.match(/^\//)) {
26463 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26466 if (v.match(/^#/)) {
26469 if (v.match(/^\{/)) { // allow template editing.
26472 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26473 node.removeAttribute(n);
26477 var cwhite = this.cwhite;
26478 var cblack = this.cblack;
26480 function cleanStyle(n,v)
26482 if (v.match(/expression/)) { //XSS?? should we even bother..
26483 node.removeAttribute(n);
26487 var parts = v.split(/;/);
26490 Roo.each(parts, function(p) {
26491 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26495 var l = p.split(':').shift().replace(/\s+/g,'');
26496 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26498 if ( cwhite.length && cblack.indexOf(l) > -1) {
26499 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26500 //node.removeAttribute(n);
26504 // only allow 'c whitelisted system attributes'
26505 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26506 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26507 //node.removeAttribute(n);
26517 if (clean.length) {
26518 node.setAttribute(n, clean.join(';'));
26520 node.removeAttribute(n);
26526 for (var i = node.attributes.length-1; i > -1 ; i--) {
26527 var a = node.attributes[i];
26530 if (a.name.toLowerCase().substr(0,2)=='on') {
26531 node.removeAttribute(a.name);
26534 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26535 node.removeAttribute(a.name);
26538 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26539 cleanAttr(a.name,a.value); // fixme..
26542 if (a.name == 'style') {
26543 cleanStyle(a.name,a.value);
26546 /// clean up MS crap..
26547 // tecnically this should be a list of valid class'es..
26550 if (a.name == 'class') {
26551 if (a.value.match(/^Mso/)) {
26552 node.removeAttribute('class');
26555 if (a.value.match(/^body$/)) {
26556 node.removeAttribute('class');
26567 this.cleanUpChildren(node);
26573 * Clean up MS wordisms...
26575 cleanWord : function(node)
26578 this.cleanWord(this.doc.body);
26583 node.nodeName == 'SPAN' &&
26584 !node.hasAttributes() &&
26585 node.childNodes.length == 1 &&
26586 node.firstChild.nodeName == "#text"
26588 var textNode = node.firstChild;
26589 node.removeChild(textNode);
26590 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26591 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26593 node.parentNode.insertBefore(textNode, node);
26594 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26595 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26597 node.parentNode.removeChild(node);
26600 if (node.nodeName == "#text") {
26601 // clean up silly Windows -- stuff?
26604 if (node.nodeName == "#comment") {
26605 node.parentNode.removeChild(node);
26606 // clean up silly Windows -- stuff?
26610 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26611 node.parentNode.removeChild(node);
26614 //Roo.log(node.tagName);
26615 // remove - but keep children..
26616 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26617 //Roo.log('-- removed');
26618 while (node.childNodes.length) {
26619 var cn = node.childNodes[0];
26620 node.removeChild(cn);
26621 node.parentNode.insertBefore(cn, node);
26622 // move node to parent - and clean it..
26623 this.cleanWord(cn);
26625 node.parentNode.removeChild(node);
26626 /// no need to iterate chidlren = it's got none..
26627 //this.iterateChildren(node, this.cleanWord);
26631 if (node.className.length) {
26633 var cn = node.className.split(/\W+/);
26635 Roo.each(cn, function(cls) {
26636 if (cls.match(/Mso[a-zA-Z]+/)) {
26641 node.className = cna.length ? cna.join(' ') : '';
26643 node.removeAttribute("class");
26647 if (node.hasAttribute("lang")) {
26648 node.removeAttribute("lang");
26651 if (node.hasAttribute("style")) {
26653 var styles = node.getAttribute("style").split(";");
26655 Roo.each(styles, function(s) {
26656 if (!s.match(/:/)) {
26659 var kv = s.split(":");
26660 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26663 // what ever is left... we allow.
26666 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26667 if (!nstyle.length) {
26668 node.removeAttribute('style');
26671 this.iterateChildren(node, this.cleanWord);
26677 * iterateChildren of a Node, calling fn each time, using this as the scole..
26678 * @param {DomNode} node node to iterate children of.
26679 * @param {Function} fn method of this class to call on each item.
26681 iterateChildren : function(node, fn)
26683 if (!node.childNodes.length) {
26686 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26687 fn.call(this, node.childNodes[i])
26693 * cleanTableWidths.
26695 * Quite often pasting from word etc.. results in tables with column and widths.
26696 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26699 cleanTableWidths : function(node)
26704 this.cleanTableWidths(this.doc.body);
26709 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26712 Roo.log(node.tagName);
26713 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26714 this.iterateChildren(node, this.cleanTableWidths);
26717 if (node.hasAttribute('width')) {
26718 node.removeAttribute('width');
26722 if (node.hasAttribute("style")) {
26725 var styles = node.getAttribute("style").split(";");
26727 Roo.each(styles, function(s) {
26728 if (!s.match(/:/)) {
26731 var kv = s.split(":");
26732 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26735 // what ever is left... we allow.
26738 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26739 if (!nstyle.length) {
26740 node.removeAttribute('style');
26744 this.iterateChildren(node, this.cleanTableWidths);
26752 domToHTML : function(currentElement, depth, nopadtext) {
26754 depth = depth || 0;
26755 nopadtext = nopadtext || false;
26757 if (!currentElement) {
26758 return this.domToHTML(this.doc.body);
26761 //Roo.log(currentElement);
26763 var allText = false;
26764 var nodeName = currentElement.nodeName;
26765 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26767 if (nodeName == '#text') {
26769 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26774 if (nodeName != 'BODY') {
26777 // Prints the node tagName, such as <A>, <IMG>, etc
26780 for(i = 0; i < currentElement.attributes.length;i++) {
26782 var aname = currentElement.attributes.item(i).name;
26783 if (!currentElement.attributes.item(i).value.length) {
26786 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26789 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26798 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26801 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26806 // Traverse the tree
26808 var currentElementChild = currentElement.childNodes.item(i);
26809 var allText = true;
26810 var innerHTML = '';
26812 while (currentElementChild) {
26813 // Formatting code (indent the tree so it looks nice on the screen)
26814 var nopad = nopadtext;
26815 if (lastnode == 'SPAN') {
26819 if (currentElementChild.nodeName == '#text') {
26820 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26821 toadd = nopadtext ? toadd : toadd.trim();
26822 if (!nopad && toadd.length > 80) {
26823 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26825 innerHTML += toadd;
26828 currentElementChild = currentElement.childNodes.item(i);
26834 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26836 // Recursively traverse the tree structure of the child node
26837 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26838 lastnode = currentElementChild.nodeName;
26840 currentElementChild=currentElement.childNodes.item(i);
26846 // The remaining code is mostly for formatting the tree
26847 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26852 ret+= "</"+tagName+">";
26858 applyBlacklists : function()
26860 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26861 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26865 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26866 if (b.indexOf(tag) > -1) {
26869 this.white.push(tag);
26873 Roo.each(w, function(tag) {
26874 if (b.indexOf(tag) > -1) {
26877 if (this.white.indexOf(tag) > -1) {
26880 this.white.push(tag);
26885 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26886 if (w.indexOf(tag) > -1) {
26889 this.black.push(tag);
26893 Roo.each(b, function(tag) {
26894 if (w.indexOf(tag) > -1) {
26897 if (this.black.indexOf(tag) > -1) {
26900 this.black.push(tag);
26905 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26906 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26910 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26911 if (b.indexOf(tag) > -1) {
26914 this.cwhite.push(tag);
26918 Roo.each(w, function(tag) {
26919 if (b.indexOf(tag) > -1) {
26922 if (this.cwhite.indexOf(tag) > -1) {
26925 this.cwhite.push(tag);
26930 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26931 if (w.indexOf(tag) > -1) {
26934 this.cblack.push(tag);
26938 Roo.each(b, function(tag) {
26939 if (w.indexOf(tag) > -1) {
26942 if (this.cblack.indexOf(tag) > -1) {
26945 this.cblack.push(tag);
26950 setStylesheets : function(stylesheets)
26952 if(typeof(stylesheets) == 'string'){
26953 Roo.get(this.iframe.contentDocument.head).createChild({
26955 rel : 'stylesheet',
26964 Roo.each(stylesheets, function(s) {
26969 Roo.get(_this.iframe.contentDocument.head).createChild({
26971 rel : 'stylesheet',
26980 removeStylesheets : function()
26984 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26989 setStyle : function(style)
26991 Roo.get(this.iframe.contentDocument.head).createChild({
27000 // hide stuff that is not compatible
27014 * @event specialkey
27018 * @cfg {String} fieldClass @hide
27021 * @cfg {String} focusClass @hide
27024 * @cfg {String} autoCreate @hide
27027 * @cfg {String} inputType @hide
27030 * @cfg {String} invalidClass @hide
27033 * @cfg {String} invalidText @hide
27036 * @cfg {String} msgFx @hide
27039 * @cfg {String} validateOnBlur @hide
27043 Roo.HtmlEditorCore.white = [
27044 'area', 'br', 'img', 'input', 'hr', 'wbr',
27046 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27047 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27048 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27049 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27050 'table', 'ul', 'xmp',
27052 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27055 'dir', 'menu', 'ol', 'ul', 'dl',
27061 Roo.HtmlEditorCore.black = [
27062 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27064 'base', 'basefont', 'bgsound', 'blink', 'body',
27065 'frame', 'frameset', 'head', 'html', 'ilayer',
27066 'iframe', 'layer', 'link', 'meta', 'object',
27067 'script', 'style' ,'title', 'xml' // clean later..
27069 Roo.HtmlEditorCore.clean = [
27070 'script', 'style', 'title', 'xml'
27072 Roo.HtmlEditorCore.remove = [
27077 Roo.HtmlEditorCore.ablack = [
27081 Roo.HtmlEditorCore.aclean = [
27082 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27086 Roo.HtmlEditorCore.pwhite= [
27087 'http', 'https', 'mailto'
27090 // white listed style attributes.
27091 Roo.HtmlEditorCore.cwhite= [
27092 // 'text-align', /// default is to allow most things..
27098 // black listed style attributes.
27099 Roo.HtmlEditorCore.cblack= [
27100 // 'font-size' -- this can be set by the project
27104 Roo.HtmlEditorCore.swapCodes =[
27105 [ 8211, "–" ],
27106 [ 8212, "—" ],
27123 * @class Roo.bootstrap.HtmlEditor
27124 * @extends Roo.bootstrap.TextArea
27125 * Bootstrap HtmlEditor class
27128 * Create a new HtmlEditor
27129 * @param {Object} config The config object
27132 Roo.bootstrap.HtmlEditor = function(config){
27133 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27134 if (!this.toolbars) {
27135 this.toolbars = [];
27138 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27141 * @event initialize
27142 * Fires when the editor is fully initialized (including the iframe)
27143 * @param {HtmlEditor} this
27148 * Fires when the editor is first receives the focus. Any insertion must wait
27149 * until after this event.
27150 * @param {HtmlEditor} this
27154 * @event beforesync
27155 * Fires before the textarea is updated with content from the editor iframe. Return false
27156 * to cancel the sync.
27157 * @param {HtmlEditor} this
27158 * @param {String} html
27162 * @event beforepush
27163 * Fires before the iframe editor is updated with content from the textarea. Return false
27164 * to cancel the push.
27165 * @param {HtmlEditor} this
27166 * @param {String} html
27171 * Fires when the textarea is updated with content from the editor iframe.
27172 * @param {HtmlEditor} this
27173 * @param {String} html
27178 * Fires when the iframe editor is updated with content from the textarea.
27179 * @param {HtmlEditor} this
27180 * @param {String} html
27184 * @event editmodechange
27185 * Fires when the editor switches edit modes
27186 * @param {HtmlEditor} this
27187 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27189 editmodechange: true,
27191 * @event editorevent
27192 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27193 * @param {HtmlEditor} this
27197 * @event firstfocus
27198 * Fires when on first focus - needed by toolbars..
27199 * @param {HtmlEditor} this
27204 * Auto save the htmlEditor value as a file into Events
27205 * @param {HtmlEditor} this
27209 * @event savedpreview
27210 * preview the saved version of htmlEditor
27211 * @param {HtmlEditor} this
27218 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27222 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27227 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27232 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27237 * @cfg {Number} height (in pixels)
27241 * @cfg {Number} width (in pixels)
27246 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27249 stylesheets: false,
27254 // private properties
27255 validationEvent : false,
27257 initialized : false,
27260 onFocus : Roo.emptyFn,
27262 hideMode:'offsets',
27264 tbContainer : false,
27268 toolbarContainer :function() {
27269 return this.wrap.select('.x-html-editor-tb',true).first();
27273 * Protected method that will not generally be called directly. It
27274 * is called when the editor creates its toolbar. Override this method if you need to
27275 * add custom toolbar buttons.
27276 * @param {HtmlEditor} editor
27278 createToolbar : function(){
27279 Roo.log('renewing');
27280 Roo.log("create toolbars");
27282 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27283 this.toolbars[0].render(this.toolbarContainer());
27287 // if (!editor.toolbars || !editor.toolbars.length) {
27288 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27291 // for (var i =0 ; i < editor.toolbars.length;i++) {
27292 // editor.toolbars[i] = Roo.factory(
27293 // typeof(editor.toolbars[i]) == 'string' ?
27294 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27295 // Roo.bootstrap.HtmlEditor);
27296 // editor.toolbars[i].init(editor);
27302 onRender : function(ct, position)
27304 // Roo.log("Call onRender: " + this.xtype);
27306 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27308 this.wrap = this.inputEl().wrap({
27309 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27312 this.editorcore.onRender(ct, position);
27314 if (this.resizable) {
27315 this.resizeEl = new Roo.Resizable(this.wrap, {
27319 minHeight : this.height,
27320 height: this.height,
27321 handles : this.resizable,
27324 resize : function(r, w, h) {
27325 _t.onResize(w,h); // -something
27331 this.createToolbar(this);
27334 if(!this.width && this.resizable){
27335 this.setSize(this.wrap.getSize());
27337 if (this.resizeEl) {
27338 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27339 // should trigger onReize..
27345 onResize : function(w, h)
27347 Roo.log('resize: ' +w + ',' + h );
27348 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27352 if(this.inputEl() ){
27353 if(typeof w == 'number'){
27354 var aw = w - this.wrap.getFrameWidth('lr');
27355 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27358 if(typeof h == 'number'){
27359 var tbh = -11; // fixme it needs to tool bar size!
27360 for (var i =0; i < this.toolbars.length;i++) {
27361 // fixme - ask toolbars for heights?
27362 tbh += this.toolbars[i].el.getHeight();
27363 //if (this.toolbars[i].footer) {
27364 // tbh += this.toolbars[i].footer.el.getHeight();
27372 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27373 ah -= 5; // knock a few pixes off for look..
27374 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27378 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27379 this.editorcore.onResize(ew,eh);
27384 * Toggles the editor between standard and source edit mode.
27385 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27387 toggleSourceEdit : function(sourceEditMode)
27389 this.editorcore.toggleSourceEdit(sourceEditMode);
27391 if(this.editorcore.sourceEditMode){
27392 Roo.log('editor - showing textarea');
27395 // Roo.log(this.syncValue());
27397 this.inputEl().removeClass(['hide', 'x-hidden']);
27398 this.inputEl().dom.removeAttribute('tabIndex');
27399 this.inputEl().focus();
27401 Roo.log('editor - hiding textarea');
27403 // Roo.log(this.pushValue());
27406 this.inputEl().addClass(['hide', 'x-hidden']);
27407 this.inputEl().dom.setAttribute('tabIndex', -1);
27408 //this.deferFocus();
27411 if(this.resizable){
27412 this.setSize(this.wrap.getSize());
27415 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27418 // private (for BoxComponent)
27419 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27421 // private (for BoxComponent)
27422 getResizeEl : function(){
27426 // private (for BoxComponent)
27427 getPositionEl : function(){
27432 initEvents : function(){
27433 this.originalValue = this.getValue();
27437 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27440 // markInvalid : Roo.emptyFn,
27442 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27445 // clearInvalid : Roo.emptyFn,
27447 setValue : function(v){
27448 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27449 this.editorcore.pushValue();
27454 deferFocus : function(){
27455 this.focus.defer(10, this);
27459 focus : function(){
27460 this.editorcore.focus();
27466 onDestroy : function(){
27472 for (var i =0; i < this.toolbars.length;i++) {
27473 // fixme - ask toolbars for heights?
27474 this.toolbars[i].onDestroy();
27477 this.wrap.dom.innerHTML = '';
27478 this.wrap.remove();
27483 onFirstFocus : function(){
27484 //Roo.log("onFirstFocus");
27485 this.editorcore.onFirstFocus();
27486 for (var i =0; i < this.toolbars.length;i++) {
27487 this.toolbars[i].onFirstFocus();
27493 syncValue : function()
27495 this.editorcore.syncValue();
27498 pushValue : function()
27500 this.editorcore.pushValue();
27504 // hide stuff that is not compatible
27518 * @event specialkey
27522 * @cfg {String} fieldClass @hide
27525 * @cfg {String} focusClass @hide
27528 * @cfg {String} autoCreate @hide
27531 * @cfg {String} inputType @hide
27535 * @cfg {String} invalidText @hide
27538 * @cfg {String} msgFx @hide
27541 * @cfg {String} validateOnBlur @hide
27550 Roo.namespace('Roo.bootstrap.htmleditor');
27552 * @class Roo.bootstrap.HtmlEditorToolbar1
27558 new Roo.bootstrap.HtmlEditor({
27561 new Roo.bootstrap.HtmlEditorToolbar1({
27562 disable : { fonts: 1 , format: 1, ..., ... , ...],
27568 * @cfg {Object} disable List of elements to disable..
27569 * @cfg {Array} btns List of additional buttons.
27573 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27576 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27579 Roo.apply(this, config);
27581 // default disabled, based on 'good practice'..
27582 this.disable = this.disable || {};
27583 Roo.applyIf(this.disable, {
27586 specialElements : true
27588 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27590 this.editor = config.editor;
27591 this.editorcore = config.editor.editorcore;
27593 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27595 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27596 // dont call parent... till later.
27598 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27603 editorcore : false,
27608 "h1","h2","h3","h4","h5","h6",
27610 "abbr", "acronym", "address", "cite", "samp", "var",
27614 onRender : function(ct, position)
27616 // Roo.log("Call onRender: " + this.xtype);
27618 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27620 this.el.dom.style.marginBottom = '0';
27622 var editorcore = this.editorcore;
27623 var editor= this.editor;
27626 var btn = function(id,cmd , toggle, handler, html){
27628 var event = toggle ? 'toggle' : 'click';
27633 xns: Roo.bootstrap,
27637 enableToggle:toggle !== false,
27639 pressed : toggle ? false : null,
27642 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27643 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27649 // var cb_box = function...
27654 xns: Roo.bootstrap,
27659 xns: Roo.bootstrap,
27663 Roo.each(this.formats, function(f) {
27664 style.menu.items.push({
27666 xns: Roo.bootstrap,
27667 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27672 editorcore.insertTag(this.tagname);
27679 children.push(style);
27681 btn('bold',false,true);
27682 btn('italic',false,true);
27683 btn('align-left', 'justifyleft',true);
27684 btn('align-center', 'justifycenter',true);
27685 btn('align-right' , 'justifyright',true);
27686 btn('link', false, false, function(btn) {
27687 //Roo.log("create link?");
27688 var url = prompt(this.createLinkText, this.defaultLinkValue);
27689 if(url && url != 'http:/'+'/'){
27690 this.editorcore.relayCmd('createlink', url);
27693 btn('list','insertunorderedlist',true);
27694 btn('pencil', false,true, function(btn){
27696 this.toggleSourceEdit(btn.pressed);
27699 if (this.editor.btns.length > 0) {
27700 for (var i = 0; i<this.editor.btns.length; i++) {
27701 children.push(this.editor.btns[i]);
27709 xns: Roo.bootstrap,
27714 xns: Roo.bootstrap,
27719 cog.menu.items.push({
27721 xns: Roo.bootstrap,
27722 html : Clean styles,
27727 editorcore.insertTag(this.tagname);
27736 this.xtype = 'NavSimplebar';
27738 for(var i=0;i< children.length;i++) {
27740 this.buttons.add(this.addxtypeChild(children[i]));
27744 editor.on('editorevent', this.updateToolbar, this);
27746 onBtnClick : function(id)
27748 this.editorcore.relayCmd(id);
27749 this.editorcore.focus();
27753 * Protected method that will not generally be called directly. It triggers
27754 * a toolbar update by reading the markup state of the current selection in the editor.
27756 updateToolbar: function(){
27758 if(!this.editorcore.activated){
27759 this.editor.onFirstFocus(); // is this neeed?
27763 var btns = this.buttons;
27764 var doc = this.editorcore.doc;
27765 btns.get('bold').setActive(doc.queryCommandState('bold'));
27766 btns.get('italic').setActive(doc.queryCommandState('italic'));
27767 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27769 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27770 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27771 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27773 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27774 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27777 var ans = this.editorcore.getAllAncestors();
27778 if (this.formatCombo) {
27781 var store = this.formatCombo.store;
27782 this.formatCombo.setValue("");
27783 for (var i =0; i < ans.length;i++) {
27784 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27786 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27794 // hides menus... - so this cant be on a menu...
27795 Roo.bootstrap.MenuMgr.hideAll();
27797 Roo.bootstrap.MenuMgr.hideAll();
27798 //this.editorsyncValue();
27800 onFirstFocus: function() {
27801 this.buttons.each(function(item){
27805 toggleSourceEdit : function(sourceEditMode){
27808 if(sourceEditMode){
27809 Roo.log("disabling buttons");
27810 this.buttons.each( function(item){
27811 if(item.cmd != 'pencil'){
27817 Roo.log("enabling buttons");
27818 if(this.editorcore.initialized){
27819 this.buttons.each( function(item){
27825 Roo.log("calling toggole on editor");
27826 // tell the editor that it's been pressed..
27827 this.editor.toggleSourceEdit(sourceEditMode);
27841 * @class Roo.bootstrap.Markdown
27842 * @extends Roo.bootstrap.TextArea
27843 * Bootstrap Showdown editable area
27844 * @cfg {string} content
27847 * Create a new Showdown
27850 Roo.bootstrap.Markdown = function(config){
27851 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27855 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27859 initEvents : function()
27862 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27863 this.markdownEl = this.el.createChild({
27864 cls : 'roo-markdown-area'
27866 this.inputEl().addClass('d-none');
27867 if (this.getValue() == '') {
27868 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27871 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27873 this.markdownEl.on('click', this.toggleTextEdit, this);
27874 this.on('blur', this.toggleTextEdit, this);
27875 this.on('specialkey', this.resizeTextArea, this);
27878 toggleTextEdit : function()
27880 var sh = this.markdownEl.getHeight();
27881 this.inputEl().addClass('d-none');
27882 this.markdownEl.addClass('d-none');
27883 if (!this.editing) {
27885 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27886 this.inputEl().removeClass('d-none');
27887 this.inputEl().focus();
27888 this.editing = true;
27891 // show showdown...
27892 this.updateMarkdown();
27893 this.markdownEl.removeClass('d-none');
27894 this.editing = false;
27897 updateMarkdown : function()
27899 if (this.getValue() == '') {
27900 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27904 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27907 resizeTextArea: function () {
27910 Roo.log([sh, this.getValue().split("\n").length * 30]);
27911 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27913 setValue : function(val)
27915 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27916 if (!this.editing) {
27917 this.updateMarkdown();
27923 if (!this.editing) {
27924 this.toggleTextEdit();
27932 * Ext JS Library 1.1.1
27933 * Copyright(c) 2006-2007, Ext JS, LLC.
27935 * Originally Released Under LGPL - original licence link has changed is not relivant.
27938 * <script type="text/javascript">
27942 * @class Roo.bootstrap.PagingToolbar
27943 * @extends Roo.bootstrap.NavSimplebar
27944 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27946 * Create a new PagingToolbar
27947 * @param {Object} config The config object
27948 * @param {Roo.data.Store} store
27950 Roo.bootstrap.PagingToolbar = function(config)
27952 // old args format still supported... - xtype is prefered..
27953 // created from xtype...
27955 this.ds = config.dataSource;
27957 if (config.store && !this.ds) {
27958 this.store= Roo.factory(config.store, Roo.data);
27959 this.ds = this.store;
27960 this.ds.xmodule = this.xmodule || false;
27963 this.toolbarItems = [];
27964 if (config.items) {
27965 this.toolbarItems = config.items;
27968 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27973 this.bind(this.ds);
27976 if (Roo.bootstrap.version == 4) {
27977 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27979 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27984 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27986 * @cfg {Roo.data.Store} dataSource
27987 * The underlying data store providing the paged data
27990 * @cfg {String/HTMLElement/Element} container
27991 * container The id or element that will contain the toolbar
27994 * @cfg {Boolean} displayInfo
27995 * True to display the displayMsg (defaults to false)
27998 * @cfg {Number} pageSize
27999 * The number of records to display per page (defaults to 20)
28003 * @cfg {String} displayMsg
28004 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28006 displayMsg : 'Displaying {0} - {1} of {2}',
28008 * @cfg {String} emptyMsg
28009 * The message to display when no records are found (defaults to "No data to display")
28011 emptyMsg : 'No data to display',
28013 * Customizable piece of the default paging text (defaults to "Page")
28016 beforePageText : "Page",
28018 * Customizable piece of the default paging text (defaults to "of %0")
28021 afterPageText : "of {0}",
28023 * Customizable piece of the default paging text (defaults to "First Page")
28026 firstText : "First Page",
28028 * Customizable piece of the default paging text (defaults to "Previous Page")
28031 prevText : "Previous Page",
28033 * Customizable piece of the default paging text (defaults to "Next Page")
28036 nextText : "Next Page",
28038 * Customizable piece of the default paging text (defaults to "Last Page")
28041 lastText : "Last Page",
28043 * Customizable piece of the default paging text (defaults to "Refresh")
28046 refreshText : "Refresh",
28050 onRender : function(ct, position)
28052 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28053 this.navgroup.parentId = this.id;
28054 this.navgroup.onRender(this.el, null);
28055 // add the buttons to the navgroup
28057 if(this.displayInfo){
28058 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28059 this.displayEl = this.el.select('.x-paging-info', true).first();
28060 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28061 // this.displayEl = navel.el.select('span',true).first();
28067 Roo.each(_this.buttons, function(e){ // this might need to use render????
28068 Roo.factory(e).render(_this.el);
28072 Roo.each(_this.toolbarItems, function(e) {
28073 _this.navgroup.addItem(e);
28077 this.first = this.navgroup.addItem({
28078 tooltip: this.firstText,
28079 cls: "prev btn-outline-secondary",
28080 html : ' <i class="fa fa-step-backward"></i>',
28082 preventDefault: true,
28083 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28086 this.prev = this.navgroup.addItem({
28087 tooltip: this.prevText,
28088 cls: "prev btn-outline-secondary",
28089 html : ' <i class="fa fa-backward"></i>',
28091 preventDefault: true,
28092 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28094 //this.addSeparator();
28097 var field = this.navgroup.addItem( {
28099 cls : 'x-paging-position btn-outline-secondary',
28101 html : this.beforePageText +
28102 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28103 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28106 this.field = field.el.select('input', true).first();
28107 this.field.on("keydown", this.onPagingKeydown, this);
28108 this.field.on("focus", function(){this.dom.select();});
28111 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28112 //this.field.setHeight(18);
28113 //this.addSeparator();
28114 this.next = this.navgroup.addItem({
28115 tooltip: this.nextText,
28116 cls: "next btn-outline-secondary",
28117 html : ' <i class="fa fa-forward"></i>',
28119 preventDefault: true,
28120 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28122 this.last = this.navgroup.addItem({
28123 tooltip: this.lastText,
28124 html : ' <i class="fa fa-step-forward"></i>',
28125 cls: "next btn-outline-secondary",
28127 preventDefault: true,
28128 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28130 //this.addSeparator();
28131 this.loading = this.navgroup.addItem({
28132 tooltip: this.refreshText,
28133 cls: "btn-outline-secondary",
28134 html : ' <i class="fa fa-refresh"></i>',
28135 preventDefault: true,
28136 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28142 updateInfo : function(){
28143 if(this.displayEl){
28144 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28145 var msg = count == 0 ?
28149 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28151 this.displayEl.update(msg);
28156 onLoad : function(ds, r, o)
28158 this.cursor = o.params && o.params.start ? o.params.start : 0;
28160 var d = this.getPageData(),
28165 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28166 this.field.dom.value = ap;
28167 this.first.setDisabled(ap == 1);
28168 this.prev.setDisabled(ap == 1);
28169 this.next.setDisabled(ap == ps);
28170 this.last.setDisabled(ap == ps);
28171 this.loading.enable();
28176 getPageData : function(){
28177 var total = this.ds.getTotalCount();
28180 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28181 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28186 onLoadError : function(){
28187 this.loading.enable();
28191 onPagingKeydown : function(e){
28192 var k = e.getKey();
28193 var d = this.getPageData();
28195 var v = this.field.dom.value, pageNum;
28196 if(!v || isNaN(pageNum = parseInt(v, 10))){
28197 this.field.dom.value = d.activePage;
28200 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28201 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28204 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))
28206 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28207 this.field.dom.value = pageNum;
28208 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28211 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28213 var v = this.field.dom.value, pageNum;
28214 var increment = (e.shiftKey) ? 10 : 1;
28215 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28218 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28219 this.field.dom.value = d.activePage;
28222 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28224 this.field.dom.value = parseInt(v, 10) + increment;
28225 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28226 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28233 beforeLoad : function(){
28235 this.loading.disable();
28240 onClick : function(which){
28249 ds.load({params:{start: 0, limit: this.pageSize}});
28252 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28255 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28258 var total = ds.getTotalCount();
28259 var extra = total % this.pageSize;
28260 var lastStart = extra ? (total - extra) : total-this.pageSize;
28261 ds.load({params:{start: lastStart, limit: this.pageSize}});
28264 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28270 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28271 * @param {Roo.data.Store} store The data store to unbind
28273 unbind : function(ds){
28274 ds.un("beforeload", this.beforeLoad, this);
28275 ds.un("load", this.onLoad, this);
28276 ds.un("loadexception", this.onLoadError, this);
28277 ds.un("remove", this.updateInfo, this);
28278 ds.un("add", this.updateInfo, this);
28279 this.ds = undefined;
28283 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28284 * @param {Roo.data.Store} store The data store to bind
28286 bind : function(ds){
28287 ds.on("beforeload", this.beforeLoad, this);
28288 ds.on("load", this.onLoad, this);
28289 ds.on("loadexception", this.onLoadError, this);
28290 ds.on("remove", this.updateInfo, this);
28291 ds.on("add", this.updateInfo, this);
28302 * @class Roo.bootstrap.MessageBar
28303 * @extends Roo.bootstrap.Component
28304 * Bootstrap MessageBar class
28305 * @cfg {String} html contents of the MessageBar
28306 * @cfg {String} weight (info | success | warning | danger) default info
28307 * @cfg {String} beforeClass insert the bar before the given class
28308 * @cfg {Boolean} closable (true | false) default false
28309 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28312 * Create a new Element
28313 * @param {Object} config The config object
28316 Roo.bootstrap.MessageBar = function(config){
28317 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28320 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28326 beforeClass: 'bootstrap-sticky-wrap',
28328 getAutoCreate : function(){
28332 cls: 'alert alert-dismissable alert-' + this.weight,
28337 html: this.html || ''
28343 cfg.cls += ' alert-messages-fixed';
28357 onRender : function(ct, position)
28359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28362 var cfg = Roo.apply({}, this.getAutoCreate());
28366 cfg.cls += ' ' + this.cls;
28369 cfg.style = this.style;
28371 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28373 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28376 this.el.select('>button.close').on('click', this.hide, this);
28382 if (!this.rendered) {
28388 this.fireEvent('show', this);
28394 if (!this.rendered) {
28400 this.fireEvent('hide', this);
28403 update : function()
28405 // var e = this.el.dom.firstChild;
28407 // if(this.closable){
28408 // e = e.nextSibling;
28411 // e.data = this.html || '';
28413 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28429 * @class Roo.bootstrap.Graph
28430 * @extends Roo.bootstrap.Component
28431 * Bootstrap Graph class
28435 @cfg {String} graphtype bar | vbar | pie
28436 @cfg {number} g_x coodinator | centre x (pie)
28437 @cfg {number} g_y coodinator | centre y (pie)
28438 @cfg {number} g_r radius (pie)
28439 @cfg {number} g_height height of the chart (respected by all elements in the set)
28440 @cfg {number} g_width width of the chart (respected by all elements in the set)
28441 @cfg {Object} title The title of the chart
28444 -opts (object) options for the chart
28446 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28447 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28449 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.
28450 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28452 o stretch (boolean)
28454 -opts (object) options for the pie
28457 o startAngle (number)
28458 o endAngle (number)
28462 * Create a new Input
28463 * @param {Object} config The config object
28466 Roo.bootstrap.Graph = function(config){
28467 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28473 * The img click event for the img.
28474 * @param {Roo.EventObject} e
28480 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28491 //g_colors: this.colors,
28498 getAutoCreate : function(){
28509 onRender : function(ct,position){
28512 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28514 if (typeof(Raphael) == 'undefined') {
28515 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28519 this.raphael = Raphael(this.el.dom);
28521 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28522 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28523 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28524 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28526 r.text(160, 10, "Single Series Chart").attr(txtattr);
28527 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28528 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28529 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28531 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28532 r.barchart(330, 10, 300, 220, data1);
28533 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28534 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28537 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28538 // r.barchart(30, 30, 560, 250, xdata, {
28539 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28540 // axis : "0 0 1 1",
28541 // axisxlabels : xdata
28542 // //yvalues : cols,
28545 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28547 // this.load(null,xdata,{
28548 // axis : "0 0 1 1",
28549 // axisxlabels : xdata
28554 load : function(graphtype,xdata,opts)
28556 this.raphael.clear();
28558 graphtype = this.graphtype;
28563 var r = this.raphael,
28564 fin = function () {
28565 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28567 fout = function () {
28568 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28570 pfin = function() {
28571 this.sector.stop();
28572 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28575 this.label[0].stop();
28576 this.label[0].attr({ r: 7.5 });
28577 this.label[1].attr({ "font-weight": 800 });
28580 pfout = function() {
28581 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28584 this.label[0].animate({ r: 5 }, 500, "bounce");
28585 this.label[1].attr({ "font-weight": 400 });
28591 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28594 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28597 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28598 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28600 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28607 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28612 setTitle: function(o)
28617 initEvents: function() {
28620 this.el.on('click', this.onClick, this);
28624 onClick : function(e)
28626 Roo.log('img onclick');
28627 this.fireEvent('click', this, e);
28639 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28642 * @class Roo.bootstrap.dash.NumberBox
28643 * @extends Roo.bootstrap.Component
28644 * Bootstrap NumberBox class
28645 * @cfg {String} headline Box headline
28646 * @cfg {String} content Box content
28647 * @cfg {String} icon Box icon
28648 * @cfg {String} footer Footer text
28649 * @cfg {String} fhref Footer href
28652 * Create a new NumberBox
28653 * @param {Object} config The config object
28657 Roo.bootstrap.dash.NumberBox = function(config){
28658 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28662 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28671 getAutoCreate : function(){
28675 cls : 'small-box ',
28683 cls : 'roo-headline',
28684 html : this.headline
28688 cls : 'roo-content',
28689 html : this.content
28703 cls : 'ion ' + this.icon
28712 cls : 'small-box-footer',
28713 href : this.fhref || '#',
28717 cfg.cn.push(footer);
28724 onRender : function(ct,position){
28725 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28732 setHeadline: function (value)
28734 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28737 setFooter: function (value, href)
28739 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28742 this.el.select('a.small-box-footer',true).first().attr('href', href);
28747 setContent: function (value)
28749 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28752 initEvents: function()
28766 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28769 * @class Roo.bootstrap.dash.TabBox
28770 * @extends Roo.bootstrap.Component
28771 * Bootstrap TabBox class
28772 * @cfg {String} title Title of the TabBox
28773 * @cfg {String} icon Icon of the TabBox
28774 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28775 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28778 * Create a new TabBox
28779 * @param {Object} config The config object
28783 Roo.bootstrap.dash.TabBox = function(config){
28784 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28789 * When a pane is added
28790 * @param {Roo.bootstrap.dash.TabPane} pane
28794 * @event activatepane
28795 * When a pane is activated
28796 * @param {Roo.bootstrap.dash.TabPane} pane
28798 "activatepane" : true
28806 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28811 tabScrollable : false,
28813 getChildContainer : function()
28815 return this.el.select('.tab-content', true).first();
28818 getAutoCreate : function(){
28822 cls: 'pull-left header',
28830 cls: 'fa ' + this.icon
28836 cls: 'nav nav-tabs pull-right',
28842 if(this.tabScrollable){
28849 cls: 'nav nav-tabs pull-right',
28860 cls: 'nav-tabs-custom',
28865 cls: 'tab-content no-padding',
28873 initEvents : function()
28875 //Roo.log('add add pane handler');
28876 this.on('addpane', this.onAddPane, this);
28879 * Updates the box title
28880 * @param {String} html to set the title to.
28882 setTitle : function(value)
28884 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28886 onAddPane : function(pane)
28888 this.panes.push(pane);
28889 //Roo.log('addpane');
28891 // tabs are rendere left to right..
28892 if(!this.showtabs){
28896 var ctr = this.el.select('.nav-tabs', true).first();
28899 var existing = ctr.select('.nav-tab',true);
28900 var qty = existing.getCount();;
28903 var tab = ctr.createChild({
28905 cls : 'nav-tab' + (qty ? '' : ' active'),
28913 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28916 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28918 pane.el.addClass('active');
28923 onTabClick : function(ev,un,ob,pane)
28925 //Roo.log('tab - prev default');
28926 ev.preventDefault();
28929 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28930 pane.tab.addClass('active');
28931 //Roo.log(pane.title);
28932 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28933 // technically we should have a deactivate event.. but maybe add later.
28934 // and it should not de-activate the selected tab...
28935 this.fireEvent('activatepane', pane);
28936 pane.el.addClass('active');
28937 pane.fireEvent('activate');
28942 getActivePane : function()
28945 Roo.each(this.panes, function(p) {
28946 if(p.el.hasClass('active')){
28967 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28969 * @class Roo.bootstrap.TabPane
28970 * @extends Roo.bootstrap.Component
28971 * Bootstrap TabPane class
28972 * @cfg {Boolean} active (false | true) Default false
28973 * @cfg {String} title title of panel
28977 * Create a new TabPane
28978 * @param {Object} config The config object
28981 Roo.bootstrap.dash.TabPane = function(config){
28982 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28988 * When a pane is activated
28989 * @param {Roo.bootstrap.dash.TabPane} pane
28996 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29001 // the tabBox that this is attached to.
29004 getAutoCreate : function()
29012 cfg.cls += ' active';
29017 initEvents : function()
29019 //Roo.log('trigger add pane handler');
29020 this.parent().fireEvent('addpane', this)
29024 * Updates the tab title
29025 * @param {String} html to set the title to.
29027 setTitle: function(str)
29033 this.tab.select('a', true).first().dom.innerHTML = str;
29050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29053 * @class Roo.bootstrap.menu.Menu
29054 * @extends Roo.bootstrap.Component
29055 * Bootstrap Menu class - container for Menu
29056 * @cfg {String} html Text of the menu
29057 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29058 * @cfg {String} icon Font awesome icon
29059 * @cfg {String} pos Menu align to (top | bottom) default bottom
29063 * Create a new Menu
29064 * @param {Object} config The config object
29068 Roo.bootstrap.menu.Menu = function(config){
29069 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29073 * @event beforeshow
29074 * Fires before this menu is displayed
29075 * @param {Roo.bootstrap.menu.Menu} this
29079 * @event beforehide
29080 * Fires before this menu is hidden
29081 * @param {Roo.bootstrap.menu.Menu} this
29086 * Fires after this menu is displayed
29087 * @param {Roo.bootstrap.menu.Menu} this
29092 * Fires after this menu is hidden
29093 * @param {Roo.bootstrap.menu.Menu} this
29098 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29099 * @param {Roo.bootstrap.menu.Menu} this
29100 * @param {Roo.EventObject} e
29107 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29111 weight : 'default',
29116 getChildContainer : function() {
29117 if(this.isSubMenu){
29121 return this.el.select('ul.dropdown-menu', true).first();
29124 getAutoCreate : function()
29129 cls : 'roo-menu-text',
29137 cls : 'fa ' + this.icon
29148 cls : 'dropdown-button btn btn-' + this.weight,
29153 cls : 'dropdown-toggle btn btn-' + this.weight,
29163 cls : 'dropdown-menu'
29169 if(this.pos == 'top'){
29170 cfg.cls += ' dropup';
29173 if(this.isSubMenu){
29176 cls : 'dropdown-menu'
29183 onRender : function(ct, position)
29185 this.isSubMenu = ct.hasClass('dropdown-submenu');
29187 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29190 initEvents : function()
29192 if(this.isSubMenu){
29196 this.hidden = true;
29198 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29199 this.triggerEl.on('click', this.onTriggerPress, this);
29201 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29202 this.buttonEl.on('click', this.onClick, this);
29208 if(this.isSubMenu){
29212 return this.el.select('ul.dropdown-menu', true).first();
29215 onClick : function(e)
29217 this.fireEvent("click", this, e);
29220 onTriggerPress : function(e)
29222 if (this.isVisible()) {
29229 isVisible : function(){
29230 return !this.hidden;
29235 this.fireEvent("beforeshow", this);
29237 this.hidden = false;
29238 this.el.addClass('open');
29240 Roo.get(document).on("mouseup", this.onMouseUp, this);
29242 this.fireEvent("show", this);
29249 this.fireEvent("beforehide", this);
29251 this.hidden = true;
29252 this.el.removeClass('open');
29254 Roo.get(document).un("mouseup", this.onMouseUp);
29256 this.fireEvent("hide", this);
29259 onMouseUp : function()
29273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29276 * @class Roo.bootstrap.menu.Item
29277 * @extends Roo.bootstrap.Component
29278 * Bootstrap MenuItem class
29279 * @cfg {Boolean} submenu (true | false) default false
29280 * @cfg {String} html text of the item
29281 * @cfg {String} href the link
29282 * @cfg {Boolean} disable (true | false) default false
29283 * @cfg {Boolean} preventDefault (true | false) default true
29284 * @cfg {String} icon Font awesome icon
29285 * @cfg {String} pos Submenu align to (left | right) default right
29289 * Create a new Item
29290 * @param {Object} config The config object
29294 Roo.bootstrap.menu.Item = function(config){
29295 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29299 * Fires when the mouse is hovering over this menu
29300 * @param {Roo.bootstrap.menu.Item} this
29301 * @param {Roo.EventObject} e
29306 * Fires when the mouse exits this menu
29307 * @param {Roo.bootstrap.menu.Item} this
29308 * @param {Roo.EventObject} e
29314 * The raw click event for the entire grid.
29315 * @param {Roo.EventObject} e
29321 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29326 preventDefault: true,
29331 getAutoCreate : function()
29336 cls : 'roo-menu-item-text',
29344 cls : 'fa ' + this.icon
29353 href : this.href || '#',
29360 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29364 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29366 if(this.pos == 'left'){
29367 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29374 initEvents : function()
29376 this.el.on('mouseover', this.onMouseOver, this);
29377 this.el.on('mouseout', this.onMouseOut, this);
29379 this.el.select('a', true).first().on('click', this.onClick, this);
29383 onClick : function(e)
29385 if(this.preventDefault){
29386 e.preventDefault();
29389 this.fireEvent("click", this, e);
29392 onMouseOver : function(e)
29394 if(this.submenu && this.pos == 'left'){
29395 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29398 this.fireEvent("mouseover", this, e);
29401 onMouseOut : function(e)
29403 this.fireEvent("mouseout", this, e);
29415 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29418 * @class Roo.bootstrap.menu.Separator
29419 * @extends Roo.bootstrap.Component
29420 * Bootstrap Separator class
29423 * Create a new Separator
29424 * @param {Object} config The config object
29428 Roo.bootstrap.menu.Separator = function(config){
29429 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29432 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29434 getAutoCreate : function(){
29437 cls: 'dropdown-divider divider'
29455 * @class Roo.bootstrap.Tooltip
29456 * Bootstrap Tooltip class
29457 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29458 * to determine which dom element triggers the tooltip.
29460 * It needs to add support for additional attributes like tooltip-position
29463 * Create a new Toolti
29464 * @param {Object} config The config object
29467 Roo.bootstrap.Tooltip = function(config){
29468 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29470 this.alignment = Roo.bootstrap.Tooltip.alignment;
29472 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29473 this.alignment = config.alignment;
29478 Roo.apply(Roo.bootstrap.Tooltip, {
29480 * @function init initialize tooltip monitoring.
29484 currentTip : false,
29485 currentRegion : false,
29491 Roo.get(document).on('mouseover', this.enter ,this);
29492 Roo.get(document).on('mouseout', this.leave, this);
29495 this.currentTip = new Roo.bootstrap.Tooltip();
29498 enter : function(ev)
29500 var dom = ev.getTarget();
29502 //Roo.log(['enter',dom]);
29503 var el = Roo.fly(dom);
29504 if (this.currentEl) {
29506 //Roo.log(this.currentEl);
29507 //Roo.log(this.currentEl.contains(dom));
29508 if (this.currentEl == el) {
29511 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29517 if (this.currentTip.el) {
29518 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29522 if(!el || el.dom == document){
29528 if (!el.attr('tooltip')) {
29529 pel = el.findParent("[tooltip]");
29531 bindEl = Roo.get(pel);
29537 // you can not look for children, as if el is the body.. then everythign is the child..
29538 if (!pel && !el.attr('tooltip')) { //
29539 if (!el.select("[tooltip]").elements.length) {
29542 // is the mouse over this child...?
29543 bindEl = el.select("[tooltip]").first();
29544 var xy = ev.getXY();
29545 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29546 //Roo.log("not in region.");
29549 //Roo.log("child element over..");
29552 this.currentEl = el;
29553 this.currentTip.bind(bindEl);
29554 this.currentRegion = Roo.lib.Region.getRegion(dom);
29555 this.currentTip.enter();
29558 leave : function(ev)
29560 var dom = ev.getTarget();
29561 //Roo.log(['leave',dom]);
29562 if (!this.currentEl) {
29567 if (dom != this.currentEl.dom) {
29570 var xy = ev.getXY();
29571 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29574 // only activate leave if mouse cursor is outside... bounding box..
29579 if (this.currentTip) {
29580 this.currentTip.leave();
29582 //Roo.log('clear currentEl');
29583 this.currentEl = false;
29588 'left' : ['r-l', [-2,0], 'right'],
29589 'right' : ['l-r', [2,0], 'left'],
29590 'bottom' : ['t-b', [0,2], 'top'],
29591 'top' : [ 'b-t', [0,-2], 'bottom']
29597 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29602 delay : null, // can be { show : 300 , hide: 500}
29606 hoverState : null, //???
29608 placement : 'bottom',
29612 getAutoCreate : function(){
29619 cls : 'tooltip-arrow arrow'
29622 cls : 'tooltip-inner'
29629 bind : function(el)
29634 initEvents : function()
29636 this.arrowEl = this.el.select('.arrow', true).first();
29637 this.innerEl = this.el.select('.tooltip-inner', true).first();
29640 enter : function () {
29642 if (this.timeout != null) {
29643 clearTimeout(this.timeout);
29646 this.hoverState = 'in';
29647 //Roo.log("enter - show");
29648 if (!this.delay || !this.delay.show) {
29653 this.timeout = setTimeout(function () {
29654 if (_t.hoverState == 'in') {
29657 }, this.delay.show);
29661 clearTimeout(this.timeout);
29663 this.hoverState = 'out';
29664 if (!this.delay || !this.delay.hide) {
29670 this.timeout = setTimeout(function () {
29671 //Roo.log("leave - timeout");
29673 if (_t.hoverState == 'out') {
29675 Roo.bootstrap.Tooltip.currentEl = false;
29680 show : function (msg)
29683 this.render(document.body);
29686 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29688 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29690 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29692 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29693 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29695 var placement = typeof this.placement == 'function' ?
29696 this.placement.call(this, this.el, on_el) :
29699 var autoToken = /\s?auto?\s?/i;
29700 var autoPlace = autoToken.test(placement);
29702 placement = placement.replace(autoToken, '') || 'top';
29706 //this.el.setXY([0,0]);
29708 //this.el.dom.style.display='block';
29710 //this.el.appendTo(on_el);
29712 var p = this.getPosition();
29713 var box = this.el.getBox();
29719 var align = this.alignment[placement];
29721 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29723 if(placement == 'top' || placement == 'bottom'){
29725 placement = 'right';
29728 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29729 placement = 'left';
29732 var scroll = Roo.select('body', true).first().getScroll();
29734 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29738 align = this.alignment[placement];
29740 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29744 var elems = document.getElementsByTagName('div');
29745 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29746 for (var i = 0; i < elems.length; i++) {
29747 var zindex = Number.parseInt(
29748 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29751 if (zindex > highest) {
29758 this.el.dom.style.zIndex = highest;
29760 this.el.alignTo(this.bindEl, align[0],align[1]);
29761 //var arrow = this.el.select('.arrow',true).first();
29762 //arrow.set(align[2],
29764 this.el.addClass(placement);
29765 this.el.addClass("bs-tooltip-"+ placement);
29767 this.el.addClass('in fade show');
29769 this.hoverState = null;
29771 if (this.el.hasClass('fade')) {
29786 //this.el.setXY([0,0]);
29787 this.el.removeClass(['show', 'in']);
29803 * @class Roo.bootstrap.LocationPicker
29804 * @extends Roo.bootstrap.Component
29805 * Bootstrap LocationPicker class
29806 * @cfg {Number} latitude Position when init default 0
29807 * @cfg {Number} longitude Position when init default 0
29808 * @cfg {Number} zoom default 15
29809 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29810 * @cfg {Boolean} mapTypeControl default false
29811 * @cfg {Boolean} disableDoubleClickZoom default false
29812 * @cfg {Boolean} scrollwheel default true
29813 * @cfg {Boolean} streetViewControl default false
29814 * @cfg {Number} radius default 0
29815 * @cfg {String} locationName
29816 * @cfg {Boolean} draggable default true
29817 * @cfg {Boolean} enableAutocomplete default false
29818 * @cfg {Boolean} enableReverseGeocode default true
29819 * @cfg {String} markerTitle
29822 * Create a new LocationPicker
29823 * @param {Object} config The config object
29827 Roo.bootstrap.LocationPicker = function(config){
29829 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29834 * Fires when the picker initialized.
29835 * @param {Roo.bootstrap.LocationPicker} this
29836 * @param {Google Location} location
29840 * @event positionchanged
29841 * Fires when the picker position changed.
29842 * @param {Roo.bootstrap.LocationPicker} this
29843 * @param {Google Location} location
29845 positionchanged : true,
29848 * Fires when the map resize.
29849 * @param {Roo.bootstrap.LocationPicker} this
29854 * Fires when the map show.
29855 * @param {Roo.bootstrap.LocationPicker} this
29860 * Fires when the map hide.
29861 * @param {Roo.bootstrap.LocationPicker} this
29866 * Fires when click the map.
29867 * @param {Roo.bootstrap.LocationPicker} this
29868 * @param {Map event} e
29872 * @event mapRightClick
29873 * Fires when right click the map.
29874 * @param {Roo.bootstrap.LocationPicker} this
29875 * @param {Map event} e
29877 mapRightClick : true,
29879 * @event markerClick
29880 * Fires when click the marker.
29881 * @param {Roo.bootstrap.LocationPicker} this
29882 * @param {Map event} e
29884 markerClick : true,
29886 * @event markerRightClick
29887 * Fires when right click the marker.
29888 * @param {Roo.bootstrap.LocationPicker} this
29889 * @param {Map event} e
29891 markerRightClick : true,
29893 * @event OverlayViewDraw
29894 * Fires when OverlayView Draw
29895 * @param {Roo.bootstrap.LocationPicker} this
29897 OverlayViewDraw : true,
29899 * @event OverlayViewOnAdd
29900 * Fires when OverlayView Draw
29901 * @param {Roo.bootstrap.LocationPicker} this
29903 OverlayViewOnAdd : true,
29905 * @event OverlayViewOnRemove
29906 * Fires when OverlayView Draw
29907 * @param {Roo.bootstrap.LocationPicker} this
29909 OverlayViewOnRemove : true,
29911 * @event OverlayViewShow
29912 * Fires when OverlayView Draw
29913 * @param {Roo.bootstrap.LocationPicker} this
29914 * @param {Pixel} cpx
29916 OverlayViewShow : true,
29918 * @event OverlayViewHide
29919 * Fires when OverlayView Draw
29920 * @param {Roo.bootstrap.LocationPicker} this
29922 OverlayViewHide : true,
29924 * @event loadexception
29925 * Fires when load google lib failed.
29926 * @param {Roo.bootstrap.LocationPicker} this
29928 loadexception : true
29933 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29935 gMapContext: false,
29941 mapTypeControl: false,
29942 disableDoubleClickZoom: false,
29944 streetViewControl: false,
29948 enableAutocomplete: false,
29949 enableReverseGeocode: true,
29952 getAutoCreate: function()
29957 cls: 'roo-location-picker'
29963 initEvents: function(ct, position)
29965 if(!this.el.getWidth() || this.isApplied()){
29969 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29974 initial: function()
29976 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29977 this.fireEvent('loadexception', this);
29981 if(!this.mapTypeId){
29982 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29985 this.gMapContext = this.GMapContext();
29987 this.initOverlayView();
29989 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29993 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29994 _this.setPosition(_this.gMapContext.marker.position);
29997 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29998 _this.fireEvent('mapClick', this, event);
30002 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30003 _this.fireEvent('mapRightClick', this, event);
30007 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30008 _this.fireEvent('markerClick', this, event);
30012 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30013 _this.fireEvent('markerRightClick', this, event);
30017 this.setPosition(this.gMapContext.location);
30019 this.fireEvent('initial', this, this.gMapContext.location);
30022 initOverlayView: function()
30026 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30030 _this.fireEvent('OverlayViewDraw', _this);
30035 _this.fireEvent('OverlayViewOnAdd', _this);
30038 onRemove: function()
30040 _this.fireEvent('OverlayViewOnRemove', _this);
30043 show: function(cpx)
30045 _this.fireEvent('OverlayViewShow', _this, cpx);
30050 _this.fireEvent('OverlayViewHide', _this);
30056 fromLatLngToContainerPixel: function(event)
30058 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30061 isApplied: function()
30063 return this.getGmapContext() == false ? false : true;
30066 getGmapContext: function()
30068 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30071 GMapContext: function()
30073 var position = new google.maps.LatLng(this.latitude, this.longitude);
30075 var _map = new google.maps.Map(this.el.dom, {
30078 mapTypeId: this.mapTypeId,
30079 mapTypeControl: this.mapTypeControl,
30080 disableDoubleClickZoom: this.disableDoubleClickZoom,
30081 scrollwheel: this.scrollwheel,
30082 streetViewControl: this.streetViewControl,
30083 locationName: this.locationName,
30084 draggable: this.draggable,
30085 enableAutocomplete: this.enableAutocomplete,
30086 enableReverseGeocode: this.enableReverseGeocode
30089 var _marker = new google.maps.Marker({
30090 position: position,
30092 title: this.markerTitle,
30093 draggable: this.draggable
30100 location: position,
30101 radius: this.radius,
30102 locationName: this.locationName,
30103 addressComponents: {
30104 formatted_address: null,
30105 addressLine1: null,
30106 addressLine2: null,
30108 streetNumber: null,
30112 stateOrProvince: null
30115 domContainer: this.el.dom,
30116 geodecoder: new google.maps.Geocoder()
30120 drawCircle: function(center, radius, options)
30122 if (this.gMapContext.circle != null) {
30123 this.gMapContext.circle.setMap(null);
30127 options = Roo.apply({}, options, {
30128 strokeColor: "#0000FF",
30129 strokeOpacity: .35,
30131 fillColor: "#0000FF",
30135 options.map = this.gMapContext.map;
30136 options.radius = radius;
30137 options.center = center;
30138 this.gMapContext.circle = new google.maps.Circle(options);
30139 return this.gMapContext.circle;
30145 setPosition: function(location)
30147 this.gMapContext.location = location;
30148 this.gMapContext.marker.setPosition(location);
30149 this.gMapContext.map.panTo(location);
30150 this.drawCircle(location, this.gMapContext.radius, {});
30154 if (this.gMapContext.settings.enableReverseGeocode) {
30155 this.gMapContext.geodecoder.geocode({
30156 latLng: this.gMapContext.location
30157 }, function(results, status) {
30159 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30160 _this.gMapContext.locationName = results[0].formatted_address;
30161 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30163 _this.fireEvent('positionchanged', this, location);
30170 this.fireEvent('positionchanged', this, location);
30175 google.maps.event.trigger(this.gMapContext.map, "resize");
30177 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30179 this.fireEvent('resize', this);
30182 setPositionByLatLng: function(latitude, longitude)
30184 this.setPosition(new google.maps.LatLng(latitude, longitude));
30187 getCurrentPosition: function()
30190 latitude: this.gMapContext.location.lat(),
30191 longitude: this.gMapContext.location.lng()
30195 getAddressName: function()
30197 return this.gMapContext.locationName;
30200 getAddressComponents: function()
30202 return this.gMapContext.addressComponents;
30205 address_component_from_google_geocode: function(address_components)
30209 for (var i = 0; i < address_components.length; i++) {
30210 var component = address_components[i];
30211 if (component.types.indexOf("postal_code") >= 0) {
30212 result.postalCode = component.short_name;
30213 } else if (component.types.indexOf("street_number") >= 0) {
30214 result.streetNumber = component.short_name;
30215 } else if (component.types.indexOf("route") >= 0) {
30216 result.streetName = component.short_name;
30217 } else if (component.types.indexOf("neighborhood") >= 0) {
30218 result.city = component.short_name;
30219 } else if (component.types.indexOf("locality") >= 0) {
30220 result.city = component.short_name;
30221 } else if (component.types.indexOf("sublocality") >= 0) {
30222 result.district = component.short_name;
30223 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30224 result.stateOrProvince = component.short_name;
30225 } else if (component.types.indexOf("country") >= 0) {
30226 result.country = component.short_name;
30230 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30231 result.addressLine2 = "";
30235 setZoomLevel: function(zoom)
30237 this.gMapContext.map.setZoom(zoom);
30250 this.fireEvent('show', this);
30261 this.fireEvent('hide', this);
30266 Roo.apply(Roo.bootstrap.LocationPicker, {
30268 OverlayView : function(map, options)
30270 options = options || {};
30277 * @class Roo.bootstrap.Alert
30278 * @extends Roo.bootstrap.Component
30279 * Bootstrap Alert class - shows an alert area box
30281 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30282 Enter a valid email address
30285 * @cfg {String} title The title of alert
30286 * @cfg {String} html The content of alert
30287 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30288 * @cfg {String} fa font-awesomeicon
30289 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30290 * @cfg {Boolean} close true to show a x closer
30294 * Create a new alert
30295 * @param {Object} config The config object
30299 Roo.bootstrap.Alert = function(config){
30300 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30304 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30310 faicon: false, // BC
30314 getAutoCreate : function()
30326 style : this.close ? '' : 'display:none'
30330 cls : 'roo-alert-icon'
30335 cls : 'roo-alert-title',
30340 cls : 'roo-alert-text',
30347 cfg.cn[0].cls += ' fa ' + this.faicon;
30350 cfg.cn[0].cls += ' fa ' + this.fa;
30354 cfg.cls += ' alert-' + this.weight;
30360 initEvents: function()
30362 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30363 this.titleEl = this.el.select('.roo-alert-title',true).first();
30364 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30365 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30366 if (this.seconds > 0) {
30367 this.hide.defer(this.seconds, this);
30371 * Set the Title Message HTML
30372 * @param {String} html
30374 setTitle : function(str)
30376 this.titleEl.dom.innerHTML = str;
30380 * Set the Body Message HTML
30381 * @param {String} html
30383 setHtml : function(str)
30385 this.htmlEl.dom.innerHTML = str;
30388 * Set the Weight of the alert
30389 * @param {String} (success|info|warning|danger) weight
30392 setWeight : function(weight)
30395 this.el.removeClass('alert-' + this.weight);
30398 this.weight = weight;
30400 this.el.addClass('alert-' + this.weight);
30403 * Set the Icon of the alert
30404 * @param {String} see fontawsome names (name without the 'fa-' bit)
30406 setIcon : function(icon)
30409 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30412 this.faicon = icon;
30414 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30439 * @class Roo.bootstrap.UploadCropbox
30440 * @extends Roo.bootstrap.Component
30441 * Bootstrap UploadCropbox class
30442 * @cfg {String} emptyText show when image has been loaded
30443 * @cfg {String} rotateNotify show when image too small to rotate
30444 * @cfg {Number} errorTimeout default 3000
30445 * @cfg {Number} minWidth default 300
30446 * @cfg {Number} minHeight default 300
30447 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30448 * @cfg {Boolean} isDocument (true|false) default false
30449 * @cfg {String} url action url
30450 * @cfg {String} paramName default 'imageUpload'
30451 * @cfg {String} method default POST
30452 * @cfg {Boolean} loadMask (true|false) default true
30453 * @cfg {Boolean} loadingText default 'Loading...'
30456 * Create a new UploadCropbox
30457 * @param {Object} config The config object
30460 Roo.bootstrap.UploadCropbox = function(config){
30461 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30465 * @event beforeselectfile
30466 * Fire before select file
30467 * @param {Roo.bootstrap.UploadCropbox} this
30469 "beforeselectfile" : true,
30472 * Fire after initEvent
30473 * @param {Roo.bootstrap.UploadCropbox} this
30478 * Fire after initEvent
30479 * @param {Roo.bootstrap.UploadCropbox} this
30480 * @param {String} data
30485 * Fire when preparing the file data
30486 * @param {Roo.bootstrap.UploadCropbox} this
30487 * @param {Object} file
30492 * Fire when get exception
30493 * @param {Roo.bootstrap.UploadCropbox} this
30494 * @param {XMLHttpRequest} xhr
30496 "exception" : true,
30498 * @event beforeloadcanvas
30499 * Fire before load the canvas
30500 * @param {Roo.bootstrap.UploadCropbox} this
30501 * @param {String} src
30503 "beforeloadcanvas" : true,
30506 * Fire when trash image
30507 * @param {Roo.bootstrap.UploadCropbox} this
30512 * Fire when download the image
30513 * @param {Roo.bootstrap.UploadCropbox} this
30517 * @event footerbuttonclick
30518 * Fire when footerbuttonclick
30519 * @param {Roo.bootstrap.UploadCropbox} this
30520 * @param {String} type
30522 "footerbuttonclick" : true,
30526 * @param {Roo.bootstrap.UploadCropbox} this
30531 * Fire when rotate the image
30532 * @param {Roo.bootstrap.UploadCropbox} this
30533 * @param {String} pos
30538 * Fire when inspect the file
30539 * @param {Roo.bootstrap.UploadCropbox} this
30540 * @param {Object} file
30545 * Fire when xhr upload the file
30546 * @param {Roo.bootstrap.UploadCropbox} this
30547 * @param {Object} data
30552 * Fire when arrange the file data
30553 * @param {Roo.bootstrap.UploadCropbox} this
30554 * @param {Object} formData
30559 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30562 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30564 emptyText : 'Click to upload image',
30565 rotateNotify : 'Image is too small to rotate',
30566 errorTimeout : 3000,
30580 cropType : 'image/jpeg',
30582 canvasLoaded : false,
30583 isDocument : false,
30585 paramName : 'imageUpload',
30587 loadingText : 'Loading...',
30590 getAutoCreate : function()
30594 cls : 'roo-upload-cropbox',
30598 cls : 'roo-upload-cropbox-selector',
30603 cls : 'roo-upload-cropbox-body',
30604 style : 'cursor:pointer',
30608 cls : 'roo-upload-cropbox-preview'
30612 cls : 'roo-upload-cropbox-thumb'
30616 cls : 'roo-upload-cropbox-empty-notify',
30617 html : this.emptyText
30621 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30622 html : this.rotateNotify
30628 cls : 'roo-upload-cropbox-footer',
30631 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30641 onRender : function(ct, position)
30643 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30645 if (this.buttons.length) {
30647 Roo.each(this.buttons, function(bb) {
30649 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30651 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30657 this.maskEl = this.el;
30661 initEvents : function()
30663 this.urlAPI = (window.createObjectURL && window) ||
30664 (window.URL && URL.revokeObjectURL && URL) ||
30665 (window.webkitURL && webkitURL);
30667 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30668 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30670 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30671 this.selectorEl.hide();
30673 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30674 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30676 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30677 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30678 this.thumbEl.hide();
30680 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30681 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30683 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30684 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30685 this.errorEl.hide();
30687 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30688 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30689 this.footerEl.hide();
30691 this.setThumbBoxSize();
30697 this.fireEvent('initial', this);
30704 window.addEventListener("resize", function() { _this.resize(); } );
30706 this.bodyEl.on('click', this.beforeSelectFile, this);
30709 this.bodyEl.on('touchstart', this.onTouchStart, this);
30710 this.bodyEl.on('touchmove', this.onTouchMove, this);
30711 this.bodyEl.on('touchend', this.onTouchEnd, this);
30715 this.bodyEl.on('mousedown', this.onMouseDown, this);
30716 this.bodyEl.on('mousemove', this.onMouseMove, this);
30717 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30718 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30719 Roo.get(document).on('mouseup', this.onMouseUp, this);
30722 this.selectorEl.on('change', this.onFileSelected, this);
30728 this.baseScale = 1;
30730 this.baseRotate = 1;
30731 this.dragable = false;
30732 this.pinching = false;
30735 this.cropData = false;
30736 this.notifyEl.dom.innerHTML = this.emptyText;
30738 this.selectorEl.dom.value = '';
30742 resize : function()
30744 if(this.fireEvent('resize', this) != false){
30745 this.setThumbBoxPosition();
30746 this.setCanvasPosition();
30750 onFooterButtonClick : function(e, el, o, type)
30753 case 'rotate-left' :
30754 this.onRotateLeft(e);
30756 case 'rotate-right' :
30757 this.onRotateRight(e);
30760 this.beforeSelectFile(e);
30775 this.fireEvent('footerbuttonclick', this, type);
30778 beforeSelectFile : function(e)
30780 e.preventDefault();
30782 if(this.fireEvent('beforeselectfile', this) != false){
30783 this.selectorEl.dom.click();
30787 onFileSelected : function(e)
30789 e.preventDefault();
30791 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30795 var file = this.selectorEl.dom.files[0];
30797 if(this.fireEvent('inspect', this, file) != false){
30798 this.prepare(file);
30803 trash : function(e)
30805 this.fireEvent('trash', this);
30808 download : function(e)
30810 this.fireEvent('download', this);
30813 loadCanvas : function(src)
30815 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30819 this.imageEl = document.createElement('img');
30823 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30825 this.imageEl.src = src;
30829 onLoadCanvas : function()
30831 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30832 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30834 this.bodyEl.un('click', this.beforeSelectFile, this);
30836 this.notifyEl.hide();
30837 this.thumbEl.show();
30838 this.footerEl.show();
30840 this.baseRotateLevel();
30842 if(this.isDocument){
30843 this.setThumbBoxSize();
30846 this.setThumbBoxPosition();
30848 this.baseScaleLevel();
30854 this.canvasLoaded = true;
30857 this.maskEl.unmask();
30862 setCanvasPosition : function()
30864 if(!this.canvasEl){
30868 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30869 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30871 this.previewEl.setLeft(pw);
30872 this.previewEl.setTop(ph);
30876 onMouseDown : function(e)
30880 this.dragable = true;
30881 this.pinching = false;
30883 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30884 this.dragable = false;
30888 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30889 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30893 onMouseMove : function(e)
30897 if(!this.canvasLoaded){
30901 if (!this.dragable){
30905 var minX = Math.ceil(this.thumbEl.getLeft(true));
30906 var minY = Math.ceil(this.thumbEl.getTop(true));
30908 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30909 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30911 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30912 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30914 x = x - this.mouseX;
30915 y = y - this.mouseY;
30917 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30918 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30920 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30921 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30923 this.previewEl.setLeft(bgX);
30924 this.previewEl.setTop(bgY);
30926 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30927 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30930 onMouseUp : function(e)
30934 this.dragable = false;
30937 onMouseWheel : function(e)
30941 this.startScale = this.scale;
30943 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30945 if(!this.zoomable()){
30946 this.scale = this.startScale;
30955 zoomable : function()
30957 var minScale = this.thumbEl.getWidth() / this.minWidth;
30959 if(this.minWidth < this.minHeight){
30960 minScale = this.thumbEl.getHeight() / this.minHeight;
30963 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30964 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30968 (this.rotate == 0 || this.rotate == 180) &&
30970 width > this.imageEl.OriginWidth ||
30971 height > this.imageEl.OriginHeight ||
30972 (width < this.minWidth && height < this.minHeight)
30980 (this.rotate == 90 || this.rotate == 270) &&
30982 width > this.imageEl.OriginWidth ||
30983 height > this.imageEl.OriginHeight ||
30984 (width < this.minHeight && height < this.minWidth)
30991 !this.isDocument &&
30992 (this.rotate == 0 || this.rotate == 180) &&
30994 width < this.minWidth ||
30995 width > this.imageEl.OriginWidth ||
30996 height < this.minHeight ||
30997 height > this.imageEl.OriginHeight
31004 !this.isDocument &&
31005 (this.rotate == 90 || this.rotate == 270) &&
31007 width < this.minHeight ||
31008 width > this.imageEl.OriginWidth ||
31009 height < this.minWidth ||
31010 height > this.imageEl.OriginHeight
31020 onRotateLeft : function(e)
31022 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31024 var minScale = this.thumbEl.getWidth() / this.minWidth;
31026 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31027 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31029 this.startScale = this.scale;
31031 while (this.getScaleLevel() < minScale){
31033 this.scale = this.scale + 1;
31035 if(!this.zoomable()){
31040 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31041 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31046 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31053 this.scale = this.startScale;
31055 this.onRotateFail();
31060 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31062 if(this.isDocument){
31063 this.setThumbBoxSize();
31064 this.setThumbBoxPosition();
31065 this.setCanvasPosition();
31070 this.fireEvent('rotate', this, 'left');
31074 onRotateRight : function(e)
31076 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31078 var minScale = this.thumbEl.getWidth() / this.minWidth;
31080 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31081 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31083 this.startScale = this.scale;
31085 while (this.getScaleLevel() < minScale){
31087 this.scale = this.scale + 1;
31089 if(!this.zoomable()){
31094 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31095 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31100 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31107 this.scale = this.startScale;
31109 this.onRotateFail();
31114 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31116 if(this.isDocument){
31117 this.setThumbBoxSize();
31118 this.setThumbBoxPosition();
31119 this.setCanvasPosition();
31124 this.fireEvent('rotate', this, 'right');
31127 onRotateFail : function()
31129 this.errorEl.show(true);
31133 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31138 this.previewEl.dom.innerHTML = '';
31140 var canvasEl = document.createElement("canvas");
31142 var contextEl = canvasEl.getContext("2d");
31144 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31145 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31146 var center = this.imageEl.OriginWidth / 2;
31148 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31149 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31150 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31151 center = this.imageEl.OriginHeight / 2;
31154 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31156 contextEl.translate(center, center);
31157 contextEl.rotate(this.rotate * Math.PI / 180);
31159 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31161 this.canvasEl = document.createElement("canvas");
31163 this.contextEl = this.canvasEl.getContext("2d");
31165 switch (this.rotate) {
31168 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31169 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31171 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31176 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31177 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31179 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31180 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);
31184 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31189 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31190 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31192 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31193 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);
31197 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);
31202 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31203 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31205 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31206 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31210 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);
31217 this.previewEl.appendChild(this.canvasEl);
31219 this.setCanvasPosition();
31224 if(!this.canvasLoaded){
31228 var imageCanvas = document.createElement("canvas");
31230 var imageContext = imageCanvas.getContext("2d");
31232 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31233 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31235 var center = imageCanvas.width / 2;
31237 imageContext.translate(center, center);
31239 imageContext.rotate(this.rotate * Math.PI / 180);
31241 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31243 var canvas = document.createElement("canvas");
31245 var context = canvas.getContext("2d");
31247 canvas.width = this.minWidth;
31248 canvas.height = this.minHeight;
31250 switch (this.rotate) {
31253 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31254 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31256 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31257 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31259 var targetWidth = this.minWidth - 2 * x;
31260 var targetHeight = this.minHeight - 2 * y;
31264 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31265 scale = targetWidth / width;
31268 if(x > 0 && y == 0){
31269 scale = targetHeight / height;
31272 if(x > 0 && y > 0){
31273 scale = targetWidth / width;
31275 if(width < height){
31276 scale = targetHeight / height;
31280 context.scale(scale, scale);
31282 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31283 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31285 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31286 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31288 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31293 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31294 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31296 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31297 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31299 var targetWidth = this.minWidth - 2 * x;
31300 var targetHeight = this.minHeight - 2 * y;
31304 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31305 scale = targetWidth / width;
31308 if(x > 0 && y == 0){
31309 scale = targetHeight / height;
31312 if(x > 0 && y > 0){
31313 scale = targetWidth / width;
31315 if(width < height){
31316 scale = targetHeight / height;
31320 context.scale(scale, scale);
31322 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31323 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31325 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31326 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31328 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31330 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31335 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31336 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31338 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31339 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31341 var targetWidth = this.minWidth - 2 * x;
31342 var targetHeight = this.minHeight - 2 * y;
31346 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31347 scale = targetWidth / width;
31350 if(x > 0 && y == 0){
31351 scale = targetHeight / height;
31354 if(x > 0 && y > 0){
31355 scale = targetWidth / width;
31357 if(width < height){
31358 scale = targetHeight / height;
31362 context.scale(scale, scale);
31364 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31365 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31367 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31368 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31370 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31371 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31373 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31378 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31379 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31381 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31382 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31384 var targetWidth = this.minWidth - 2 * x;
31385 var targetHeight = this.minHeight - 2 * y;
31389 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31390 scale = targetWidth / width;
31393 if(x > 0 && y == 0){
31394 scale = targetHeight / height;
31397 if(x > 0 && y > 0){
31398 scale = targetWidth / width;
31400 if(width < height){
31401 scale = targetHeight / height;
31405 context.scale(scale, scale);
31407 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31408 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31410 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31411 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31413 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31415 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31422 this.cropData = canvas.toDataURL(this.cropType);
31424 if(this.fireEvent('crop', this, this.cropData) !== false){
31425 this.process(this.file, this.cropData);
31432 setThumbBoxSize : function()
31436 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31437 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31438 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31440 this.minWidth = width;
31441 this.minHeight = height;
31443 if(this.rotate == 90 || this.rotate == 270){
31444 this.minWidth = height;
31445 this.minHeight = width;
31450 width = Math.ceil(this.minWidth * height / this.minHeight);
31452 if(this.minWidth > this.minHeight){
31454 height = Math.ceil(this.minHeight * width / this.minWidth);
31457 this.thumbEl.setStyle({
31458 width : width + 'px',
31459 height : height + 'px'
31466 setThumbBoxPosition : function()
31468 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31469 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31471 this.thumbEl.setLeft(x);
31472 this.thumbEl.setTop(y);
31476 baseRotateLevel : function()
31478 this.baseRotate = 1;
31481 typeof(this.exif) != 'undefined' &&
31482 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31483 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31485 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31488 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31492 baseScaleLevel : function()
31496 if(this.isDocument){
31498 if(this.baseRotate == 6 || this.baseRotate == 8){
31500 height = this.thumbEl.getHeight();
31501 this.baseScale = height / this.imageEl.OriginWidth;
31503 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31504 width = this.thumbEl.getWidth();
31505 this.baseScale = width / this.imageEl.OriginHeight;
31511 height = this.thumbEl.getHeight();
31512 this.baseScale = height / this.imageEl.OriginHeight;
31514 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31515 width = this.thumbEl.getWidth();
31516 this.baseScale = width / this.imageEl.OriginWidth;
31522 if(this.baseRotate == 6 || this.baseRotate == 8){
31524 width = this.thumbEl.getHeight();
31525 this.baseScale = width / this.imageEl.OriginHeight;
31527 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31528 height = this.thumbEl.getWidth();
31529 this.baseScale = height / this.imageEl.OriginHeight;
31532 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31533 height = this.thumbEl.getWidth();
31534 this.baseScale = height / this.imageEl.OriginHeight;
31536 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31537 width = this.thumbEl.getHeight();
31538 this.baseScale = width / this.imageEl.OriginWidth;
31545 width = this.thumbEl.getWidth();
31546 this.baseScale = width / this.imageEl.OriginWidth;
31548 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31549 height = this.thumbEl.getHeight();
31550 this.baseScale = height / this.imageEl.OriginHeight;
31553 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31555 height = this.thumbEl.getHeight();
31556 this.baseScale = height / this.imageEl.OriginHeight;
31558 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31559 width = this.thumbEl.getWidth();
31560 this.baseScale = width / this.imageEl.OriginWidth;
31568 getScaleLevel : function()
31570 return this.baseScale * Math.pow(1.1, this.scale);
31573 onTouchStart : function(e)
31575 if(!this.canvasLoaded){
31576 this.beforeSelectFile(e);
31580 var touches = e.browserEvent.touches;
31586 if(touches.length == 1){
31587 this.onMouseDown(e);
31591 if(touches.length != 2){
31597 for(var i = 0, finger; finger = touches[i]; i++){
31598 coords.push(finger.pageX, finger.pageY);
31601 var x = Math.pow(coords[0] - coords[2], 2);
31602 var y = Math.pow(coords[1] - coords[3], 2);
31604 this.startDistance = Math.sqrt(x + y);
31606 this.startScale = this.scale;
31608 this.pinching = true;
31609 this.dragable = false;
31613 onTouchMove : function(e)
31615 if(!this.pinching && !this.dragable){
31619 var touches = e.browserEvent.touches;
31626 this.onMouseMove(e);
31632 for(var i = 0, finger; finger = touches[i]; i++){
31633 coords.push(finger.pageX, finger.pageY);
31636 var x = Math.pow(coords[0] - coords[2], 2);
31637 var y = Math.pow(coords[1] - coords[3], 2);
31639 this.endDistance = Math.sqrt(x + y);
31641 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31643 if(!this.zoomable()){
31644 this.scale = this.startScale;
31652 onTouchEnd : function(e)
31654 this.pinching = false;
31655 this.dragable = false;
31659 process : function(file, crop)
31662 this.maskEl.mask(this.loadingText);
31665 this.xhr = new XMLHttpRequest();
31667 file.xhr = this.xhr;
31669 this.xhr.open(this.method, this.url, true);
31672 "Accept": "application/json",
31673 "Cache-Control": "no-cache",
31674 "X-Requested-With": "XMLHttpRequest"
31677 for (var headerName in headers) {
31678 var headerValue = headers[headerName];
31680 this.xhr.setRequestHeader(headerName, headerValue);
31686 this.xhr.onload = function()
31688 _this.xhrOnLoad(_this.xhr);
31691 this.xhr.onerror = function()
31693 _this.xhrOnError(_this.xhr);
31696 var formData = new FormData();
31698 formData.append('returnHTML', 'NO');
31701 formData.append('crop', crop);
31704 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31705 formData.append(this.paramName, file, file.name);
31708 if(typeof(file.filename) != 'undefined'){
31709 formData.append('filename', file.filename);
31712 if(typeof(file.mimetype) != 'undefined'){
31713 formData.append('mimetype', file.mimetype);
31716 if(this.fireEvent('arrange', this, formData) != false){
31717 this.xhr.send(formData);
31721 xhrOnLoad : function(xhr)
31724 this.maskEl.unmask();
31727 if (xhr.readyState !== 4) {
31728 this.fireEvent('exception', this, xhr);
31732 var response = Roo.decode(xhr.responseText);
31734 if(!response.success){
31735 this.fireEvent('exception', this, xhr);
31739 var response = Roo.decode(xhr.responseText);
31741 this.fireEvent('upload', this, response);
31745 xhrOnError : function()
31748 this.maskEl.unmask();
31751 Roo.log('xhr on error');
31753 var response = Roo.decode(xhr.responseText);
31759 prepare : function(file)
31762 this.maskEl.mask(this.loadingText);
31768 if(typeof(file) === 'string'){
31769 this.loadCanvas(file);
31773 if(!file || !this.urlAPI){
31778 this.cropType = file.type;
31782 if(this.fireEvent('prepare', this, this.file) != false){
31784 var reader = new FileReader();
31786 reader.onload = function (e) {
31787 if (e.target.error) {
31788 Roo.log(e.target.error);
31792 var buffer = e.target.result,
31793 dataView = new DataView(buffer),
31795 maxOffset = dataView.byteLength - 4,
31799 if (dataView.getUint16(0) === 0xffd8) {
31800 while (offset < maxOffset) {
31801 markerBytes = dataView.getUint16(offset);
31803 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31804 markerLength = dataView.getUint16(offset + 2) + 2;
31805 if (offset + markerLength > dataView.byteLength) {
31806 Roo.log('Invalid meta data: Invalid segment size.');
31810 if(markerBytes == 0xffe1){
31811 _this.parseExifData(
31818 offset += markerLength;
31828 var url = _this.urlAPI.createObjectURL(_this.file);
31830 _this.loadCanvas(url);
31835 reader.readAsArrayBuffer(this.file);
31841 parseExifData : function(dataView, offset, length)
31843 var tiffOffset = offset + 10,
31847 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31848 // No Exif data, might be XMP data instead
31852 // Check for the ASCII code for "Exif" (0x45786966):
31853 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31854 // No Exif data, might be XMP data instead
31857 if (tiffOffset + 8 > dataView.byteLength) {
31858 Roo.log('Invalid Exif data: Invalid segment size.');
31861 // Check for the two null bytes:
31862 if (dataView.getUint16(offset + 8) !== 0x0000) {
31863 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31866 // Check the byte alignment:
31867 switch (dataView.getUint16(tiffOffset)) {
31869 littleEndian = true;
31872 littleEndian = false;
31875 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31878 // Check for the TIFF tag marker (0x002A):
31879 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31880 Roo.log('Invalid Exif data: Missing TIFF marker.');
31883 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31884 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31886 this.parseExifTags(
31889 tiffOffset + dirOffset,
31894 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31899 if (dirOffset + 6 > dataView.byteLength) {
31900 Roo.log('Invalid Exif data: Invalid directory offset.');
31903 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31904 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31905 if (dirEndOffset + 4 > dataView.byteLength) {
31906 Roo.log('Invalid Exif data: Invalid directory size.');
31909 for (i = 0; i < tagsNumber; i += 1) {
31913 dirOffset + 2 + 12 * i, // tag offset
31917 // Return the offset to the next directory:
31918 return dataView.getUint32(dirEndOffset, littleEndian);
31921 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31923 var tag = dataView.getUint16(offset, littleEndian);
31925 this.exif[tag] = this.getExifValue(
31929 dataView.getUint16(offset + 2, littleEndian), // tag type
31930 dataView.getUint32(offset + 4, littleEndian), // tag length
31935 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31937 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31946 Roo.log('Invalid Exif data: Invalid tag type.');
31950 tagSize = tagType.size * length;
31951 // Determine if the value is contained in the dataOffset bytes,
31952 // or if the value at the dataOffset is a pointer to the actual data:
31953 dataOffset = tagSize > 4 ?
31954 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31955 if (dataOffset + tagSize > dataView.byteLength) {
31956 Roo.log('Invalid Exif data: Invalid data offset.');
31959 if (length === 1) {
31960 return tagType.getValue(dataView, dataOffset, littleEndian);
31963 for (i = 0; i < length; i += 1) {
31964 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31967 if (tagType.ascii) {
31969 // Concatenate the chars:
31970 for (i = 0; i < values.length; i += 1) {
31972 // Ignore the terminating NULL byte(s):
31973 if (c === '\u0000') {
31985 Roo.apply(Roo.bootstrap.UploadCropbox, {
31987 'Orientation': 0x0112
31991 1: 0, //'top-left',
31993 3: 180, //'bottom-right',
31994 // 4: 'bottom-left',
31996 6: 90, //'right-top',
31997 // 7: 'right-bottom',
31998 8: 270 //'left-bottom'
32002 // byte, 8-bit unsigned int:
32004 getValue: function (dataView, dataOffset) {
32005 return dataView.getUint8(dataOffset);
32009 // ascii, 8-bit byte:
32011 getValue: function (dataView, dataOffset) {
32012 return String.fromCharCode(dataView.getUint8(dataOffset));
32017 // short, 16 bit int:
32019 getValue: function (dataView, dataOffset, littleEndian) {
32020 return dataView.getUint16(dataOffset, littleEndian);
32024 // long, 32 bit int:
32026 getValue: function (dataView, dataOffset, littleEndian) {
32027 return dataView.getUint32(dataOffset, littleEndian);
32031 // rational = two long values, first is numerator, second is denominator:
32033 getValue: function (dataView, dataOffset, littleEndian) {
32034 return dataView.getUint32(dataOffset, littleEndian) /
32035 dataView.getUint32(dataOffset + 4, littleEndian);
32039 // slong, 32 bit signed int:
32041 getValue: function (dataView, dataOffset, littleEndian) {
32042 return dataView.getInt32(dataOffset, littleEndian);
32046 // srational, two slongs, first is numerator, second is denominator:
32048 getValue: function (dataView, dataOffset, littleEndian) {
32049 return dataView.getInt32(dataOffset, littleEndian) /
32050 dataView.getInt32(dataOffset + 4, littleEndian);
32060 cls : 'btn-group roo-upload-cropbox-rotate-left',
32061 action : 'rotate-left',
32065 cls : 'btn btn-default',
32066 html : '<i class="fa fa-undo"></i>'
32072 cls : 'btn-group roo-upload-cropbox-picture',
32073 action : 'picture',
32077 cls : 'btn btn-default',
32078 html : '<i class="fa fa-picture-o"></i>'
32084 cls : 'btn-group roo-upload-cropbox-rotate-right',
32085 action : 'rotate-right',
32089 cls : 'btn btn-default',
32090 html : '<i class="fa fa-repeat"></i>'
32098 cls : 'btn-group roo-upload-cropbox-rotate-left',
32099 action : 'rotate-left',
32103 cls : 'btn btn-default',
32104 html : '<i class="fa fa-undo"></i>'
32110 cls : 'btn-group roo-upload-cropbox-download',
32111 action : 'download',
32115 cls : 'btn btn-default',
32116 html : '<i class="fa fa-download"></i>'
32122 cls : 'btn-group roo-upload-cropbox-crop',
32127 cls : 'btn btn-default',
32128 html : '<i class="fa fa-crop"></i>'
32134 cls : 'btn-group roo-upload-cropbox-trash',
32139 cls : 'btn btn-default',
32140 html : '<i class="fa fa-trash"></i>'
32146 cls : 'btn-group roo-upload-cropbox-rotate-right',
32147 action : 'rotate-right',
32151 cls : 'btn btn-default',
32152 html : '<i class="fa fa-repeat"></i>'
32160 cls : 'btn-group roo-upload-cropbox-rotate-left',
32161 action : 'rotate-left',
32165 cls : 'btn btn-default',
32166 html : '<i class="fa fa-undo"></i>'
32172 cls : 'btn-group roo-upload-cropbox-rotate-right',
32173 action : 'rotate-right',
32177 cls : 'btn btn-default',
32178 html : '<i class="fa fa-repeat"></i>'
32191 * @class Roo.bootstrap.DocumentManager
32192 * @extends Roo.bootstrap.Component
32193 * Bootstrap DocumentManager class
32194 * @cfg {String} paramName default 'imageUpload'
32195 * @cfg {String} toolTipName default 'filename'
32196 * @cfg {String} method default POST
32197 * @cfg {String} url action url
32198 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32199 * @cfg {Boolean} multiple multiple upload default true
32200 * @cfg {Number} thumbSize default 300
32201 * @cfg {String} fieldLabel
32202 * @cfg {Number} labelWidth default 4
32203 * @cfg {String} labelAlign (left|top) default left
32204 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32205 * @cfg {Number} labellg set the width of label (1-12)
32206 * @cfg {Number} labelmd set the width of label (1-12)
32207 * @cfg {Number} labelsm set the width of label (1-12)
32208 * @cfg {Number} labelxs set the width of label (1-12)
32211 * Create a new DocumentManager
32212 * @param {Object} config The config object
32215 Roo.bootstrap.DocumentManager = function(config){
32216 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32219 this.delegates = [];
32224 * Fire when initial the DocumentManager
32225 * @param {Roo.bootstrap.DocumentManager} this
32230 * inspect selected file
32231 * @param {Roo.bootstrap.DocumentManager} this
32232 * @param {File} file
32237 * Fire when xhr load exception
32238 * @param {Roo.bootstrap.DocumentManager} this
32239 * @param {XMLHttpRequest} xhr
32241 "exception" : true,
32243 * @event afterupload
32244 * Fire when xhr load exception
32245 * @param {Roo.bootstrap.DocumentManager} this
32246 * @param {XMLHttpRequest} xhr
32248 "afterupload" : true,
32251 * prepare the form data
32252 * @param {Roo.bootstrap.DocumentManager} this
32253 * @param {Object} formData
32258 * Fire when remove the file
32259 * @param {Roo.bootstrap.DocumentManager} this
32260 * @param {Object} file
32265 * Fire after refresh the file
32266 * @param {Roo.bootstrap.DocumentManager} this
32271 * Fire after click the image
32272 * @param {Roo.bootstrap.DocumentManager} this
32273 * @param {Object} file
32278 * Fire when upload a image and editable set to true
32279 * @param {Roo.bootstrap.DocumentManager} this
32280 * @param {Object} file
32284 * @event beforeselectfile
32285 * Fire before select file
32286 * @param {Roo.bootstrap.DocumentManager} this
32288 "beforeselectfile" : true,
32291 * Fire before process file
32292 * @param {Roo.bootstrap.DocumentManager} this
32293 * @param {Object} file
32297 * @event previewrendered
32298 * Fire when preview rendered
32299 * @param {Roo.bootstrap.DocumentManager} this
32300 * @param {Object} file
32302 "previewrendered" : true,
32305 "previewResize" : true
32310 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32319 paramName : 'imageUpload',
32320 toolTipName : 'filename',
32323 labelAlign : 'left',
32333 getAutoCreate : function()
32335 var managerWidget = {
32337 cls : 'roo-document-manager',
32341 cls : 'roo-document-manager-selector',
32346 cls : 'roo-document-manager-uploader',
32350 cls : 'roo-document-manager-upload-btn',
32351 html : '<i class="fa fa-plus"></i>'
32362 cls : 'column col-md-12',
32367 if(this.fieldLabel.length){
32372 cls : 'column col-md-12',
32373 html : this.fieldLabel
32377 cls : 'column col-md-12',
32382 if(this.labelAlign == 'left'){
32387 html : this.fieldLabel
32396 if(this.labelWidth > 12){
32397 content[0].style = "width: " + this.labelWidth + 'px';
32400 if(this.labelWidth < 13 && this.labelmd == 0){
32401 this.labelmd = this.labelWidth;
32404 if(this.labellg > 0){
32405 content[0].cls += ' col-lg-' + this.labellg;
32406 content[1].cls += ' col-lg-' + (12 - this.labellg);
32409 if(this.labelmd > 0){
32410 content[0].cls += ' col-md-' + this.labelmd;
32411 content[1].cls += ' col-md-' + (12 - this.labelmd);
32414 if(this.labelsm > 0){
32415 content[0].cls += ' col-sm-' + this.labelsm;
32416 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32419 if(this.labelxs > 0){
32420 content[0].cls += ' col-xs-' + this.labelxs;
32421 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32429 cls : 'row clearfix',
32437 initEvents : function()
32439 this.managerEl = this.el.select('.roo-document-manager', true).first();
32440 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32442 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32443 this.selectorEl.hide();
32446 this.selectorEl.attr('multiple', 'multiple');
32449 this.selectorEl.on('change', this.onFileSelected, this);
32451 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32452 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32454 this.uploader.on('click', this.onUploaderClick, this);
32456 this.renderProgressDialog();
32460 window.addEventListener("resize", function() { _this.refresh(); } );
32462 this.fireEvent('initial', this);
32465 renderProgressDialog : function()
32469 this.progressDialog = new Roo.bootstrap.Modal({
32470 cls : 'roo-document-manager-progress-dialog',
32471 allow_close : false,
32482 btnclick : function() {
32483 _this.uploadCancel();
32489 this.progressDialog.render(Roo.get(document.body));
32491 this.progress = new Roo.bootstrap.Progress({
32492 cls : 'roo-document-manager-progress',
32497 this.progress.render(this.progressDialog.getChildContainer());
32499 this.progressBar = new Roo.bootstrap.ProgressBar({
32500 cls : 'roo-document-manager-progress-bar',
32503 aria_valuemax : 12,
32507 this.progressBar.render(this.progress.getChildContainer());
32510 onUploaderClick : function(e)
32512 e.preventDefault();
32514 if(this.fireEvent('beforeselectfile', this) != false){
32515 this.selectorEl.dom.click();
32520 onFileSelected : function(e)
32522 e.preventDefault();
32524 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32528 Roo.each(this.selectorEl.dom.files, function(file){
32529 if(this.fireEvent('inspect', this, file) != false){
32530 this.files.push(file);
32540 this.selectorEl.dom.value = '';
32542 if(!this.files || !this.files.length){
32546 if(this.boxes > 0 && this.files.length > this.boxes){
32547 this.files = this.files.slice(0, this.boxes);
32550 this.uploader.show();
32552 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32553 this.uploader.hide();
32562 Roo.each(this.files, function(file){
32564 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32565 var f = this.renderPreview(file);
32570 if(file.type.indexOf('image') != -1){
32571 this.delegates.push(
32573 _this.process(file);
32574 }).createDelegate(this)
32582 _this.process(file);
32583 }).createDelegate(this)
32588 this.files = files;
32590 this.delegates = this.delegates.concat(docs);
32592 if(!this.delegates.length){
32597 this.progressBar.aria_valuemax = this.delegates.length;
32604 arrange : function()
32606 if(!this.delegates.length){
32607 this.progressDialog.hide();
32612 var delegate = this.delegates.shift();
32614 this.progressDialog.show();
32616 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32618 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32623 refresh : function()
32625 this.uploader.show();
32627 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32628 this.uploader.hide();
32631 Roo.isTouch ? this.closable(false) : this.closable(true);
32633 this.fireEvent('refresh', this);
32636 onRemove : function(e, el, o)
32638 e.preventDefault();
32640 this.fireEvent('remove', this, o);
32644 remove : function(o)
32648 Roo.each(this.files, function(file){
32649 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32658 this.files = files;
32665 Roo.each(this.files, function(file){
32670 file.target.remove();
32679 onClick : function(e, el, o)
32681 e.preventDefault();
32683 this.fireEvent('click', this, o);
32687 closable : function(closable)
32689 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32691 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32703 xhrOnLoad : function(xhr)
32705 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32709 if (xhr.readyState !== 4) {
32711 this.fireEvent('exception', this, xhr);
32715 var response = Roo.decode(xhr.responseText);
32717 if(!response.success){
32719 this.fireEvent('exception', this, xhr);
32723 var file = this.renderPreview(response.data);
32725 this.files.push(file);
32729 this.fireEvent('afterupload', this, xhr);
32733 xhrOnError : function(xhr)
32735 Roo.log('xhr on error');
32737 var response = Roo.decode(xhr.responseText);
32744 process : function(file)
32746 if(this.fireEvent('process', this, file) !== false){
32747 if(this.editable && file.type.indexOf('image') != -1){
32748 this.fireEvent('edit', this, file);
32752 this.uploadStart(file, false);
32759 uploadStart : function(file, crop)
32761 this.xhr = new XMLHttpRequest();
32763 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32768 file.xhr = this.xhr;
32770 this.managerEl.createChild({
32772 cls : 'roo-document-manager-loading',
32776 tooltip : file.name,
32777 cls : 'roo-document-manager-thumb',
32778 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32784 this.xhr.open(this.method, this.url, true);
32787 "Accept": "application/json",
32788 "Cache-Control": "no-cache",
32789 "X-Requested-With": "XMLHttpRequest"
32792 for (var headerName in headers) {
32793 var headerValue = headers[headerName];
32795 this.xhr.setRequestHeader(headerName, headerValue);
32801 this.xhr.onload = function()
32803 _this.xhrOnLoad(_this.xhr);
32806 this.xhr.onerror = function()
32808 _this.xhrOnError(_this.xhr);
32811 var formData = new FormData();
32813 formData.append('returnHTML', 'NO');
32816 formData.append('crop', crop);
32819 formData.append(this.paramName, file, file.name);
32826 if(this.fireEvent('prepare', this, formData, options) != false){
32828 if(options.manually){
32832 this.xhr.send(formData);
32836 this.uploadCancel();
32839 uploadCancel : function()
32845 this.delegates = [];
32847 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32854 renderPreview : function(file)
32856 if(typeof(file.target) != 'undefined' && file.target){
32860 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32862 var previewEl = this.managerEl.createChild({
32864 cls : 'roo-document-manager-preview',
32868 tooltip : file[this.toolTipName],
32869 cls : 'roo-document-manager-thumb',
32870 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32875 html : '<i class="fa fa-times-circle"></i>'
32880 var close = previewEl.select('button.close', true).first();
32882 close.on('click', this.onRemove, this, file);
32884 file.target = previewEl;
32886 var image = previewEl.select('img', true).first();
32890 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32892 image.on('click', this.onClick, this, file);
32894 this.fireEvent('previewrendered', this, file);
32900 onPreviewLoad : function(file, image)
32902 if(typeof(file.target) == 'undefined' || !file.target){
32906 var width = image.dom.naturalWidth || image.dom.width;
32907 var height = image.dom.naturalHeight || image.dom.height;
32909 if(!this.previewResize) {
32913 if(width > height){
32914 file.target.addClass('wide');
32918 file.target.addClass('tall');
32923 uploadFromSource : function(file, crop)
32925 this.xhr = new XMLHttpRequest();
32927 this.managerEl.createChild({
32929 cls : 'roo-document-manager-loading',
32933 tooltip : file.name,
32934 cls : 'roo-document-manager-thumb',
32935 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32941 this.xhr.open(this.method, this.url, true);
32944 "Accept": "application/json",
32945 "Cache-Control": "no-cache",
32946 "X-Requested-With": "XMLHttpRequest"
32949 for (var headerName in headers) {
32950 var headerValue = headers[headerName];
32952 this.xhr.setRequestHeader(headerName, headerValue);
32958 this.xhr.onload = function()
32960 _this.xhrOnLoad(_this.xhr);
32963 this.xhr.onerror = function()
32965 _this.xhrOnError(_this.xhr);
32968 var formData = new FormData();
32970 formData.append('returnHTML', 'NO');
32972 formData.append('crop', crop);
32974 if(typeof(file.filename) != 'undefined'){
32975 formData.append('filename', file.filename);
32978 if(typeof(file.mimetype) != 'undefined'){
32979 formData.append('mimetype', file.mimetype);
32984 if(this.fireEvent('prepare', this, formData) != false){
32985 this.xhr.send(formData);
32995 * @class Roo.bootstrap.DocumentViewer
32996 * @extends Roo.bootstrap.Component
32997 * Bootstrap DocumentViewer class
32998 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32999 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33002 * Create a new DocumentViewer
33003 * @param {Object} config The config object
33006 Roo.bootstrap.DocumentViewer = function(config){
33007 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33012 * Fire after initEvent
33013 * @param {Roo.bootstrap.DocumentViewer} this
33019 * @param {Roo.bootstrap.DocumentViewer} this
33024 * Fire after download button
33025 * @param {Roo.bootstrap.DocumentViewer} this
33030 * Fire after trash button
33031 * @param {Roo.bootstrap.DocumentViewer} this
33038 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33040 showDownload : true,
33044 getAutoCreate : function()
33048 cls : 'roo-document-viewer',
33052 cls : 'roo-document-viewer-body',
33056 cls : 'roo-document-viewer-thumb',
33060 cls : 'roo-document-viewer-image'
33068 cls : 'roo-document-viewer-footer',
33071 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33075 cls : 'btn-group roo-document-viewer-download',
33079 cls : 'btn btn-default',
33080 html : '<i class="fa fa-download"></i>'
33086 cls : 'btn-group roo-document-viewer-trash',
33090 cls : 'btn btn-default',
33091 html : '<i class="fa fa-trash"></i>'
33104 initEvents : function()
33106 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33107 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33109 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33110 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33112 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33113 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33115 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33116 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33118 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33119 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33121 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33122 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33124 this.bodyEl.on('click', this.onClick, this);
33125 this.downloadBtn.on('click', this.onDownload, this);
33126 this.trashBtn.on('click', this.onTrash, this);
33128 this.downloadBtn.hide();
33129 this.trashBtn.hide();
33131 if(this.showDownload){
33132 this.downloadBtn.show();
33135 if(this.showTrash){
33136 this.trashBtn.show();
33139 if(!this.showDownload && !this.showTrash) {
33140 this.footerEl.hide();
33145 initial : function()
33147 this.fireEvent('initial', this);
33151 onClick : function(e)
33153 e.preventDefault();
33155 this.fireEvent('click', this);
33158 onDownload : function(e)
33160 e.preventDefault();
33162 this.fireEvent('download', this);
33165 onTrash : function(e)
33167 e.preventDefault();
33169 this.fireEvent('trash', this);
33181 * @class Roo.bootstrap.NavProgressBar
33182 * @extends Roo.bootstrap.Component
33183 * Bootstrap NavProgressBar class
33186 * Create a new nav progress bar
33187 * @param {Object} config The config object
33190 Roo.bootstrap.NavProgressBar = function(config){
33191 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33193 this.bullets = this.bullets || [];
33195 // Roo.bootstrap.NavProgressBar.register(this);
33199 * Fires when the active item changes
33200 * @param {Roo.bootstrap.NavProgressBar} this
33201 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33202 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33209 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33214 getAutoCreate : function()
33216 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33220 cls : 'roo-navigation-bar-group',
33224 cls : 'roo-navigation-top-bar'
33228 cls : 'roo-navigation-bullets-bar',
33232 cls : 'roo-navigation-bar'
33239 cls : 'roo-navigation-bottom-bar'
33249 initEvents: function()
33254 onRender : function(ct, position)
33256 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33258 if(this.bullets.length){
33259 Roo.each(this.bullets, function(b){
33268 addItem : function(cfg)
33270 var item = new Roo.bootstrap.NavProgressItem(cfg);
33272 item.parentId = this.id;
33273 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33276 var top = new Roo.bootstrap.Element({
33278 cls : 'roo-navigation-bar-text'
33281 var bottom = new Roo.bootstrap.Element({
33283 cls : 'roo-navigation-bar-text'
33286 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33287 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33289 var topText = new Roo.bootstrap.Element({
33291 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33294 var bottomText = new Roo.bootstrap.Element({
33296 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33299 topText.onRender(top.el, null);
33300 bottomText.onRender(bottom.el, null);
33303 item.bottomEl = bottom;
33306 this.barItems.push(item);
33311 getActive : function()
33313 var active = false;
33315 Roo.each(this.barItems, function(v){
33317 if (!v.isActive()) {
33329 setActiveItem : function(item)
33333 Roo.each(this.barItems, function(v){
33334 if (v.rid == item.rid) {
33338 if (v.isActive()) {
33339 v.setActive(false);
33344 item.setActive(true);
33346 this.fireEvent('changed', this, item, prev);
33349 getBarItem: function(rid)
33353 Roo.each(this.barItems, function(e) {
33354 if (e.rid != rid) {
33365 indexOfItem : function(item)
33369 Roo.each(this.barItems, function(v, i){
33371 if (v.rid != item.rid) {
33382 setActiveNext : function()
33384 var i = this.indexOfItem(this.getActive());
33386 if (i > this.barItems.length) {
33390 this.setActiveItem(this.barItems[i+1]);
33393 setActivePrev : function()
33395 var i = this.indexOfItem(this.getActive());
33401 this.setActiveItem(this.barItems[i-1]);
33404 format : function()
33406 if(!this.barItems.length){
33410 var width = 100 / this.barItems.length;
33412 Roo.each(this.barItems, function(i){
33413 i.el.setStyle('width', width + '%');
33414 i.topEl.el.setStyle('width', width + '%');
33415 i.bottomEl.el.setStyle('width', width + '%');
33424 * Nav Progress Item
33429 * @class Roo.bootstrap.NavProgressItem
33430 * @extends Roo.bootstrap.Component
33431 * Bootstrap NavProgressItem class
33432 * @cfg {String} rid the reference id
33433 * @cfg {Boolean} active (true|false) Is item active default false
33434 * @cfg {Boolean} disabled (true|false) Is item active default false
33435 * @cfg {String} html
33436 * @cfg {String} position (top|bottom) text position default bottom
33437 * @cfg {String} icon show icon instead of number
33440 * Create a new NavProgressItem
33441 * @param {Object} config The config object
33443 Roo.bootstrap.NavProgressItem = function(config){
33444 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33449 * The raw click event for the entire grid.
33450 * @param {Roo.bootstrap.NavProgressItem} this
33451 * @param {Roo.EventObject} e
33458 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33464 position : 'bottom',
33467 getAutoCreate : function()
33469 var iconCls = 'roo-navigation-bar-item-icon';
33471 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33475 cls: 'roo-navigation-bar-item',
33485 cfg.cls += ' active';
33488 cfg.cls += ' disabled';
33494 disable : function()
33496 this.setDisabled(true);
33499 enable : function()
33501 this.setDisabled(false);
33504 initEvents: function()
33506 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33508 this.iconEl.on('click', this.onClick, this);
33511 onClick : function(e)
33513 e.preventDefault();
33519 if(this.fireEvent('click', this, e) === false){
33523 this.parent().setActiveItem(this);
33526 isActive: function ()
33528 return this.active;
33531 setActive : function(state)
33533 if(this.active == state){
33537 this.active = state;
33540 this.el.addClass('active');
33544 this.el.removeClass('active');
33549 setDisabled : function(state)
33551 if(this.disabled == state){
33555 this.disabled = state;
33558 this.el.addClass('disabled');
33562 this.el.removeClass('disabled');
33565 tooltipEl : function()
33567 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33580 * @class Roo.bootstrap.FieldLabel
33581 * @extends Roo.bootstrap.Component
33582 * Bootstrap FieldLabel class
33583 * @cfg {String} html contents of the element
33584 * @cfg {String} tag tag of the element default label
33585 * @cfg {String} cls class of the element
33586 * @cfg {String} target label target
33587 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33588 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33589 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33590 * @cfg {String} iconTooltip default "This field is required"
33591 * @cfg {String} indicatorpos (left|right) default left
33594 * Create a new FieldLabel
33595 * @param {Object} config The config object
33598 Roo.bootstrap.FieldLabel = function(config){
33599 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33604 * Fires after the field has been marked as invalid.
33605 * @param {Roo.form.FieldLabel} this
33606 * @param {String} msg The validation message
33611 * Fires after the field has been validated with no errors.
33612 * @param {Roo.form.FieldLabel} this
33618 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33625 invalidClass : 'has-warning',
33626 validClass : 'has-success',
33627 iconTooltip : 'This field is required',
33628 indicatorpos : 'left',
33630 getAutoCreate : function(){
33633 if (!this.allowBlank) {
33639 cls : 'roo-bootstrap-field-label ' + this.cls,
33644 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33645 tooltip : this.iconTooltip
33654 if(this.indicatorpos == 'right'){
33657 cls : 'roo-bootstrap-field-label ' + this.cls,
33666 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33667 tooltip : this.iconTooltip
33676 initEvents: function()
33678 Roo.bootstrap.Element.superclass.initEvents.call(this);
33680 this.indicator = this.indicatorEl();
33682 if(this.indicator){
33683 this.indicator.removeClass('visible');
33684 this.indicator.addClass('invisible');
33687 Roo.bootstrap.FieldLabel.register(this);
33690 indicatorEl : function()
33692 var indicator = this.el.select('i.roo-required-indicator',true).first();
33703 * Mark this field as valid
33705 markValid : function()
33707 if(this.indicator){
33708 this.indicator.removeClass('visible');
33709 this.indicator.addClass('invisible');
33711 if (Roo.bootstrap.version == 3) {
33712 this.el.removeClass(this.invalidClass);
33713 this.el.addClass(this.validClass);
33715 this.el.removeClass('is-invalid');
33716 this.el.addClass('is-valid');
33720 this.fireEvent('valid', this);
33724 * Mark this field as invalid
33725 * @param {String} msg The validation message
33727 markInvalid : function(msg)
33729 if(this.indicator){
33730 this.indicator.removeClass('invisible');
33731 this.indicator.addClass('visible');
33733 if (Roo.bootstrap.version == 3) {
33734 this.el.removeClass(this.validClass);
33735 this.el.addClass(this.invalidClass);
33737 this.el.removeClass('is-valid');
33738 this.el.addClass('is-invalid');
33742 this.fireEvent('invalid', this, msg);
33748 Roo.apply(Roo.bootstrap.FieldLabel, {
33753 * register a FieldLabel Group
33754 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33756 register : function(label)
33758 if(this.groups.hasOwnProperty(label.target)){
33762 this.groups[label.target] = label;
33766 * fetch a FieldLabel Group based on the target
33767 * @param {string} target
33768 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33770 get: function(target) {
33771 if (typeof(this.groups[target]) == 'undefined') {
33775 return this.groups[target] ;
33784 * page DateSplitField.
33790 * @class Roo.bootstrap.DateSplitField
33791 * @extends Roo.bootstrap.Component
33792 * Bootstrap DateSplitField class
33793 * @cfg {string} fieldLabel - the label associated
33794 * @cfg {Number} labelWidth set the width of label (0-12)
33795 * @cfg {String} labelAlign (top|left)
33796 * @cfg {Boolean} dayAllowBlank (true|false) default false
33797 * @cfg {Boolean} monthAllowBlank (true|false) default false
33798 * @cfg {Boolean} yearAllowBlank (true|false) default false
33799 * @cfg {string} dayPlaceholder
33800 * @cfg {string} monthPlaceholder
33801 * @cfg {string} yearPlaceholder
33802 * @cfg {string} dayFormat default 'd'
33803 * @cfg {string} monthFormat default 'm'
33804 * @cfg {string} yearFormat default 'Y'
33805 * @cfg {Number} labellg set the width of label (1-12)
33806 * @cfg {Number} labelmd set the width of label (1-12)
33807 * @cfg {Number} labelsm set the width of label (1-12)
33808 * @cfg {Number} labelxs set the width of label (1-12)
33812 * Create a new DateSplitField
33813 * @param {Object} config The config object
33816 Roo.bootstrap.DateSplitField = function(config){
33817 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33823 * getting the data of years
33824 * @param {Roo.bootstrap.DateSplitField} this
33825 * @param {Object} years
33830 * getting the data of days
33831 * @param {Roo.bootstrap.DateSplitField} this
33832 * @param {Object} days
33837 * Fires after the field has been marked as invalid.
33838 * @param {Roo.form.Field} this
33839 * @param {String} msg The validation message
33844 * Fires after the field has been validated with no errors.
33845 * @param {Roo.form.Field} this
33851 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33854 labelAlign : 'top',
33856 dayAllowBlank : false,
33857 monthAllowBlank : false,
33858 yearAllowBlank : false,
33859 dayPlaceholder : '',
33860 monthPlaceholder : '',
33861 yearPlaceholder : '',
33865 isFormField : true,
33871 getAutoCreate : function()
33875 cls : 'row roo-date-split-field-group',
33880 cls : 'form-hidden-field roo-date-split-field-group-value',
33886 var labelCls = 'col-md-12';
33887 var contentCls = 'col-md-4';
33889 if(this.fieldLabel){
33893 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33897 html : this.fieldLabel
33902 if(this.labelAlign == 'left'){
33904 if(this.labelWidth > 12){
33905 label.style = "width: " + this.labelWidth + 'px';
33908 if(this.labelWidth < 13 && this.labelmd == 0){
33909 this.labelmd = this.labelWidth;
33912 if(this.labellg > 0){
33913 labelCls = ' col-lg-' + this.labellg;
33914 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33917 if(this.labelmd > 0){
33918 labelCls = ' col-md-' + this.labelmd;
33919 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33922 if(this.labelsm > 0){
33923 labelCls = ' col-sm-' + this.labelsm;
33924 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33927 if(this.labelxs > 0){
33928 labelCls = ' col-xs-' + this.labelxs;
33929 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33933 label.cls += ' ' + labelCls;
33935 cfg.cn.push(label);
33938 Roo.each(['day', 'month', 'year'], function(t){
33941 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33948 inputEl: function ()
33950 return this.el.select('.roo-date-split-field-group-value', true).first();
33953 onRender : function(ct, position)
33957 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33959 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33961 this.dayField = new Roo.bootstrap.ComboBox({
33962 allowBlank : this.dayAllowBlank,
33963 alwaysQuery : true,
33964 displayField : 'value',
33967 forceSelection : true,
33969 placeholder : this.dayPlaceholder,
33970 selectOnFocus : true,
33971 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33972 triggerAction : 'all',
33974 valueField : 'value',
33975 store : new Roo.data.SimpleStore({
33976 data : (function() {
33978 _this.fireEvent('days', _this, days);
33981 fields : [ 'value' ]
33984 select : function (_self, record, index)
33986 _this.setValue(_this.getValue());
33991 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33993 this.monthField = new Roo.bootstrap.MonthField({
33994 after : '<i class=\"fa fa-calendar\"></i>',
33995 allowBlank : this.monthAllowBlank,
33996 placeholder : this.monthPlaceholder,
33999 render : function (_self)
34001 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34002 e.preventDefault();
34006 select : function (_self, oldvalue, newvalue)
34008 _this.setValue(_this.getValue());
34013 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34015 this.yearField = new Roo.bootstrap.ComboBox({
34016 allowBlank : this.yearAllowBlank,
34017 alwaysQuery : true,
34018 displayField : 'value',
34021 forceSelection : true,
34023 placeholder : this.yearPlaceholder,
34024 selectOnFocus : true,
34025 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34026 triggerAction : 'all',
34028 valueField : 'value',
34029 store : new Roo.data.SimpleStore({
34030 data : (function() {
34032 _this.fireEvent('years', _this, years);
34035 fields : [ 'value' ]
34038 select : function (_self, record, index)
34040 _this.setValue(_this.getValue());
34045 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34048 setValue : function(v, format)
34050 this.inputEl.dom.value = v;
34052 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34054 var d = Date.parseDate(v, f);
34061 this.setDay(d.format(this.dayFormat));
34062 this.setMonth(d.format(this.monthFormat));
34063 this.setYear(d.format(this.yearFormat));
34070 setDay : function(v)
34072 this.dayField.setValue(v);
34073 this.inputEl.dom.value = this.getValue();
34078 setMonth : function(v)
34080 this.monthField.setValue(v, true);
34081 this.inputEl.dom.value = this.getValue();
34086 setYear : function(v)
34088 this.yearField.setValue(v);
34089 this.inputEl.dom.value = this.getValue();
34094 getDay : function()
34096 return this.dayField.getValue();
34099 getMonth : function()
34101 return this.monthField.getValue();
34104 getYear : function()
34106 return this.yearField.getValue();
34109 getValue : function()
34111 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34113 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34123 this.inputEl.dom.value = '';
34128 validate : function()
34130 var d = this.dayField.validate();
34131 var m = this.monthField.validate();
34132 var y = this.yearField.validate();
34137 (!this.dayAllowBlank && !d) ||
34138 (!this.monthAllowBlank && !m) ||
34139 (!this.yearAllowBlank && !y)
34144 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34153 this.markInvalid();
34158 markValid : function()
34161 var label = this.el.select('label', true).first();
34162 var icon = this.el.select('i.fa-star', true).first();
34168 this.fireEvent('valid', this);
34172 * Mark this field as invalid
34173 * @param {String} msg The validation message
34175 markInvalid : function(msg)
34178 var label = this.el.select('label', true).first();
34179 var icon = this.el.select('i.fa-star', true).first();
34181 if(label && !icon){
34182 this.el.select('.roo-date-split-field-label', true).createChild({
34184 cls : 'text-danger fa fa-lg fa-star',
34185 tooltip : 'This field is required',
34186 style : 'margin-right:5px;'
34190 this.fireEvent('invalid', this, msg);
34193 clearInvalid : function()
34195 var label = this.el.select('label', true).first();
34196 var icon = this.el.select('i.fa-star', true).first();
34202 this.fireEvent('valid', this);
34205 getName: function()
34215 * http://masonry.desandro.com
34217 * The idea is to render all the bricks based on vertical width...
34219 * The original code extends 'outlayer' - we might need to use that....
34225 * @class Roo.bootstrap.LayoutMasonry
34226 * @extends Roo.bootstrap.Component
34227 * Bootstrap Layout Masonry class
34230 * Create a new Element
34231 * @param {Object} config The config object
34234 Roo.bootstrap.LayoutMasonry = function(config){
34236 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34240 Roo.bootstrap.LayoutMasonry.register(this);
34246 * Fire after layout the items
34247 * @param {Roo.bootstrap.LayoutMasonry} this
34248 * @param {Roo.EventObject} e
34255 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34258 * @cfg {Boolean} isLayoutInstant = no animation?
34260 isLayoutInstant : false, // needed?
34263 * @cfg {Number} boxWidth width of the columns
34268 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34273 * @cfg {Number} padWidth padding below box..
34278 * @cfg {Number} gutter gutter width..
34283 * @cfg {Number} maxCols maximum number of columns
34289 * @cfg {Boolean} isAutoInitial defalut true
34291 isAutoInitial : true,
34296 * @cfg {Boolean} isHorizontal defalut false
34298 isHorizontal : false,
34300 currentSize : null,
34306 bricks: null, //CompositeElement
34310 _isLayoutInited : false,
34312 // isAlternative : false, // only use for vertical layout...
34315 * @cfg {Number} alternativePadWidth padding below box..
34317 alternativePadWidth : 50,
34319 selectedBrick : [],
34321 getAutoCreate : function(){
34323 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34327 cls: 'blog-masonary-wrapper ' + this.cls,
34329 cls : 'mas-boxes masonary'
34336 getChildContainer: function( )
34338 if (this.boxesEl) {
34339 return this.boxesEl;
34342 this.boxesEl = this.el.select('.mas-boxes').first();
34344 return this.boxesEl;
34348 initEvents : function()
34352 if(this.isAutoInitial){
34353 Roo.log('hook children rendered');
34354 this.on('childrenrendered', function() {
34355 Roo.log('children rendered');
34361 initial : function()
34363 this.selectedBrick = [];
34365 this.currentSize = this.el.getBox(true);
34367 Roo.EventManager.onWindowResize(this.resize, this);
34369 if(!this.isAutoInitial){
34377 //this.layout.defer(500,this);
34381 resize : function()
34383 var cs = this.el.getBox(true);
34386 this.currentSize.width == cs.width &&
34387 this.currentSize.x == cs.x &&
34388 this.currentSize.height == cs.height &&
34389 this.currentSize.y == cs.y
34391 Roo.log("no change in with or X or Y");
34395 this.currentSize = cs;
34401 layout : function()
34403 this._resetLayout();
34405 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34407 this.layoutItems( isInstant );
34409 this._isLayoutInited = true;
34411 this.fireEvent('layout', this);
34415 _resetLayout : function()
34417 if(this.isHorizontal){
34418 this.horizontalMeasureColumns();
34422 this.verticalMeasureColumns();
34426 verticalMeasureColumns : function()
34428 this.getContainerWidth();
34430 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34431 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34435 var boxWidth = this.boxWidth + this.padWidth;
34437 if(this.containerWidth < this.boxWidth){
34438 boxWidth = this.containerWidth
34441 var containerWidth = this.containerWidth;
34443 var cols = Math.floor(containerWidth / boxWidth);
34445 this.cols = Math.max( cols, 1 );
34447 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34449 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34451 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34453 this.colWidth = boxWidth + avail - this.padWidth;
34455 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34456 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34459 horizontalMeasureColumns : function()
34461 this.getContainerWidth();
34463 var boxWidth = this.boxWidth;
34465 if(this.containerWidth < boxWidth){
34466 boxWidth = this.containerWidth;
34469 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34471 this.el.setHeight(boxWidth);
34475 getContainerWidth : function()
34477 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34480 layoutItems : function( isInstant )
34482 Roo.log(this.bricks);
34484 var items = Roo.apply([], this.bricks);
34486 if(this.isHorizontal){
34487 this._horizontalLayoutItems( items , isInstant );
34491 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34492 // this._verticalAlternativeLayoutItems( items , isInstant );
34496 this._verticalLayoutItems( items , isInstant );
34500 _verticalLayoutItems : function ( items , isInstant)
34502 if ( !items || !items.length ) {
34507 ['xs', 'xs', 'xs', 'tall'],
34508 ['xs', 'xs', 'tall'],
34509 ['xs', 'xs', 'sm'],
34510 ['xs', 'xs', 'xs'],
34516 ['sm', 'xs', 'xs'],
34520 ['tall', 'xs', 'xs', 'xs'],
34521 ['tall', 'xs', 'xs'],
34533 Roo.each(items, function(item, k){
34535 switch (item.size) {
34536 // these layouts take up a full box,
34547 boxes.push([item]);
34570 var filterPattern = function(box, length)
34578 var pattern = box.slice(0, length);
34582 Roo.each(pattern, function(i){
34583 format.push(i.size);
34586 Roo.each(standard, function(s){
34588 if(String(s) != String(format)){
34597 if(!match && length == 1){
34602 filterPattern(box, length - 1);
34606 queue.push(pattern);
34608 box = box.slice(length, box.length);
34610 filterPattern(box, 4);
34616 Roo.each(boxes, function(box, k){
34622 if(box.length == 1){
34627 filterPattern(box, 4);
34631 this._processVerticalLayoutQueue( queue, isInstant );
34635 // _verticalAlternativeLayoutItems : function( items , isInstant )
34637 // if ( !items || !items.length ) {
34641 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34645 _horizontalLayoutItems : function ( items , isInstant)
34647 if ( !items || !items.length || items.length < 3) {
34653 var eItems = items.slice(0, 3);
34655 items = items.slice(3, items.length);
34658 ['xs', 'xs', 'xs', 'wide'],
34659 ['xs', 'xs', 'wide'],
34660 ['xs', 'xs', 'sm'],
34661 ['xs', 'xs', 'xs'],
34667 ['sm', 'xs', 'xs'],
34671 ['wide', 'xs', 'xs', 'xs'],
34672 ['wide', 'xs', 'xs'],
34685 Roo.each(items, function(item, k){
34687 switch (item.size) {
34698 boxes.push([item]);
34722 var filterPattern = function(box, length)
34730 var pattern = box.slice(0, length);
34734 Roo.each(pattern, function(i){
34735 format.push(i.size);
34738 Roo.each(standard, function(s){
34740 if(String(s) != String(format)){
34749 if(!match && length == 1){
34754 filterPattern(box, length - 1);
34758 queue.push(pattern);
34760 box = box.slice(length, box.length);
34762 filterPattern(box, 4);
34768 Roo.each(boxes, function(box, k){
34774 if(box.length == 1){
34779 filterPattern(box, 4);
34786 var pos = this.el.getBox(true);
34790 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34792 var hit_end = false;
34794 Roo.each(queue, function(box){
34798 Roo.each(box, function(b){
34800 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34810 Roo.each(box, function(b){
34812 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34815 mx = Math.max(mx, b.x);
34819 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34823 Roo.each(box, function(b){
34825 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34839 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34842 /** Sets position of item in DOM
34843 * @param {Element} item
34844 * @param {Number} x - horizontal position
34845 * @param {Number} y - vertical position
34846 * @param {Boolean} isInstant - disables transitions
34848 _processVerticalLayoutQueue : function( queue, isInstant )
34850 var pos = this.el.getBox(true);
34855 for (var i = 0; i < this.cols; i++){
34859 Roo.each(queue, function(box, k){
34861 var col = k % this.cols;
34863 Roo.each(box, function(b,kk){
34865 b.el.position('absolute');
34867 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34868 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34870 if(b.size == 'md-left' || b.size == 'md-right'){
34871 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34872 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34875 b.el.setWidth(width);
34876 b.el.setHeight(height);
34878 b.el.select('iframe',true).setSize(width,height);
34882 for (var i = 0; i < this.cols; i++){
34884 if(maxY[i] < maxY[col]){
34889 col = Math.min(col, i);
34893 x = pos.x + col * (this.colWidth + this.padWidth);
34897 var positions = [];
34899 switch (box.length){
34901 positions = this.getVerticalOneBoxColPositions(x, y, box);
34904 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34907 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34910 positions = this.getVerticalFourBoxColPositions(x, y, box);
34916 Roo.each(box, function(b,kk){
34918 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34920 var sz = b.el.getSize();
34922 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34930 for (var i = 0; i < this.cols; i++){
34931 mY = Math.max(mY, maxY[i]);
34934 this.el.setHeight(mY - pos.y);
34938 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34940 // var pos = this.el.getBox(true);
34943 // var maxX = pos.right;
34945 // var maxHeight = 0;
34947 // Roo.each(items, function(item, k){
34951 // item.el.position('absolute');
34953 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34955 // item.el.setWidth(width);
34957 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34959 // item.el.setHeight(height);
34962 // item.el.setXY([x, y], isInstant ? false : true);
34964 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34967 // y = y + height + this.alternativePadWidth;
34969 // maxHeight = maxHeight + height + this.alternativePadWidth;
34973 // this.el.setHeight(maxHeight);
34977 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34979 var pos = this.el.getBox(true);
34984 var maxX = pos.right;
34986 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34988 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34990 Roo.each(queue, function(box, k){
34992 Roo.each(box, function(b, kk){
34994 b.el.position('absolute');
34996 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34997 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34999 if(b.size == 'md-left' || b.size == 'md-right'){
35000 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35001 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35004 b.el.setWidth(width);
35005 b.el.setHeight(height);
35013 var positions = [];
35015 switch (box.length){
35017 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35020 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35023 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35026 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35032 Roo.each(box, function(b,kk){
35034 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35036 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35044 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35046 Roo.each(eItems, function(b,k){
35048 b.size = (k == 0) ? 'sm' : 'xs';
35049 b.x = (k == 0) ? 2 : 1;
35050 b.y = (k == 0) ? 2 : 1;
35052 b.el.position('absolute');
35054 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35056 b.el.setWidth(width);
35058 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35060 b.el.setHeight(height);
35064 var positions = [];
35067 x : maxX - this.unitWidth * 2 - this.gutter,
35072 x : maxX - this.unitWidth,
35073 y : minY + (this.unitWidth + this.gutter) * 2
35077 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35081 Roo.each(eItems, function(b,k){
35083 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35089 getVerticalOneBoxColPositions : function(x, y, box)
35093 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35095 if(box[0].size == 'md-left'){
35099 if(box[0].size == 'md-right'){
35104 x : x + (this.unitWidth + this.gutter) * rand,
35111 getVerticalTwoBoxColPositions : function(x, y, box)
35115 if(box[0].size == 'xs'){
35119 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35123 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35137 x : x + (this.unitWidth + this.gutter) * 2,
35138 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35145 getVerticalThreeBoxColPositions : function(x, y, box)
35149 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35157 x : x + (this.unitWidth + this.gutter) * 1,
35162 x : x + (this.unitWidth + this.gutter) * 2,
35170 if(box[0].size == 'xs' && box[1].size == 'xs'){
35179 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35183 x : x + (this.unitWidth + this.gutter) * 1,
35197 x : x + (this.unitWidth + this.gutter) * 2,
35202 x : x + (this.unitWidth + this.gutter) * 2,
35203 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35210 getVerticalFourBoxColPositions : function(x, y, box)
35214 if(box[0].size == 'xs'){
35223 y : y + (this.unitHeight + this.gutter) * 1
35228 y : y + (this.unitHeight + this.gutter) * 2
35232 x : x + (this.unitWidth + this.gutter) * 1,
35246 x : x + (this.unitWidth + this.gutter) * 2,
35251 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35252 y : y + (this.unitHeight + this.gutter) * 1
35256 x : x + (this.unitWidth + this.gutter) * 2,
35257 y : y + (this.unitWidth + this.gutter) * 2
35264 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35268 if(box[0].size == 'md-left'){
35270 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35277 if(box[0].size == 'md-right'){
35279 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35280 y : minY + (this.unitWidth + this.gutter) * 1
35286 var rand = Math.floor(Math.random() * (4 - box[0].y));
35289 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35290 y : minY + (this.unitWidth + this.gutter) * rand
35297 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35301 if(box[0].size == 'xs'){
35304 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35309 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35310 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35318 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35323 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35324 y : minY + (this.unitWidth + this.gutter) * 2
35331 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35335 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35338 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35343 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35344 y : minY + (this.unitWidth + this.gutter) * 1
35348 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35349 y : minY + (this.unitWidth + this.gutter) * 2
35356 if(box[0].size == 'xs' && box[1].size == 'xs'){
35359 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35364 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35369 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35370 y : minY + (this.unitWidth + this.gutter) * 1
35378 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35383 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35384 y : minY + (this.unitWidth + this.gutter) * 2
35388 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35389 y : minY + (this.unitWidth + this.gutter) * 2
35396 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35400 if(box[0].size == 'xs'){
35403 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35408 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35413 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),
35418 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35419 y : minY + (this.unitWidth + this.gutter) * 1
35427 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35432 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35433 y : minY + (this.unitWidth + this.gutter) * 2
35437 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35438 y : minY + (this.unitWidth + this.gutter) * 2
35442 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),
35443 y : minY + (this.unitWidth + this.gutter) * 2
35451 * remove a Masonry Brick
35452 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35454 removeBrick : function(brick_id)
35460 for (var i = 0; i<this.bricks.length; i++) {
35461 if (this.bricks[i].id == brick_id) {
35462 this.bricks.splice(i,1);
35463 this.el.dom.removeChild(Roo.get(brick_id).dom);
35470 * adds a Masonry Brick
35471 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35473 addBrick : function(cfg)
35475 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35476 //this.register(cn);
35477 cn.parentId = this.id;
35478 cn.render(this.el);
35483 * register a Masonry Brick
35484 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35487 register : function(brick)
35489 this.bricks.push(brick);
35490 brick.masonryId = this.id;
35494 * clear all the Masonry Brick
35496 clearAll : function()
35499 //this.getChildContainer().dom.innerHTML = "";
35500 this.el.dom.innerHTML = '';
35503 getSelected : function()
35505 if (!this.selectedBrick) {
35509 return this.selectedBrick;
35513 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35517 * register a Masonry Layout
35518 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35521 register : function(layout)
35523 this.groups[layout.id] = layout;
35526 * fetch a Masonry Layout based on the masonry layout ID
35527 * @param {string} the masonry layout to add
35528 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35531 get: function(layout_id) {
35532 if (typeof(this.groups[layout_id]) == 'undefined') {
35535 return this.groups[layout_id] ;
35547 * http://masonry.desandro.com
35549 * The idea is to render all the bricks based on vertical width...
35551 * The original code extends 'outlayer' - we might need to use that....
35557 * @class Roo.bootstrap.LayoutMasonryAuto
35558 * @extends Roo.bootstrap.Component
35559 * Bootstrap Layout Masonry class
35562 * Create a new Element
35563 * @param {Object} config The config object
35566 Roo.bootstrap.LayoutMasonryAuto = function(config){
35567 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35570 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35573 * @cfg {Boolean} isFitWidth - resize the width..
35575 isFitWidth : false, // options..
35577 * @cfg {Boolean} isOriginLeft = left align?
35579 isOriginLeft : true,
35581 * @cfg {Boolean} isOriginTop = top align?
35583 isOriginTop : false,
35585 * @cfg {Boolean} isLayoutInstant = no animation?
35587 isLayoutInstant : false, // needed?
35589 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35591 isResizingContainer : true,
35593 * @cfg {Number} columnWidth width of the columns
35599 * @cfg {Number} maxCols maximum number of columns
35604 * @cfg {Number} padHeight padding below box..
35610 * @cfg {Boolean} isAutoInitial defalut true
35613 isAutoInitial : true,
35619 initialColumnWidth : 0,
35620 currentSize : null,
35622 colYs : null, // array.
35629 bricks: null, //CompositeElement
35630 cols : 0, // array?
35631 // element : null, // wrapped now this.el
35632 _isLayoutInited : null,
35635 getAutoCreate : function(){
35639 cls: 'blog-masonary-wrapper ' + this.cls,
35641 cls : 'mas-boxes masonary'
35648 getChildContainer: function( )
35650 if (this.boxesEl) {
35651 return this.boxesEl;
35654 this.boxesEl = this.el.select('.mas-boxes').first();
35656 return this.boxesEl;
35660 initEvents : function()
35664 if(this.isAutoInitial){
35665 Roo.log('hook children rendered');
35666 this.on('childrenrendered', function() {
35667 Roo.log('children rendered');
35674 initial : function()
35676 this.reloadItems();
35678 this.currentSize = this.el.getBox(true);
35680 /// was window resize... - let's see if this works..
35681 Roo.EventManager.onWindowResize(this.resize, this);
35683 if(!this.isAutoInitial){
35688 this.layout.defer(500,this);
35691 reloadItems: function()
35693 this.bricks = this.el.select('.masonry-brick', true);
35695 this.bricks.each(function(b) {
35696 //Roo.log(b.getSize());
35697 if (!b.attr('originalwidth')) {
35698 b.attr('originalwidth', b.getSize().width);
35703 Roo.log(this.bricks.elements.length);
35706 resize : function()
35709 var cs = this.el.getBox(true);
35711 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35712 Roo.log("no change in with or X");
35715 this.currentSize = cs;
35719 layout : function()
35722 this._resetLayout();
35723 //this._manageStamps();
35725 // don't animate first layout
35726 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35727 this.layoutItems( isInstant );
35729 // flag for initalized
35730 this._isLayoutInited = true;
35733 layoutItems : function( isInstant )
35735 //var items = this._getItemsForLayout( this.items );
35736 // original code supports filtering layout items.. we just ignore it..
35738 this._layoutItems( this.bricks , isInstant );
35740 this._postLayout();
35742 _layoutItems : function ( items , isInstant)
35744 //this.fireEvent( 'layout', this, items );
35747 if ( !items || !items.elements.length ) {
35748 // no items, emit event with empty array
35753 items.each(function(item) {
35754 Roo.log("layout item");
35756 // get x/y object from method
35757 var position = this._getItemLayoutPosition( item );
35759 position.item = item;
35760 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35761 queue.push( position );
35764 this._processLayoutQueue( queue );
35766 /** Sets position of item in DOM
35767 * @param {Element} item
35768 * @param {Number} x - horizontal position
35769 * @param {Number} y - vertical position
35770 * @param {Boolean} isInstant - disables transitions
35772 _processLayoutQueue : function( queue )
35774 for ( var i=0, len = queue.length; i < len; i++ ) {
35775 var obj = queue[i];
35776 obj.item.position('absolute');
35777 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35783 * Any logic you want to do after each layout,
35784 * i.e. size the container
35786 _postLayout : function()
35788 this.resizeContainer();
35791 resizeContainer : function()
35793 if ( !this.isResizingContainer ) {
35796 var size = this._getContainerSize();
35798 this.el.setSize(size.width,size.height);
35799 this.boxesEl.setSize(size.width,size.height);
35805 _resetLayout : function()
35807 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35808 this.colWidth = this.el.getWidth();
35809 //this.gutter = this.el.getWidth();
35811 this.measureColumns();
35817 this.colYs.push( 0 );
35823 measureColumns : function()
35825 this.getContainerWidth();
35826 // if columnWidth is 0, default to outerWidth of first item
35827 if ( !this.columnWidth ) {
35828 var firstItem = this.bricks.first();
35829 Roo.log(firstItem);
35830 this.columnWidth = this.containerWidth;
35831 if (firstItem && firstItem.attr('originalwidth') ) {
35832 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35834 // columnWidth fall back to item of first element
35835 Roo.log("set column width?");
35836 this.initialColumnWidth = this.columnWidth ;
35838 // if first elem has no width, default to size of container
35843 if (this.initialColumnWidth) {
35844 this.columnWidth = this.initialColumnWidth;
35849 // column width is fixed at the top - however if container width get's smaller we should
35852 // this bit calcs how man columns..
35854 var columnWidth = this.columnWidth += this.gutter;
35856 // calculate columns
35857 var containerWidth = this.containerWidth + this.gutter;
35859 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35860 // fix rounding errors, typically with gutters
35861 var excess = columnWidth - containerWidth % columnWidth;
35864 // if overshoot is less than a pixel, round up, otherwise floor it
35865 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35866 cols = Math[ mathMethod ]( cols );
35867 this.cols = Math.max( cols, 1 );
35868 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35870 // padding positioning..
35871 var totalColWidth = this.cols * this.columnWidth;
35872 var padavail = this.containerWidth - totalColWidth;
35873 // so for 2 columns - we need 3 'pads'
35875 var padNeeded = (1+this.cols) * this.padWidth;
35877 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35879 this.columnWidth += padExtra
35880 //this.padWidth = Math.floor(padavail / ( this.cols));
35882 // adjust colum width so that padding is fixed??
35884 // we have 3 columns ... total = width * 3
35885 // we have X left over... that should be used by
35887 //if (this.expandC) {
35895 getContainerWidth : function()
35897 /* // container is parent if fit width
35898 var container = this.isFitWidth ? this.element.parentNode : this.element;
35899 // check that this.size and size are there
35900 // IE8 triggers resize on body size change, so they might not be
35902 var size = getSize( container ); //FIXME
35903 this.containerWidth = size && size.innerWidth; //FIXME
35906 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35910 _getItemLayoutPosition : function( item ) // what is item?
35912 // we resize the item to our columnWidth..
35914 item.setWidth(this.columnWidth);
35915 item.autoBoxAdjust = false;
35917 var sz = item.getSize();
35919 // how many columns does this brick span
35920 var remainder = this.containerWidth % this.columnWidth;
35922 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35923 // round if off by 1 pixel, otherwise use ceil
35924 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35925 colSpan = Math.min( colSpan, this.cols );
35927 // normally this should be '1' as we dont' currently allow multi width columns..
35929 var colGroup = this._getColGroup( colSpan );
35930 // get the minimum Y value from the columns
35931 var minimumY = Math.min.apply( Math, colGroup );
35932 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35934 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35936 // position the brick
35938 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35939 y: this.currentSize.y + minimumY + this.padHeight
35943 // apply setHeight to necessary columns
35944 var setHeight = minimumY + sz.height + this.padHeight;
35945 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35947 var setSpan = this.cols + 1 - colGroup.length;
35948 for ( var i = 0; i < setSpan; i++ ) {
35949 this.colYs[ shortColIndex + i ] = setHeight ;
35956 * @param {Number} colSpan - number of columns the element spans
35957 * @returns {Array} colGroup
35959 _getColGroup : function( colSpan )
35961 if ( colSpan < 2 ) {
35962 // if brick spans only one column, use all the column Ys
35967 // how many different places could this brick fit horizontally
35968 var groupCount = this.cols + 1 - colSpan;
35969 // for each group potential horizontal position
35970 for ( var i = 0; i < groupCount; i++ ) {
35971 // make an array of colY values for that one group
35972 var groupColYs = this.colYs.slice( i, i + colSpan );
35973 // and get the max value of the array
35974 colGroup[i] = Math.max.apply( Math, groupColYs );
35979 _manageStamp : function( stamp )
35981 var stampSize = stamp.getSize();
35982 var offset = stamp.getBox();
35983 // get the columns that this stamp affects
35984 var firstX = this.isOriginLeft ? offset.x : offset.right;
35985 var lastX = firstX + stampSize.width;
35986 var firstCol = Math.floor( firstX / this.columnWidth );
35987 firstCol = Math.max( 0, firstCol );
35989 var lastCol = Math.floor( lastX / this.columnWidth );
35990 // lastCol should not go over if multiple of columnWidth #425
35991 lastCol -= lastX % this.columnWidth ? 0 : 1;
35992 lastCol = Math.min( this.cols - 1, lastCol );
35994 // set colYs to bottom of the stamp
35995 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35998 for ( var i = firstCol; i <= lastCol; i++ ) {
35999 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36004 _getContainerSize : function()
36006 this.maxY = Math.max.apply( Math, this.colYs );
36011 if ( this.isFitWidth ) {
36012 size.width = this._getContainerFitWidth();
36018 _getContainerFitWidth : function()
36020 var unusedCols = 0;
36021 // count unused columns
36024 if ( this.colYs[i] !== 0 ) {
36029 // fit container to columns that have been used
36030 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36033 needsResizeLayout : function()
36035 var previousWidth = this.containerWidth;
36036 this.getContainerWidth();
36037 return previousWidth !== this.containerWidth;
36052 * @class Roo.bootstrap.MasonryBrick
36053 * @extends Roo.bootstrap.Component
36054 * Bootstrap MasonryBrick class
36057 * Create a new MasonryBrick
36058 * @param {Object} config The config object
36061 Roo.bootstrap.MasonryBrick = function(config){
36063 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36065 Roo.bootstrap.MasonryBrick.register(this);
36071 * When a MasonryBrick is clcik
36072 * @param {Roo.bootstrap.MasonryBrick} this
36073 * @param {Roo.EventObject} e
36079 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36082 * @cfg {String} title
36086 * @cfg {String} html
36090 * @cfg {String} bgimage
36094 * @cfg {String} videourl
36098 * @cfg {String} cls
36102 * @cfg {String} href
36106 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36111 * @cfg {String} placetitle (center|bottom)
36116 * @cfg {Boolean} isFitContainer defalut true
36118 isFitContainer : true,
36121 * @cfg {Boolean} preventDefault defalut false
36123 preventDefault : false,
36126 * @cfg {Boolean} inverse defalut false
36128 maskInverse : false,
36130 getAutoCreate : function()
36132 if(!this.isFitContainer){
36133 return this.getSplitAutoCreate();
36136 var cls = 'masonry-brick masonry-brick-full';
36138 if(this.href.length){
36139 cls += ' masonry-brick-link';
36142 if(this.bgimage.length){
36143 cls += ' masonry-brick-image';
36146 if(this.maskInverse){
36147 cls += ' mask-inverse';
36150 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36151 cls += ' enable-mask';
36155 cls += ' masonry-' + this.size + '-brick';
36158 if(this.placetitle.length){
36160 switch (this.placetitle) {
36162 cls += ' masonry-center-title';
36165 cls += ' masonry-bottom-title';
36172 if(!this.html.length && !this.bgimage.length){
36173 cls += ' masonry-center-title';
36176 if(!this.html.length && this.bgimage.length){
36177 cls += ' masonry-bottom-title';
36182 cls += ' ' + this.cls;
36186 tag: (this.href.length) ? 'a' : 'div',
36191 cls: 'masonry-brick-mask'
36195 cls: 'masonry-brick-paragraph',
36201 if(this.href.length){
36202 cfg.href = this.href;
36205 var cn = cfg.cn[1].cn;
36207 if(this.title.length){
36210 cls: 'masonry-brick-title',
36215 if(this.html.length){
36218 cls: 'masonry-brick-text',
36223 if (!this.title.length && !this.html.length) {
36224 cfg.cn[1].cls += ' hide';
36227 if(this.bgimage.length){
36230 cls: 'masonry-brick-image-view',
36235 if(this.videourl.length){
36236 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36237 // youtube support only?
36240 cls: 'masonry-brick-image-view',
36243 allowfullscreen : true
36251 getSplitAutoCreate : function()
36253 var cls = 'masonry-brick masonry-brick-split';
36255 if(this.href.length){
36256 cls += ' masonry-brick-link';
36259 if(this.bgimage.length){
36260 cls += ' masonry-brick-image';
36264 cls += ' masonry-' + this.size + '-brick';
36267 switch (this.placetitle) {
36269 cls += ' masonry-center-title';
36272 cls += ' masonry-bottom-title';
36275 if(!this.bgimage.length){
36276 cls += ' masonry-center-title';
36279 if(this.bgimage.length){
36280 cls += ' masonry-bottom-title';
36286 cls += ' ' + this.cls;
36290 tag: (this.href.length) ? 'a' : 'div',
36295 cls: 'masonry-brick-split-head',
36299 cls: 'masonry-brick-paragraph',
36306 cls: 'masonry-brick-split-body',
36312 if(this.href.length){
36313 cfg.href = this.href;
36316 if(this.title.length){
36317 cfg.cn[0].cn[0].cn.push({
36319 cls: 'masonry-brick-title',
36324 if(this.html.length){
36325 cfg.cn[1].cn.push({
36327 cls: 'masonry-brick-text',
36332 if(this.bgimage.length){
36333 cfg.cn[0].cn.push({
36335 cls: 'masonry-brick-image-view',
36340 if(this.videourl.length){
36341 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36342 // youtube support only?
36343 cfg.cn[0].cn.cn.push({
36345 cls: 'masonry-brick-image-view',
36348 allowfullscreen : true
36355 initEvents: function()
36357 switch (this.size) {
36390 this.el.on('touchstart', this.onTouchStart, this);
36391 this.el.on('touchmove', this.onTouchMove, this);
36392 this.el.on('touchend', this.onTouchEnd, this);
36393 this.el.on('contextmenu', this.onContextMenu, this);
36395 this.el.on('mouseenter' ,this.enter, this);
36396 this.el.on('mouseleave', this.leave, this);
36397 this.el.on('click', this.onClick, this);
36400 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36401 this.parent().bricks.push(this);
36406 onClick: function(e, el)
36408 var time = this.endTimer - this.startTimer;
36409 // Roo.log(e.preventDefault());
36412 e.preventDefault();
36417 if(!this.preventDefault){
36421 e.preventDefault();
36423 if (this.activeClass != '') {
36424 this.selectBrick();
36427 this.fireEvent('click', this, e);
36430 enter: function(e, el)
36432 e.preventDefault();
36434 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36438 if(this.bgimage.length && this.html.length){
36439 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36443 leave: function(e, el)
36445 e.preventDefault();
36447 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36451 if(this.bgimage.length && this.html.length){
36452 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36456 onTouchStart: function(e, el)
36458 // e.preventDefault();
36460 this.touchmoved = false;
36462 if(!this.isFitContainer){
36466 if(!this.bgimage.length || !this.html.length){
36470 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36472 this.timer = new Date().getTime();
36476 onTouchMove: function(e, el)
36478 this.touchmoved = true;
36481 onContextMenu : function(e,el)
36483 e.preventDefault();
36484 e.stopPropagation();
36488 onTouchEnd: function(e, el)
36490 // e.preventDefault();
36492 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36499 if(!this.bgimage.length || !this.html.length){
36501 if(this.href.length){
36502 window.location.href = this.href;
36508 if(!this.isFitContainer){
36512 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36514 window.location.href = this.href;
36517 //selection on single brick only
36518 selectBrick : function() {
36520 if (!this.parentId) {
36524 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36525 var index = m.selectedBrick.indexOf(this.id);
36528 m.selectedBrick.splice(index,1);
36529 this.el.removeClass(this.activeClass);
36533 for(var i = 0; i < m.selectedBrick.length; i++) {
36534 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36535 b.el.removeClass(b.activeClass);
36538 m.selectedBrick = [];
36540 m.selectedBrick.push(this.id);
36541 this.el.addClass(this.activeClass);
36545 isSelected : function(){
36546 return this.el.hasClass(this.activeClass);
36551 Roo.apply(Roo.bootstrap.MasonryBrick, {
36554 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36556 * register a Masonry Brick
36557 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36560 register : function(brick)
36562 //this.groups[brick.id] = brick;
36563 this.groups.add(brick.id, brick);
36566 * fetch a masonry brick based on the masonry brick ID
36567 * @param {string} the masonry brick to add
36568 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36571 get: function(brick_id)
36573 // if (typeof(this.groups[brick_id]) == 'undefined') {
36576 // return this.groups[brick_id] ;
36578 if(this.groups.key(brick_id)) {
36579 return this.groups.key(brick_id);
36597 * @class Roo.bootstrap.Brick
36598 * @extends Roo.bootstrap.Component
36599 * Bootstrap Brick class
36602 * Create a new Brick
36603 * @param {Object} config The config object
36606 Roo.bootstrap.Brick = function(config){
36607 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36613 * When a Brick is click
36614 * @param {Roo.bootstrap.Brick} this
36615 * @param {Roo.EventObject} e
36621 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36624 * @cfg {String} title
36628 * @cfg {String} html
36632 * @cfg {String} bgimage
36636 * @cfg {String} cls
36640 * @cfg {String} href
36644 * @cfg {String} video
36648 * @cfg {Boolean} square
36652 getAutoCreate : function()
36654 var cls = 'roo-brick';
36656 if(this.href.length){
36657 cls += ' roo-brick-link';
36660 if(this.bgimage.length){
36661 cls += ' roo-brick-image';
36664 if(!this.html.length && !this.bgimage.length){
36665 cls += ' roo-brick-center-title';
36668 if(!this.html.length && this.bgimage.length){
36669 cls += ' roo-brick-bottom-title';
36673 cls += ' ' + this.cls;
36677 tag: (this.href.length) ? 'a' : 'div',
36682 cls: 'roo-brick-paragraph',
36688 if(this.href.length){
36689 cfg.href = this.href;
36692 var cn = cfg.cn[0].cn;
36694 if(this.title.length){
36697 cls: 'roo-brick-title',
36702 if(this.html.length){
36705 cls: 'roo-brick-text',
36712 if(this.bgimage.length){
36715 cls: 'roo-brick-image-view',
36723 initEvents: function()
36725 if(this.title.length || this.html.length){
36726 this.el.on('mouseenter' ,this.enter, this);
36727 this.el.on('mouseleave', this.leave, this);
36730 Roo.EventManager.onWindowResize(this.resize, this);
36732 if(this.bgimage.length){
36733 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36734 this.imageEl.on('load', this.onImageLoad, this);
36741 onImageLoad : function()
36746 resize : function()
36748 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36750 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36752 if(this.bgimage.length){
36753 var image = this.el.select('.roo-brick-image-view', true).first();
36755 image.setWidth(paragraph.getWidth());
36758 image.setHeight(paragraph.getWidth());
36761 this.el.setHeight(image.getHeight());
36762 paragraph.setHeight(image.getHeight());
36768 enter: function(e, el)
36770 e.preventDefault();
36772 if(this.bgimage.length){
36773 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36774 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36778 leave: function(e, el)
36780 e.preventDefault();
36782 if(this.bgimage.length){
36783 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36784 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36799 * @class Roo.bootstrap.NumberField
36800 * @extends Roo.bootstrap.Input
36801 * Bootstrap NumberField class
36807 * Create a new NumberField
36808 * @param {Object} config The config object
36811 Roo.bootstrap.NumberField = function(config){
36812 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36815 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36818 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36820 allowDecimals : true,
36822 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36824 decimalSeparator : ".",
36826 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36828 decimalPrecision : 2,
36830 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36832 allowNegative : true,
36835 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36839 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36841 minValue : Number.NEGATIVE_INFINITY,
36843 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36845 maxValue : Number.MAX_VALUE,
36847 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36849 minText : "The minimum value for this field is {0}",
36851 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36853 maxText : "The maximum value for this field is {0}",
36855 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36856 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36858 nanText : "{0} is not a valid number",
36860 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36862 thousandsDelimiter : false,
36864 * @cfg {String} valueAlign alignment of value
36866 valueAlign : "left",
36868 getAutoCreate : function()
36870 var hiddenInput = {
36874 cls: 'hidden-number-input'
36878 hiddenInput.name = this.name;
36883 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36885 this.name = hiddenInput.name;
36887 if(cfg.cn.length > 0) {
36888 cfg.cn.push(hiddenInput);
36895 initEvents : function()
36897 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36899 var allowed = "0123456789";
36901 if(this.allowDecimals){
36902 allowed += this.decimalSeparator;
36905 if(this.allowNegative){
36909 if(this.thousandsDelimiter) {
36913 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36915 var keyPress = function(e){
36917 var k = e.getKey();
36919 var c = e.getCharCode();
36922 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36923 allowed.indexOf(String.fromCharCode(c)) === -1
36929 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36933 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36938 this.el.on("keypress", keyPress, this);
36941 validateValue : function(value)
36944 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36948 var num = this.parseValue(value);
36951 this.markInvalid(String.format(this.nanText, value));
36955 if(num < this.minValue){
36956 this.markInvalid(String.format(this.minText, this.minValue));
36960 if(num > this.maxValue){
36961 this.markInvalid(String.format(this.maxText, this.maxValue));
36968 getValue : function()
36970 var v = this.hiddenEl().getValue();
36972 return this.fixPrecision(this.parseValue(v));
36975 parseValue : function(value)
36977 if(this.thousandsDelimiter) {
36979 r = new RegExp(",", "g");
36980 value = value.replace(r, "");
36983 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36984 return isNaN(value) ? '' : value;
36987 fixPrecision : function(value)
36989 if(this.thousandsDelimiter) {
36991 r = new RegExp(",", "g");
36992 value = value.replace(r, "");
36995 var nan = isNaN(value);
36997 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36998 return nan ? '' : value;
37000 return parseFloat(value).toFixed(this.decimalPrecision);
37003 setValue : function(v)
37005 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37011 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37013 this.inputEl().dom.value = (v == '') ? '' :
37014 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37016 if(!this.allowZero && v === '0') {
37017 this.hiddenEl().dom.value = '';
37018 this.inputEl().dom.value = '';
37025 decimalPrecisionFcn : function(v)
37027 return Math.floor(v);
37030 beforeBlur : function()
37032 var v = this.parseValue(this.getRawValue());
37034 if(v || v === 0 || v === ''){
37039 hiddenEl : function()
37041 return this.el.select('input.hidden-number-input',true).first();
37053 * @class Roo.bootstrap.DocumentSlider
37054 * @extends Roo.bootstrap.Component
37055 * Bootstrap DocumentSlider class
37058 * Create a new DocumentViewer
37059 * @param {Object} config The config object
37062 Roo.bootstrap.DocumentSlider = function(config){
37063 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37070 * Fire after initEvent
37071 * @param {Roo.bootstrap.DocumentSlider} this
37076 * Fire after update
37077 * @param {Roo.bootstrap.DocumentSlider} this
37083 * @param {Roo.bootstrap.DocumentSlider} this
37089 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37095 getAutoCreate : function()
37099 cls : 'roo-document-slider',
37103 cls : 'roo-document-slider-header',
37107 cls : 'roo-document-slider-header-title'
37113 cls : 'roo-document-slider-body',
37117 cls : 'roo-document-slider-prev',
37121 cls : 'fa fa-chevron-left'
37127 cls : 'roo-document-slider-thumb',
37131 cls : 'roo-document-slider-image'
37137 cls : 'roo-document-slider-next',
37141 cls : 'fa fa-chevron-right'
37153 initEvents : function()
37155 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37156 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37158 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37159 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37161 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37162 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37164 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37165 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37167 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37168 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37170 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37171 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37173 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37174 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37176 this.thumbEl.on('click', this.onClick, this);
37178 this.prevIndicator.on('click', this.prev, this);
37180 this.nextIndicator.on('click', this.next, this);
37184 initial : function()
37186 if(this.files.length){
37187 this.indicator = 1;
37191 this.fireEvent('initial', this);
37194 update : function()
37196 this.imageEl.attr('src', this.files[this.indicator - 1]);
37198 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37200 this.prevIndicator.show();
37202 if(this.indicator == 1){
37203 this.prevIndicator.hide();
37206 this.nextIndicator.show();
37208 if(this.indicator == this.files.length){
37209 this.nextIndicator.hide();
37212 this.thumbEl.scrollTo('top');
37214 this.fireEvent('update', this);
37217 onClick : function(e)
37219 e.preventDefault();
37221 this.fireEvent('click', this);
37226 e.preventDefault();
37228 this.indicator = Math.max(1, this.indicator - 1);
37235 e.preventDefault();
37237 this.indicator = Math.min(this.files.length, this.indicator + 1);
37251 * @class Roo.bootstrap.RadioSet
37252 * @extends Roo.bootstrap.Input
37253 * Bootstrap RadioSet class
37254 * @cfg {String} indicatorpos (left|right) default left
37255 * @cfg {Boolean} inline (true|false) inline the element (default true)
37256 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37258 * Create a new RadioSet
37259 * @param {Object} config The config object
37262 Roo.bootstrap.RadioSet = function(config){
37264 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37268 Roo.bootstrap.RadioSet.register(this);
37273 * Fires when the element is checked or unchecked.
37274 * @param {Roo.bootstrap.RadioSet} this This radio
37275 * @param {Roo.bootstrap.Radio} item The checked item
37280 * Fires when the element is click.
37281 * @param {Roo.bootstrap.RadioSet} this This radio set
37282 * @param {Roo.bootstrap.Radio} item The checked item
37283 * @param {Roo.EventObject} e The event object
37290 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37298 indicatorpos : 'left',
37300 getAutoCreate : function()
37304 cls : 'roo-radio-set-label',
37308 html : this.fieldLabel
37312 if (Roo.bootstrap.version == 3) {
37315 if(this.indicatorpos == 'left'){
37318 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37319 tooltip : 'This field is required'
37324 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37325 tooltip : 'This field is required'
37331 cls : 'roo-radio-set-items'
37334 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37336 if (align === 'left' && this.fieldLabel.length) {
37339 cls : "roo-radio-set-right",
37345 if(this.labelWidth > 12){
37346 label.style = "width: " + this.labelWidth + 'px';
37349 if(this.labelWidth < 13 && this.labelmd == 0){
37350 this.labelmd = this.labelWidth;
37353 if(this.labellg > 0){
37354 label.cls += ' col-lg-' + this.labellg;
37355 items.cls += ' col-lg-' + (12 - this.labellg);
37358 if(this.labelmd > 0){
37359 label.cls += ' col-md-' + this.labelmd;
37360 items.cls += ' col-md-' + (12 - this.labelmd);
37363 if(this.labelsm > 0){
37364 label.cls += ' col-sm-' + this.labelsm;
37365 items.cls += ' col-sm-' + (12 - this.labelsm);
37368 if(this.labelxs > 0){
37369 label.cls += ' col-xs-' + this.labelxs;
37370 items.cls += ' col-xs-' + (12 - this.labelxs);
37376 cls : 'roo-radio-set',
37380 cls : 'roo-radio-set-input',
37383 value : this.value ? this.value : ''
37390 if(this.weight.length){
37391 cfg.cls += ' roo-radio-' + this.weight;
37395 cfg.cls += ' roo-radio-set-inline';
37399 ['xs','sm','md','lg'].map(function(size){
37400 if (settings[size]) {
37401 cfg.cls += ' col-' + size + '-' + settings[size];
37409 initEvents : function()
37411 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37412 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37414 if(!this.fieldLabel.length){
37415 this.labelEl.hide();
37418 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37419 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37421 this.indicator = this.indicatorEl();
37423 if(this.indicator){
37424 this.indicator.addClass('invisible');
37427 this.originalValue = this.getValue();
37431 inputEl: function ()
37433 return this.el.select('.roo-radio-set-input', true).first();
37436 getChildContainer : function()
37438 return this.itemsEl;
37441 register : function(item)
37443 this.radioes.push(item);
37447 validate : function()
37449 if(this.getVisibilityEl().hasClass('hidden')){
37455 Roo.each(this.radioes, function(i){
37464 if(this.allowBlank) {
37468 if(this.disabled || valid){
37473 this.markInvalid();
37478 markValid : function()
37480 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37481 this.indicatorEl().removeClass('visible');
37482 this.indicatorEl().addClass('invisible');
37486 if (Roo.bootstrap.version == 3) {
37487 this.el.removeClass([this.invalidClass, this.validClass]);
37488 this.el.addClass(this.validClass);
37490 this.el.removeClass(['is-invalid','is-valid']);
37491 this.el.addClass(['is-valid']);
37493 this.fireEvent('valid', this);
37496 markInvalid : function(msg)
37498 if(this.allowBlank || this.disabled){
37502 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37503 this.indicatorEl().removeClass('invisible');
37504 this.indicatorEl().addClass('visible');
37506 if (Roo.bootstrap.version == 3) {
37507 this.el.removeClass([this.invalidClass, this.validClass]);
37508 this.el.addClass(this.invalidClass);
37510 this.el.removeClass(['is-invalid','is-valid']);
37511 this.el.addClass(['is-invalid']);
37514 this.fireEvent('invalid', this, msg);
37518 setValue : function(v, suppressEvent)
37520 if(this.value === v){
37527 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37530 Roo.each(this.radioes, function(i){
37532 i.el.removeClass('checked');
37535 Roo.each(this.radioes, function(i){
37537 if(i.value === v || i.value.toString() === v.toString()){
37539 i.el.addClass('checked');
37541 if(suppressEvent !== true){
37542 this.fireEvent('check', this, i);
37553 clearInvalid : function(){
37555 if(!this.el || this.preventMark){
37559 this.el.removeClass([this.invalidClass]);
37561 this.fireEvent('valid', this);
37566 Roo.apply(Roo.bootstrap.RadioSet, {
37570 register : function(set)
37572 this.groups[set.name] = set;
37575 get: function(name)
37577 if (typeof(this.groups[name]) == 'undefined') {
37581 return this.groups[name] ;
37587 * Ext JS Library 1.1.1
37588 * Copyright(c) 2006-2007, Ext JS, LLC.
37590 * Originally Released Under LGPL - original licence link has changed is not relivant.
37593 * <script type="text/javascript">
37598 * @class Roo.bootstrap.SplitBar
37599 * @extends Roo.util.Observable
37600 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37604 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37605 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37606 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37607 split.minSize = 100;
37608 split.maxSize = 600;
37609 split.animate = true;
37610 split.on('moved', splitterMoved);
37613 * Create a new SplitBar
37614 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37615 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37616 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37617 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37618 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37619 position of the SplitBar).
37621 Roo.bootstrap.SplitBar = function(cfg){
37626 // dragElement : elm
37627 // resizingElement: el,
37629 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37630 // placement : Roo.bootstrap.SplitBar.LEFT ,
37631 // existingProxy ???
37634 this.el = Roo.get(cfg.dragElement, true);
37635 this.el.dom.unselectable = "on";
37637 this.resizingEl = Roo.get(cfg.resizingElement, true);
37641 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37642 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37645 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37648 * The minimum size of the resizing element. (Defaults to 0)
37654 * The maximum size of the resizing element. (Defaults to 2000)
37657 this.maxSize = 2000;
37660 * Whether to animate the transition to the new size
37663 this.animate = false;
37666 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37669 this.useShim = false;
37674 if(!cfg.existingProxy){
37676 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37678 this.proxy = Roo.get(cfg.existingProxy).dom;
37681 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37684 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37687 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37690 this.dragSpecs = {};
37693 * @private The adapter to use to positon and resize elements
37695 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37696 this.adapter.init(this);
37698 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37700 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37701 this.el.addClass("roo-splitbar-h");
37704 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37705 this.el.addClass("roo-splitbar-v");
37711 * Fires when the splitter is moved (alias for {@link #event-moved})
37712 * @param {Roo.bootstrap.SplitBar} this
37713 * @param {Number} newSize the new width or height
37718 * Fires when the splitter is moved
37719 * @param {Roo.bootstrap.SplitBar} this
37720 * @param {Number} newSize the new width or height
37724 * @event beforeresize
37725 * Fires before the splitter is dragged
37726 * @param {Roo.bootstrap.SplitBar} this
37728 "beforeresize" : true,
37730 "beforeapply" : true
37733 Roo.util.Observable.call(this);
37736 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37737 onStartProxyDrag : function(x, y){
37738 this.fireEvent("beforeresize", this);
37740 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37742 o.enableDisplayMode("block");
37743 // all splitbars share the same overlay
37744 Roo.bootstrap.SplitBar.prototype.overlay = o;
37746 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37747 this.overlay.show();
37748 Roo.get(this.proxy).setDisplayed("block");
37749 var size = this.adapter.getElementSize(this);
37750 this.activeMinSize = this.getMinimumSize();;
37751 this.activeMaxSize = this.getMaximumSize();;
37752 var c1 = size - this.activeMinSize;
37753 var c2 = Math.max(this.activeMaxSize - size, 0);
37754 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37755 this.dd.resetConstraints();
37756 this.dd.setXConstraint(
37757 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37758 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37760 this.dd.setYConstraint(0, 0);
37762 this.dd.resetConstraints();
37763 this.dd.setXConstraint(0, 0);
37764 this.dd.setYConstraint(
37765 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37766 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37769 this.dragSpecs.startSize = size;
37770 this.dragSpecs.startPoint = [x, y];
37771 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37775 * @private Called after the drag operation by the DDProxy
37777 onEndProxyDrag : function(e){
37778 Roo.get(this.proxy).setDisplayed(false);
37779 var endPoint = Roo.lib.Event.getXY(e);
37781 this.overlay.hide();
37784 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37785 newSize = this.dragSpecs.startSize +
37786 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37787 endPoint[0] - this.dragSpecs.startPoint[0] :
37788 this.dragSpecs.startPoint[0] - endPoint[0]
37791 newSize = this.dragSpecs.startSize +
37792 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37793 endPoint[1] - this.dragSpecs.startPoint[1] :
37794 this.dragSpecs.startPoint[1] - endPoint[1]
37797 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37798 if(newSize != this.dragSpecs.startSize){
37799 if(this.fireEvent('beforeapply', this, newSize) !== false){
37800 this.adapter.setElementSize(this, newSize);
37801 this.fireEvent("moved", this, newSize);
37802 this.fireEvent("resize", this, newSize);
37808 * Get the adapter this SplitBar uses
37809 * @return The adapter object
37811 getAdapter : function(){
37812 return this.adapter;
37816 * Set the adapter this SplitBar uses
37817 * @param {Object} adapter A SplitBar adapter object
37819 setAdapter : function(adapter){
37820 this.adapter = adapter;
37821 this.adapter.init(this);
37825 * Gets the minimum size for the resizing element
37826 * @return {Number} The minimum size
37828 getMinimumSize : function(){
37829 return this.minSize;
37833 * Sets the minimum size for the resizing element
37834 * @param {Number} minSize The minimum size
37836 setMinimumSize : function(minSize){
37837 this.minSize = minSize;
37841 * Gets the maximum size for the resizing element
37842 * @return {Number} The maximum size
37844 getMaximumSize : function(){
37845 return this.maxSize;
37849 * Sets the maximum size for the resizing element
37850 * @param {Number} maxSize The maximum size
37852 setMaximumSize : function(maxSize){
37853 this.maxSize = maxSize;
37857 * Sets the initialize size for the resizing element
37858 * @param {Number} size The initial size
37860 setCurrentSize : function(size){
37861 var oldAnimate = this.animate;
37862 this.animate = false;
37863 this.adapter.setElementSize(this, size);
37864 this.animate = oldAnimate;
37868 * Destroy this splitbar.
37869 * @param {Boolean} removeEl True to remove the element
37871 destroy : function(removeEl){
37873 this.shim.remove();
37876 this.proxy.parentNode.removeChild(this.proxy);
37884 * @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.
37886 Roo.bootstrap.SplitBar.createProxy = function(dir){
37887 var proxy = new Roo.Element(document.createElement("div"));
37888 proxy.unselectable();
37889 var cls = 'roo-splitbar-proxy';
37890 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37891 document.body.appendChild(proxy.dom);
37896 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37897 * Default Adapter. It assumes the splitter and resizing element are not positioned
37898 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37900 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37903 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37904 // do nothing for now
37905 init : function(s){
37909 * Called before drag operations to get the current size of the resizing element.
37910 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37912 getElementSize : function(s){
37913 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37914 return s.resizingEl.getWidth();
37916 return s.resizingEl.getHeight();
37921 * Called after drag operations to set the size of the resizing element.
37922 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37923 * @param {Number} newSize The new size to set
37924 * @param {Function} onComplete A function to be invoked when resizing is complete
37926 setElementSize : function(s, newSize, onComplete){
37927 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37929 s.resizingEl.setWidth(newSize);
37931 onComplete(s, newSize);
37934 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37939 s.resizingEl.setHeight(newSize);
37941 onComplete(s, newSize);
37944 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37951 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37952 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37953 * Adapter that moves the splitter element to align with the resized sizing element.
37954 * Used with an absolute positioned SplitBar.
37955 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37956 * document.body, make sure you assign an id to the body element.
37958 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37959 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37960 this.container = Roo.get(container);
37963 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37964 init : function(s){
37965 this.basic.init(s);
37968 getElementSize : function(s){
37969 return this.basic.getElementSize(s);
37972 setElementSize : function(s, newSize, onComplete){
37973 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37976 moveSplitter : function(s){
37977 var yes = Roo.bootstrap.SplitBar;
37978 switch(s.placement){
37980 s.el.setX(s.resizingEl.getRight());
37983 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37986 s.el.setY(s.resizingEl.getBottom());
37989 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37996 * Orientation constant - Create a vertical SplitBar
38000 Roo.bootstrap.SplitBar.VERTICAL = 1;
38003 * Orientation constant - Create a horizontal SplitBar
38007 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38010 * Placement constant - The resizing element is to the left of the splitter element
38014 Roo.bootstrap.SplitBar.LEFT = 1;
38017 * Placement constant - The resizing element is to the right of the splitter element
38021 Roo.bootstrap.SplitBar.RIGHT = 2;
38024 * Placement constant - The resizing element is positioned above the splitter element
38028 Roo.bootstrap.SplitBar.TOP = 3;
38031 * Placement constant - The resizing element is positioned under splitter element
38035 Roo.bootstrap.SplitBar.BOTTOM = 4;
38036 Roo.namespace("Roo.bootstrap.layout");/*
38038 * Ext JS Library 1.1.1
38039 * Copyright(c) 2006-2007, Ext JS, LLC.
38041 * Originally Released Under LGPL - original licence link has changed is not relivant.
38044 * <script type="text/javascript">
38048 * @class Roo.bootstrap.layout.Manager
38049 * @extends Roo.bootstrap.Component
38050 * Base class for layout managers.
38052 Roo.bootstrap.layout.Manager = function(config)
38054 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38060 /** false to disable window resize monitoring @type Boolean */
38061 this.monitorWindowResize = true;
38066 * Fires when a layout is performed.
38067 * @param {Roo.LayoutManager} this
38071 * @event regionresized
38072 * Fires when the user resizes a region.
38073 * @param {Roo.LayoutRegion} region The resized region
38074 * @param {Number} newSize The new size (width for east/west, height for north/south)
38076 "regionresized" : true,
38078 * @event regioncollapsed
38079 * Fires when a region is collapsed.
38080 * @param {Roo.LayoutRegion} region The collapsed region
38082 "regioncollapsed" : true,
38084 * @event regionexpanded
38085 * Fires when a region is expanded.
38086 * @param {Roo.LayoutRegion} region The expanded region
38088 "regionexpanded" : true
38090 this.updating = false;
38093 this.el = Roo.get(config.el);
38099 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38104 monitorWindowResize : true,
38110 onRender : function(ct, position)
38113 this.el = Roo.get(ct);
38116 //this.fireEvent('render',this);
38120 initEvents: function()
38124 // ie scrollbar fix
38125 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38126 document.body.scroll = "no";
38127 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38128 this.el.position('relative');
38130 this.id = this.el.id;
38131 this.el.addClass("roo-layout-container");
38132 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38133 if(this.el.dom != document.body ) {
38134 this.el.on('resize', this.layout,this);
38135 this.el.on('show', this.layout,this);
38141 * Returns true if this layout is currently being updated
38142 * @return {Boolean}
38144 isUpdating : function(){
38145 return this.updating;
38149 * Suspend the LayoutManager from doing auto-layouts while
38150 * making multiple add or remove calls
38152 beginUpdate : function(){
38153 this.updating = true;
38157 * Restore auto-layouts and optionally disable the manager from performing a layout
38158 * @param {Boolean} noLayout true to disable a layout update
38160 endUpdate : function(noLayout){
38161 this.updating = false;
38167 layout: function(){
38171 onRegionResized : function(region, newSize){
38172 this.fireEvent("regionresized", region, newSize);
38176 onRegionCollapsed : function(region){
38177 this.fireEvent("regioncollapsed", region);
38180 onRegionExpanded : function(region){
38181 this.fireEvent("regionexpanded", region);
38185 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38186 * performs box-model adjustments.
38187 * @return {Object} The size as an object {width: (the width), height: (the height)}
38189 getViewSize : function()
38192 if(this.el.dom != document.body){
38193 size = this.el.getSize();
38195 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38197 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38198 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38203 * Returns the Element this layout is bound to.
38204 * @return {Roo.Element}
38206 getEl : function(){
38211 * Returns the specified region.
38212 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38213 * @return {Roo.LayoutRegion}
38215 getRegion : function(target){
38216 return this.regions[target.toLowerCase()];
38219 onWindowResize : function(){
38220 if(this.monitorWindowResize){
38227 * Ext JS Library 1.1.1
38228 * Copyright(c) 2006-2007, Ext JS, LLC.
38230 * Originally Released Under LGPL - original licence link has changed is not relivant.
38233 * <script type="text/javascript">
38236 * @class Roo.bootstrap.layout.Border
38237 * @extends Roo.bootstrap.layout.Manager
38238 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38239 * please see: examples/bootstrap/nested.html<br><br>
38241 <b>The container the layout is rendered into can be either the body element or any other element.
38242 If it is not the body element, the container needs to either be an absolute positioned element,
38243 or you will need to add "position:relative" to the css of the container. You will also need to specify
38244 the container size if it is not the body element.</b>
38247 * Create a new Border
38248 * @param {Object} config Configuration options
38250 Roo.bootstrap.layout.Border = function(config){
38251 config = config || {};
38252 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38256 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38257 if(config[region]){
38258 config[region].region = region;
38259 this.addRegion(config[region]);
38265 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38267 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38269 parent : false, // this might point to a 'nest' or a ???
38272 * Creates and adds a new region if it doesn't already exist.
38273 * @param {String} target The target region key (north, south, east, west or center).
38274 * @param {Object} config The regions config object
38275 * @return {BorderLayoutRegion} The new region
38277 addRegion : function(config)
38279 if(!this.regions[config.region]){
38280 var r = this.factory(config);
38281 this.bindRegion(r);
38283 return this.regions[config.region];
38287 bindRegion : function(r){
38288 this.regions[r.config.region] = r;
38290 r.on("visibilitychange", this.layout, this);
38291 r.on("paneladded", this.layout, this);
38292 r.on("panelremoved", this.layout, this);
38293 r.on("invalidated", this.layout, this);
38294 r.on("resized", this.onRegionResized, this);
38295 r.on("collapsed", this.onRegionCollapsed, this);
38296 r.on("expanded", this.onRegionExpanded, this);
38300 * Performs a layout update.
38302 layout : function()
38304 if(this.updating) {
38308 // render all the rebions if they have not been done alreayd?
38309 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38310 if(this.regions[region] && !this.regions[region].bodyEl){
38311 this.regions[region].onRender(this.el)
38315 var size = this.getViewSize();
38316 var w = size.width;
38317 var h = size.height;
38322 //var x = 0, y = 0;
38324 var rs = this.regions;
38325 var north = rs["north"];
38326 var south = rs["south"];
38327 var west = rs["west"];
38328 var east = rs["east"];
38329 var center = rs["center"];
38330 //if(this.hideOnLayout){ // not supported anymore
38331 //c.el.setStyle("display", "none");
38333 if(north && north.isVisible()){
38334 var b = north.getBox();
38335 var m = north.getMargins();
38336 b.width = w - (m.left+m.right);
38339 centerY = b.height + b.y + m.bottom;
38340 centerH -= centerY;
38341 north.updateBox(this.safeBox(b));
38343 if(south && south.isVisible()){
38344 var b = south.getBox();
38345 var m = south.getMargins();
38346 b.width = w - (m.left+m.right);
38348 var totalHeight = (b.height + m.top + m.bottom);
38349 b.y = h - totalHeight + m.top;
38350 centerH -= totalHeight;
38351 south.updateBox(this.safeBox(b));
38353 if(west && west.isVisible()){
38354 var b = west.getBox();
38355 var m = west.getMargins();
38356 b.height = centerH - (m.top+m.bottom);
38358 b.y = centerY + m.top;
38359 var totalWidth = (b.width + m.left + m.right);
38360 centerX += totalWidth;
38361 centerW -= totalWidth;
38362 west.updateBox(this.safeBox(b));
38364 if(east && east.isVisible()){
38365 var b = east.getBox();
38366 var m = east.getMargins();
38367 b.height = centerH - (m.top+m.bottom);
38368 var totalWidth = (b.width + m.left + m.right);
38369 b.x = w - totalWidth + m.left;
38370 b.y = centerY + m.top;
38371 centerW -= totalWidth;
38372 east.updateBox(this.safeBox(b));
38375 var m = center.getMargins();
38377 x: centerX + m.left,
38378 y: centerY + m.top,
38379 width: centerW - (m.left+m.right),
38380 height: centerH - (m.top+m.bottom)
38382 //if(this.hideOnLayout){
38383 //center.el.setStyle("display", "block");
38385 center.updateBox(this.safeBox(centerBox));
38388 this.fireEvent("layout", this);
38392 safeBox : function(box){
38393 box.width = Math.max(0, box.width);
38394 box.height = Math.max(0, box.height);
38399 * Adds a ContentPanel (or subclass) to this layout.
38400 * @param {String} target The target region key (north, south, east, west or center).
38401 * @param {Roo.ContentPanel} panel The panel to add
38402 * @return {Roo.ContentPanel} The added panel
38404 add : function(target, panel){
38406 target = target.toLowerCase();
38407 return this.regions[target].add(panel);
38411 * Remove a ContentPanel (or subclass) to this layout.
38412 * @param {String} target The target region key (north, south, east, west or center).
38413 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38414 * @return {Roo.ContentPanel} The removed panel
38416 remove : function(target, panel){
38417 target = target.toLowerCase();
38418 return this.regions[target].remove(panel);
38422 * Searches all regions for a panel with the specified id
38423 * @param {String} panelId
38424 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38426 findPanel : function(panelId){
38427 var rs = this.regions;
38428 for(var target in rs){
38429 if(typeof rs[target] != "function"){
38430 var p = rs[target].getPanel(panelId);
38440 * Searches all regions for a panel with the specified id and activates (shows) it.
38441 * @param {String/ContentPanel} panelId The panels id or the panel itself
38442 * @return {Roo.ContentPanel} The shown panel or null
38444 showPanel : function(panelId) {
38445 var rs = this.regions;
38446 for(var target in rs){
38447 var r = rs[target];
38448 if(typeof r != "function"){
38449 if(r.hasPanel(panelId)){
38450 return r.showPanel(panelId);
38458 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38459 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38462 restoreState : function(provider){
38464 provider = Roo.state.Manager;
38466 var sm = new Roo.LayoutStateManager();
38467 sm.init(this, provider);
38473 * Adds a xtype elements to the layout.
38477 xtype : 'ContentPanel',
38484 xtype : 'NestedLayoutPanel',
38490 items : [ ... list of content panels or nested layout panels.. ]
38494 * @param {Object} cfg Xtype definition of item to add.
38496 addxtype : function(cfg)
38498 // basically accepts a pannel...
38499 // can accept a layout region..!?!?
38500 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38503 // theory? children can only be panels??
38505 //if (!cfg.xtype.match(/Panel$/)) {
38510 if (typeof(cfg.region) == 'undefined') {
38511 Roo.log("Failed to add Panel, region was not set");
38515 var region = cfg.region;
38521 xitems = cfg.items;
38526 if ( region == 'center') {
38527 Roo.log("Center: " + cfg.title);
38533 case 'Content': // ContentPanel (el, cfg)
38534 case 'Scroll': // ContentPanel (el, cfg)
38536 cfg.autoCreate = cfg.autoCreate || true;
38537 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38539 // var el = this.el.createChild();
38540 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38543 this.add(region, ret);
38547 case 'TreePanel': // our new panel!
38548 cfg.el = this.el.createChild();
38549 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38550 this.add(region, ret);
38555 // create a new Layout (which is a Border Layout...
38557 var clayout = cfg.layout;
38558 clayout.el = this.el.createChild();
38559 clayout.items = clayout.items || [];
38563 // replace this exitems with the clayout ones..
38564 xitems = clayout.items;
38566 // force background off if it's in center...
38567 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38568 cfg.background = false;
38570 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38573 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38574 //console.log('adding nested layout panel ' + cfg.toSource());
38575 this.add(region, ret);
38576 nb = {}; /// find first...
38581 // needs grid and region
38583 //var el = this.getRegion(region).el.createChild();
38585 *var el = this.el.createChild();
38586 // create the grid first...
38587 cfg.grid.container = el;
38588 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38591 if (region == 'center' && this.active ) {
38592 cfg.background = false;
38595 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38597 this.add(region, ret);
38599 if (cfg.background) {
38600 // render grid on panel activation (if panel background)
38601 ret.on('activate', function(gp) {
38602 if (!gp.grid.rendered) {
38603 // gp.grid.render(el);
38607 // cfg.grid.render(el);
38613 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38614 // it was the old xcomponent building that caused this before.
38615 // espeically if border is the top element in the tree.
38625 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38627 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38628 this.add(region, ret);
38632 throw "Can not add '" + cfg.xtype + "' to Border";
38638 this.beginUpdate();
38642 Roo.each(xitems, function(i) {
38643 region = nb && i.region ? i.region : false;
38645 var add = ret.addxtype(i);
38648 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38649 if (!i.background) {
38650 abn[region] = nb[region] ;
38657 // make the last non-background panel active..
38658 //if (nb) { Roo.log(abn); }
38661 for(var r in abn) {
38662 region = this.getRegion(r);
38664 // tried using nb[r], but it does not work..
38666 region.showPanel(abn[r]);
38677 factory : function(cfg)
38680 var validRegions = Roo.bootstrap.layout.Border.regions;
38682 var target = cfg.region;
38685 var r = Roo.bootstrap.layout;
38689 return new r.North(cfg);
38691 return new r.South(cfg);
38693 return new r.East(cfg);
38695 return new r.West(cfg);
38697 return new r.Center(cfg);
38699 throw 'Layout region "'+target+'" not supported.';
38706 * Ext JS Library 1.1.1
38707 * Copyright(c) 2006-2007, Ext JS, LLC.
38709 * Originally Released Under LGPL - original licence link has changed is not relivant.
38712 * <script type="text/javascript">
38716 * @class Roo.bootstrap.layout.Basic
38717 * @extends Roo.util.Observable
38718 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38719 * and does not have a titlebar, tabs or any other features. All it does is size and position
38720 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38721 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38722 * @cfg {string} region the region that it inhabits..
38723 * @cfg {bool} skipConfig skip config?
38727 Roo.bootstrap.layout.Basic = function(config){
38729 this.mgr = config.mgr;
38731 this.position = config.region;
38733 var skipConfig = config.skipConfig;
38737 * @scope Roo.BasicLayoutRegion
38741 * @event beforeremove
38742 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38743 * @param {Roo.LayoutRegion} this
38744 * @param {Roo.ContentPanel} panel The panel
38745 * @param {Object} e The cancel event object
38747 "beforeremove" : true,
38749 * @event invalidated
38750 * Fires when the layout for this region is changed.
38751 * @param {Roo.LayoutRegion} this
38753 "invalidated" : true,
38755 * @event visibilitychange
38756 * Fires when this region is shown or hidden
38757 * @param {Roo.LayoutRegion} this
38758 * @param {Boolean} visibility true or false
38760 "visibilitychange" : true,
38762 * @event paneladded
38763 * Fires when a panel is added.
38764 * @param {Roo.LayoutRegion} this
38765 * @param {Roo.ContentPanel} panel The panel
38767 "paneladded" : true,
38769 * @event panelremoved
38770 * Fires when a panel is removed.
38771 * @param {Roo.LayoutRegion} this
38772 * @param {Roo.ContentPanel} panel The panel
38774 "panelremoved" : true,
38776 * @event beforecollapse
38777 * Fires when this region before collapse.
38778 * @param {Roo.LayoutRegion} this
38780 "beforecollapse" : true,
38783 * Fires when this region is collapsed.
38784 * @param {Roo.LayoutRegion} this
38786 "collapsed" : true,
38789 * Fires when this region is expanded.
38790 * @param {Roo.LayoutRegion} this
38795 * Fires when this region is slid into view.
38796 * @param {Roo.LayoutRegion} this
38798 "slideshow" : true,
38801 * Fires when this region slides out of view.
38802 * @param {Roo.LayoutRegion} this
38804 "slidehide" : true,
38806 * @event panelactivated
38807 * Fires when a panel is activated.
38808 * @param {Roo.LayoutRegion} this
38809 * @param {Roo.ContentPanel} panel The activated panel
38811 "panelactivated" : true,
38814 * Fires when the user resizes this region.
38815 * @param {Roo.LayoutRegion} this
38816 * @param {Number} newSize The new size (width for east/west, height for north/south)
38820 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38821 this.panels = new Roo.util.MixedCollection();
38822 this.panels.getKey = this.getPanelId.createDelegate(this);
38824 this.activePanel = null;
38825 // ensure listeners are added...
38827 if (config.listeners || config.events) {
38828 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38829 listeners : config.listeners || {},
38830 events : config.events || {}
38834 if(skipConfig !== true){
38835 this.applyConfig(config);
38839 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38841 getPanelId : function(p){
38845 applyConfig : function(config){
38846 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38847 this.config = config;
38852 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38853 * the width, for horizontal (north, south) the height.
38854 * @param {Number} newSize The new width or height
38856 resizeTo : function(newSize){
38857 var el = this.el ? this.el :
38858 (this.activePanel ? this.activePanel.getEl() : null);
38860 switch(this.position){
38863 el.setWidth(newSize);
38864 this.fireEvent("resized", this, newSize);
38868 el.setHeight(newSize);
38869 this.fireEvent("resized", this, newSize);
38875 getBox : function(){
38876 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38879 getMargins : function(){
38880 return this.margins;
38883 updateBox : function(box){
38885 var el = this.activePanel.getEl();
38886 el.dom.style.left = box.x + "px";
38887 el.dom.style.top = box.y + "px";
38888 this.activePanel.setSize(box.width, box.height);
38892 * Returns the container element for this region.
38893 * @return {Roo.Element}
38895 getEl : function(){
38896 return this.activePanel;
38900 * Returns true if this region is currently visible.
38901 * @return {Boolean}
38903 isVisible : function(){
38904 return this.activePanel ? true : false;
38907 setActivePanel : function(panel){
38908 panel = this.getPanel(panel);
38909 if(this.activePanel && this.activePanel != panel){
38910 this.activePanel.setActiveState(false);
38911 this.activePanel.getEl().setLeftTop(-10000,-10000);
38913 this.activePanel = panel;
38914 panel.setActiveState(true);
38916 panel.setSize(this.box.width, this.box.height);
38918 this.fireEvent("panelactivated", this, panel);
38919 this.fireEvent("invalidated");
38923 * Show the specified panel.
38924 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38925 * @return {Roo.ContentPanel} The shown panel or null
38927 showPanel : function(panel){
38928 panel = this.getPanel(panel);
38930 this.setActivePanel(panel);
38936 * Get the active panel for this region.
38937 * @return {Roo.ContentPanel} The active panel or null
38939 getActivePanel : function(){
38940 return this.activePanel;
38944 * Add the passed ContentPanel(s)
38945 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38946 * @return {Roo.ContentPanel} The panel added (if only one was added)
38948 add : function(panel){
38949 if(arguments.length > 1){
38950 for(var i = 0, len = arguments.length; i < len; i++) {
38951 this.add(arguments[i]);
38955 if(this.hasPanel(panel)){
38956 this.showPanel(panel);
38959 var el = panel.getEl();
38960 if(el.dom.parentNode != this.mgr.el.dom){
38961 this.mgr.el.dom.appendChild(el.dom);
38963 if(panel.setRegion){
38964 panel.setRegion(this);
38966 this.panels.add(panel);
38967 el.setStyle("position", "absolute");
38968 if(!panel.background){
38969 this.setActivePanel(panel);
38970 if(this.config.initialSize && this.panels.getCount()==1){
38971 this.resizeTo(this.config.initialSize);
38974 this.fireEvent("paneladded", this, panel);
38979 * Returns true if the panel is in this region.
38980 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38981 * @return {Boolean}
38983 hasPanel : function(panel){
38984 if(typeof panel == "object"){ // must be panel obj
38985 panel = panel.getId();
38987 return this.getPanel(panel) ? true : false;
38991 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38992 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38993 * @param {Boolean} preservePanel Overrides the config preservePanel option
38994 * @return {Roo.ContentPanel} The panel that was removed
38996 remove : function(panel, preservePanel){
38997 panel = this.getPanel(panel);
39002 this.fireEvent("beforeremove", this, panel, e);
39003 if(e.cancel === true){
39006 var panelId = panel.getId();
39007 this.panels.removeKey(panelId);
39012 * Returns the panel specified or null if it's not in this region.
39013 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39014 * @return {Roo.ContentPanel}
39016 getPanel : function(id){
39017 if(typeof id == "object"){ // must be panel obj
39020 return this.panels.get(id);
39024 * Returns this regions position (north/south/east/west/center).
39027 getPosition: function(){
39028 return this.position;
39032 * Ext JS Library 1.1.1
39033 * Copyright(c) 2006-2007, Ext JS, LLC.
39035 * Originally Released Under LGPL - original licence link has changed is not relivant.
39038 * <script type="text/javascript">
39042 * @class Roo.bootstrap.layout.Region
39043 * @extends Roo.bootstrap.layout.Basic
39044 * This class represents a region in a layout manager.
39046 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39047 * @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})
39048 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39049 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39050 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39051 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39052 * @cfg {String} title The title for the region (overrides panel titles)
39053 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39054 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39055 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39056 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39057 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39058 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39059 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39060 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39061 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39062 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39064 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39065 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39066 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39067 * @cfg {Number} width For East/West panels
39068 * @cfg {Number} height For North/South panels
39069 * @cfg {Boolean} split To show the splitter
39070 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39072 * @cfg {string} cls Extra CSS classes to add to region
39074 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39075 * @cfg {string} region the region that it inhabits..
39078 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39079 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39081 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39082 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39083 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39085 Roo.bootstrap.layout.Region = function(config)
39087 this.applyConfig(config);
39089 var mgr = config.mgr;
39090 var pos = config.region;
39091 config.skipConfig = true;
39092 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39095 this.onRender(mgr.el);
39098 this.visible = true;
39099 this.collapsed = false;
39100 this.unrendered_panels = [];
39103 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39105 position: '', // set by wrapper (eg. north/south etc..)
39106 unrendered_panels : null, // unrendered panels.
39108 tabPosition : false,
39110 mgr: false, // points to 'Border'
39113 createBody : function(){
39114 /** This region's body element
39115 * @type Roo.Element */
39116 this.bodyEl = this.el.createChild({
39118 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39122 onRender: function(ctr, pos)
39124 var dh = Roo.DomHelper;
39125 /** This region's container element
39126 * @type Roo.Element */
39127 this.el = dh.append(ctr.dom, {
39129 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39131 /** This region's title element
39132 * @type Roo.Element */
39134 this.titleEl = dh.append(this.el.dom, {
39136 unselectable: "on",
39137 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39139 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39140 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39144 this.titleEl.enableDisplayMode();
39145 /** This region's title text element
39146 * @type HTMLElement */
39147 this.titleTextEl = this.titleEl.dom.firstChild;
39148 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39150 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39151 this.closeBtn.enableDisplayMode();
39152 this.closeBtn.on("click", this.closeClicked, this);
39153 this.closeBtn.hide();
39155 this.createBody(this.config);
39156 if(this.config.hideWhenEmpty){
39158 this.on("paneladded", this.validateVisibility, this);
39159 this.on("panelremoved", this.validateVisibility, this);
39161 if(this.autoScroll){
39162 this.bodyEl.setStyle("overflow", "auto");
39164 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39166 //if(c.titlebar !== false){
39167 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39168 this.titleEl.hide();
39170 this.titleEl.show();
39171 if(this.config.title){
39172 this.titleTextEl.innerHTML = this.config.title;
39176 if(this.config.collapsed){
39177 this.collapse(true);
39179 if(this.config.hidden){
39183 if (this.unrendered_panels && this.unrendered_panels.length) {
39184 for (var i =0;i< this.unrendered_panels.length; i++) {
39185 this.add(this.unrendered_panels[i]);
39187 this.unrendered_panels = null;
39193 applyConfig : function(c)
39196 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39197 var dh = Roo.DomHelper;
39198 if(c.titlebar !== false){
39199 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39200 this.collapseBtn.on("click", this.collapse, this);
39201 this.collapseBtn.enableDisplayMode();
39203 if(c.showPin === true || this.showPin){
39204 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39205 this.stickBtn.enableDisplayMode();
39206 this.stickBtn.on("click", this.expand, this);
39207 this.stickBtn.hide();
39212 /** This region's collapsed element
39213 * @type Roo.Element */
39216 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39217 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39220 if(c.floatable !== false){
39221 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39222 this.collapsedEl.on("click", this.collapseClick, this);
39225 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39226 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39227 id: "message", unselectable: "on", style:{"float":"left"}});
39228 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39230 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39231 this.expandBtn.on("click", this.expand, this);
39235 if(this.collapseBtn){
39236 this.collapseBtn.setVisible(c.collapsible == true);
39239 this.cmargins = c.cmargins || this.cmargins ||
39240 (this.position == "west" || this.position == "east" ?
39241 {top: 0, left: 2, right:2, bottom: 0} :
39242 {top: 2, left: 0, right:0, bottom: 2});
39244 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39247 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39249 this.autoScroll = c.autoScroll || false;
39254 this.duration = c.duration || .30;
39255 this.slideDuration = c.slideDuration || .45;
39260 * Returns true if this region is currently visible.
39261 * @return {Boolean}
39263 isVisible : function(){
39264 return this.visible;
39268 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39269 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39271 //setCollapsedTitle : function(title){
39272 // title = title || " ";
39273 // if(this.collapsedTitleTextEl){
39274 // this.collapsedTitleTextEl.innerHTML = title;
39278 getBox : function(){
39280 // if(!this.collapsed){
39281 b = this.el.getBox(false, true);
39283 // b = this.collapsedEl.getBox(false, true);
39288 getMargins : function(){
39289 return this.margins;
39290 //return this.collapsed ? this.cmargins : this.margins;
39293 highlight : function(){
39294 this.el.addClass("x-layout-panel-dragover");
39297 unhighlight : function(){
39298 this.el.removeClass("x-layout-panel-dragover");
39301 updateBox : function(box)
39303 if (!this.bodyEl) {
39304 return; // not rendered yet..
39308 if(!this.collapsed){
39309 this.el.dom.style.left = box.x + "px";
39310 this.el.dom.style.top = box.y + "px";
39311 this.updateBody(box.width, box.height);
39313 this.collapsedEl.dom.style.left = box.x + "px";
39314 this.collapsedEl.dom.style.top = box.y + "px";
39315 this.collapsedEl.setSize(box.width, box.height);
39318 this.tabs.autoSizeTabs();
39322 updateBody : function(w, h)
39325 this.el.setWidth(w);
39326 w -= this.el.getBorderWidth("rl");
39327 if(this.config.adjustments){
39328 w += this.config.adjustments[0];
39331 if(h !== null && h > 0){
39332 this.el.setHeight(h);
39333 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39334 h -= this.el.getBorderWidth("tb");
39335 if(this.config.adjustments){
39336 h += this.config.adjustments[1];
39338 this.bodyEl.setHeight(h);
39340 h = this.tabs.syncHeight(h);
39343 if(this.panelSize){
39344 w = w !== null ? w : this.panelSize.width;
39345 h = h !== null ? h : this.panelSize.height;
39347 if(this.activePanel){
39348 var el = this.activePanel.getEl();
39349 w = w !== null ? w : el.getWidth();
39350 h = h !== null ? h : el.getHeight();
39351 this.panelSize = {width: w, height: h};
39352 this.activePanel.setSize(w, h);
39354 if(Roo.isIE && this.tabs){
39355 this.tabs.el.repaint();
39360 * Returns the container element for this region.
39361 * @return {Roo.Element}
39363 getEl : function(){
39368 * Hides this region.
39371 //if(!this.collapsed){
39372 this.el.dom.style.left = "-2000px";
39375 // this.collapsedEl.dom.style.left = "-2000px";
39376 // this.collapsedEl.hide();
39378 this.visible = false;
39379 this.fireEvent("visibilitychange", this, false);
39383 * Shows this region if it was previously hidden.
39386 //if(!this.collapsed){
39389 // this.collapsedEl.show();
39391 this.visible = true;
39392 this.fireEvent("visibilitychange", this, true);
39395 closeClicked : function(){
39396 if(this.activePanel){
39397 this.remove(this.activePanel);
39401 collapseClick : function(e){
39403 e.stopPropagation();
39406 e.stopPropagation();
39412 * Collapses this region.
39413 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39416 collapse : function(skipAnim, skipCheck = false){
39417 if(this.collapsed) {
39421 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39423 this.collapsed = true;
39425 this.split.el.hide();
39427 if(this.config.animate && skipAnim !== true){
39428 this.fireEvent("invalidated", this);
39429 this.animateCollapse();
39431 this.el.setLocation(-20000,-20000);
39433 this.collapsedEl.show();
39434 this.fireEvent("collapsed", this);
39435 this.fireEvent("invalidated", this);
39441 animateCollapse : function(){
39446 * Expands this region if it was previously collapsed.
39447 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39448 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39451 expand : function(e, skipAnim){
39453 e.stopPropagation();
39455 if(!this.collapsed || this.el.hasActiveFx()) {
39459 this.afterSlideIn();
39462 this.collapsed = false;
39463 if(this.config.animate && skipAnim !== true){
39464 this.animateExpand();
39468 this.split.el.show();
39470 this.collapsedEl.setLocation(-2000,-2000);
39471 this.collapsedEl.hide();
39472 this.fireEvent("invalidated", this);
39473 this.fireEvent("expanded", this);
39477 animateExpand : function(){
39481 initTabs : function()
39483 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39485 var ts = new Roo.bootstrap.panel.Tabs({
39486 el: this.bodyEl.dom,
39488 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39489 disableTooltips: this.config.disableTabTips,
39490 toolbar : this.config.toolbar
39493 if(this.config.hideTabs){
39494 ts.stripWrap.setDisplayed(false);
39497 ts.resizeTabs = this.config.resizeTabs === true;
39498 ts.minTabWidth = this.config.minTabWidth || 40;
39499 ts.maxTabWidth = this.config.maxTabWidth || 250;
39500 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39501 ts.monitorResize = false;
39502 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39503 ts.bodyEl.addClass('roo-layout-tabs-body');
39504 this.panels.each(this.initPanelAsTab, this);
39507 initPanelAsTab : function(panel){
39508 var ti = this.tabs.addTab(
39512 this.config.closeOnTab && panel.isClosable(),
39515 if(panel.tabTip !== undefined){
39516 ti.setTooltip(panel.tabTip);
39518 ti.on("activate", function(){
39519 this.setActivePanel(panel);
39522 if(this.config.closeOnTab){
39523 ti.on("beforeclose", function(t, e){
39525 this.remove(panel);
39529 panel.tabItem = ti;
39534 updatePanelTitle : function(panel, title)
39536 if(this.activePanel == panel){
39537 this.updateTitle(title);
39540 var ti = this.tabs.getTab(panel.getEl().id);
39542 if(panel.tabTip !== undefined){
39543 ti.setTooltip(panel.tabTip);
39548 updateTitle : function(title){
39549 if(this.titleTextEl && !this.config.title){
39550 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39554 setActivePanel : function(panel)
39556 panel = this.getPanel(panel);
39557 if(this.activePanel && this.activePanel != panel){
39558 if(this.activePanel.setActiveState(false) === false){
39562 this.activePanel = panel;
39563 panel.setActiveState(true);
39564 if(this.panelSize){
39565 panel.setSize(this.panelSize.width, this.panelSize.height);
39568 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39570 this.updateTitle(panel.getTitle());
39572 this.fireEvent("invalidated", this);
39574 this.fireEvent("panelactivated", this, panel);
39578 * Shows the specified panel.
39579 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39580 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39582 showPanel : function(panel)
39584 panel = this.getPanel(panel);
39587 var tab = this.tabs.getTab(panel.getEl().id);
39588 if(tab.isHidden()){
39589 this.tabs.unhideTab(tab.id);
39593 this.setActivePanel(panel);
39600 * Get the active panel for this region.
39601 * @return {Roo.ContentPanel} The active panel or null
39603 getActivePanel : function(){
39604 return this.activePanel;
39607 validateVisibility : function(){
39608 if(this.panels.getCount() < 1){
39609 this.updateTitle(" ");
39610 this.closeBtn.hide();
39613 if(!this.isVisible()){
39620 * Adds the passed ContentPanel(s) to this region.
39621 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39622 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39624 add : function(panel)
39626 if(arguments.length > 1){
39627 for(var i = 0, len = arguments.length; i < len; i++) {
39628 this.add(arguments[i]);
39633 // if we have not been rendered yet, then we can not really do much of this..
39634 if (!this.bodyEl) {
39635 this.unrendered_panels.push(panel);
39642 if(this.hasPanel(panel)){
39643 this.showPanel(panel);
39646 panel.setRegion(this);
39647 this.panels.add(panel);
39648 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39649 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39650 // and hide them... ???
39651 this.bodyEl.dom.appendChild(panel.getEl().dom);
39652 if(panel.background !== true){
39653 this.setActivePanel(panel);
39655 this.fireEvent("paneladded", this, panel);
39662 this.initPanelAsTab(panel);
39666 if(panel.background !== true){
39667 this.tabs.activate(panel.getEl().id);
39669 this.fireEvent("paneladded", this, panel);
39674 * Hides the tab for the specified panel.
39675 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39677 hidePanel : function(panel){
39678 if(this.tabs && (panel = this.getPanel(panel))){
39679 this.tabs.hideTab(panel.getEl().id);
39684 * Unhides the tab for a previously hidden panel.
39685 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39687 unhidePanel : function(panel){
39688 if(this.tabs && (panel = this.getPanel(panel))){
39689 this.tabs.unhideTab(panel.getEl().id);
39693 clearPanels : function(){
39694 while(this.panels.getCount() > 0){
39695 this.remove(this.panels.first());
39700 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39701 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39702 * @param {Boolean} preservePanel Overrides the config preservePanel option
39703 * @return {Roo.ContentPanel} The panel that was removed
39705 remove : function(panel, preservePanel)
39707 panel = this.getPanel(panel);
39712 this.fireEvent("beforeremove", this, panel, e);
39713 if(e.cancel === true){
39716 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39717 var panelId = panel.getId();
39718 this.panels.removeKey(panelId);
39720 document.body.appendChild(panel.getEl().dom);
39723 this.tabs.removeTab(panel.getEl().id);
39724 }else if (!preservePanel){
39725 this.bodyEl.dom.removeChild(panel.getEl().dom);
39727 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39728 var p = this.panels.first();
39729 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39730 tempEl.appendChild(p.getEl().dom);
39731 this.bodyEl.update("");
39732 this.bodyEl.dom.appendChild(p.getEl().dom);
39734 this.updateTitle(p.getTitle());
39736 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39737 this.setActivePanel(p);
39739 panel.setRegion(null);
39740 if(this.activePanel == panel){
39741 this.activePanel = null;
39743 if(this.config.autoDestroy !== false && preservePanel !== true){
39744 try{panel.destroy();}catch(e){}
39746 this.fireEvent("panelremoved", this, panel);
39751 * Returns the TabPanel component used by this region
39752 * @return {Roo.TabPanel}
39754 getTabs : function(){
39758 createTool : function(parentEl, className){
39759 var btn = Roo.DomHelper.append(parentEl, {
39761 cls: "x-layout-tools-button",
39764 cls: "roo-layout-tools-button-inner " + className,
39768 btn.addClassOnOver("roo-layout-tools-button-over");
39773 * Ext JS Library 1.1.1
39774 * Copyright(c) 2006-2007, Ext JS, LLC.
39776 * Originally Released Under LGPL - original licence link has changed is not relivant.
39779 * <script type="text/javascript">
39785 * @class Roo.SplitLayoutRegion
39786 * @extends Roo.LayoutRegion
39787 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39789 Roo.bootstrap.layout.Split = function(config){
39790 this.cursor = config.cursor;
39791 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39794 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39796 splitTip : "Drag to resize.",
39797 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39798 useSplitTips : false,
39800 applyConfig : function(config){
39801 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39804 onRender : function(ctr,pos) {
39806 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39807 if(!this.config.split){
39812 var splitEl = Roo.DomHelper.append(ctr.dom, {
39814 id: this.el.id + "-split",
39815 cls: "roo-layout-split roo-layout-split-"+this.position,
39818 /** The SplitBar for this region
39819 * @type Roo.SplitBar */
39820 // does not exist yet...
39821 Roo.log([this.position, this.orientation]);
39823 this.split = new Roo.bootstrap.SplitBar({
39824 dragElement : splitEl,
39825 resizingElement: this.el,
39826 orientation : this.orientation
39829 this.split.on("moved", this.onSplitMove, this);
39830 this.split.useShim = this.config.useShim === true;
39831 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39832 if(this.useSplitTips){
39833 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39835 //if(config.collapsible){
39836 // this.split.el.on("dblclick", this.collapse, this);
39839 if(typeof this.config.minSize != "undefined"){
39840 this.split.minSize = this.config.minSize;
39842 if(typeof this.config.maxSize != "undefined"){
39843 this.split.maxSize = this.config.maxSize;
39845 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39846 this.hideSplitter();
39851 getHMaxSize : function(){
39852 var cmax = this.config.maxSize || 10000;
39853 var center = this.mgr.getRegion("center");
39854 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39857 getVMaxSize : function(){
39858 var cmax = this.config.maxSize || 10000;
39859 var center = this.mgr.getRegion("center");
39860 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39863 onSplitMove : function(split, newSize){
39864 this.fireEvent("resized", this, newSize);
39868 * Returns the {@link Roo.SplitBar} for this region.
39869 * @return {Roo.SplitBar}
39871 getSplitBar : function(){
39876 this.hideSplitter();
39877 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39880 hideSplitter : function(){
39882 this.split.el.setLocation(-2000,-2000);
39883 this.split.el.hide();
39889 this.split.el.show();
39891 Roo.bootstrap.layout.Split.superclass.show.call(this);
39894 beforeSlide: function(){
39895 if(Roo.isGecko){// firefox overflow auto bug workaround
39896 this.bodyEl.clip();
39898 this.tabs.bodyEl.clip();
39900 if(this.activePanel){
39901 this.activePanel.getEl().clip();
39903 if(this.activePanel.beforeSlide){
39904 this.activePanel.beforeSlide();
39910 afterSlide : function(){
39911 if(Roo.isGecko){// firefox overflow auto bug workaround
39912 this.bodyEl.unclip();
39914 this.tabs.bodyEl.unclip();
39916 if(this.activePanel){
39917 this.activePanel.getEl().unclip();
39918 if(this.activePanel.afterSlide){
39919 this.activePanel.afterSlide();
39925 initAutoHide : function(){
39926 if(this.autoHide !== false){
39927 if(!this.autoHideHd){
39928 var st = new Roo.util.DelayedTask(this.slideIn, this);
39929 this.autoHideHd = {
39930 "mouseout": function(e){
39931 if(!e.within(this.el, true)){
39935 "mouseover" : function(e){
39941 this.el.on(this.autoHideHd);
39945 clearAutoHide : function(){
39946 if(this.autoHide !== false){
39947 this.el.un("mouseout", this.autoHideHd.mouseout);
39948 this.el.un("mouseover", this.autoHideHd.mouseover);
39952 clearMonitor : function(){
39953 Roo.get(document).un("click", this.slideInIf, this);
39956 // these names are backwards but not changed for compat
39957 slideOut : function(){
39958 if(this.isSlid || this.el.hasActiveFx()){
39961 this.isSlid = true;
39962 if(this.collapseBtn){
39963 this.collapseBtn.hide();
39965 this.closeBtnState = this.closeBtn.getStyle('display');
39966 this.closeBtn.hide();
39968 this.stickBtn.show();
39971 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39972 this.beforeSlide();
39973 this.el.setStyle("z-index", 10001);
39974 this.el.slideIn(this.getSlideAnchor(), {
39975 callback: function(){
39977 this.initAutoHide();
39978 Roo.get(document).on("click", this.slideInIf, this);
39979 this.fireEvent("slideshow", this);
39986 afterSlideIn : function(){
39987 this.clearAutoHide();
39988 this.isSlid = false;
39989 this.clearMonitor();
39990 this.el.setStyle("z-index", "");
39991 if(this.collapseBtn){
39992 this.collapseBtn.show();
39994 this.closeBtn.setStyle('display', this.closeBtnState);
39996 this.stickBtn.hide();
39998 this.fireEvent("slidehide", this);
40001 slideIn : function(cb){
40002 if(!this.isSlid || this.el.hasActiveFx()){
40006 this.isSlid = false;
40007 this.beforeSlide();
40008 this.el.slideOut(this.getSlideAnchor(), {
40009 callback: function(){
40010 this.el.setLeftTop(-10000, -10000);
40012 this.afterSlideIn();
40020 slideInIf : function(e){
40021 if(!e.within(this.el)){
40026 animateCollapse : function(){
40027 this.beforeSlide();
40028 this.el.setStyle("z-index", 20000);
40029 var anchor = this.getSlideAnchor();
40030 this.el.slideOut(anchor, {
40031 callback : function(){
40032 this.el.setStyle("z-index", "");
40033 this.collapsedEl.slideIn(anchor, {duration:.3});
40035 this.el.setLocation(-10000,-10000);
40037 this.fireEvent("collapsed", this);
40044 animateExpand : function(){
40045 this.beforeSlide();
40046 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40047 this.el.setStyle("z-index", 20000);
40048 this.collapsedEl.hide({
40051 this.el.slideIn(this.getSlideAnchor(), {
40052 callback : function(){
40053 this.el.setStyle("z-index", "");
40056 this.split.el.show();
40058 this.fireEvent("invalidated", this);
40059 this.fireEvent("expanded", this);
40087 getAnchor : function(){
40088 return this.anchors[this.position];
40091 getCollapseAnchor : function(){
40092 return this.canchors[this.position];
40095 getSlideAnchor : function(){
40096 return this.sanchors[this.position];
40099 getAlignAdj : function(){
40100 var cm = this.cmargins;
40101 switch(this.position){
40117 getExpandAdj : function(){
40118 var c = this.collapsedEl, cm = this.cmargins;
40119 switch(this.position){
40121 return [-(cm.right+c.getWidth()+cm.left), 0];
40124 return [cm.right+c.getWidth()+cm.left, 0];
40127 return [0, -(cm.top+cm.bottom+c.getHeight())];
40130 return [0, cm.top+cm.bottom+c.getHeight()];
40136 * Ext JS Library 1.1.1
40137 * Copyright(c) 2006-2007, Ext JS, LLC.
40139 * Originally Released Under LGPL - original licence link has changed is not relivant.
40142 * <script type="text/javascript">
40145 * These classes are private internal classes
40147 Roo.bootstrap.layout.Center = function(config){
40148 config.region = "center";
40149 Roo.bootstrap.layout.Region.call(this, config);
40150 this.visible = true;
40151 this.minWidth = config.minWidth || 20;
40152 this.minHeight = config.minHeight || 20;
40155 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40157 // center panel can't be hidden
40161 // center panel can't be hidden
40164 getMinWidth: function(){
40165 return this.minWidth;
40168 getMinHeight: function(){
40169 return this.minHeight;
40183 Roo.bootstrap.layout.North = function(config)
40185 config.region = 'north';
40186 config.cursor = 'n-resize';
40188 Roo.bootstrap.layout.Split.call(this, config);
40192 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40193 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40194 this.split.el.addClass("roo-layout-split-v");
40196 //var size = config.initialSize || config.height;
40197 //if(this.el && typeof size != "undefined"){
40198 // this.el.setHeight(size);
40201 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40203 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40206 onRender : function(ctr, pos)
40208 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40209 var size = this.config.initialSize || this.config.height;
40210 if(this.el && typeof size != "undefined"){
40211 this.el.setHeight(size);
40216 getBox : function(){
40217 if(this.collapsed){
40218 return this.collapsedEl.getBox();
40220 var box = this.el.getBox();
40222 box.height += this.split.el.getHeight();
40227 updateBox : function(box){
40228 if(this.split && !this.collapsed){
40229 box.height -= this.split.el.getHeight();
40230 this.split.el.setLeft(box.x);
40231 this.split.el.setTop(box.y+box.height);
40232 this.split.el.setWidth(box.width);
40234 if(this.collapsed){
40235 this.updateBody(box.width, null);
40237 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40245 Roo.bootstrap.layout.South = function(config){
40246 config.region = 'south';
40247 config.cursor = 's-resize';
40248 Roo.bootstrap.layout.Split.call(this, config);
40250 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40251 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40252 this.split.el.addClass("roo-layout-split-v");
40257 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40258 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40260 onRender : function(ctr, pos)
40262 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40263 var size = this.config.initialSize || this.config.height;
40264 if(this.el && typeof size != "undefined"){
40265 this.el.setHeight(size);
40270 getBox : function(){
40271 if(this.collapsed){
40272 return this.collapsedEl.getBox();
40274 var box = this.el.getBox();
40276 var sh = this.split.el.getHeight();
40283 updateBox : function(box){
40284 if(this.split && !this.collapsed){
40285 var sh = this.split.el.getHeight();
40288 this.split.el.setLeft(box.x);
40289 this.split.el.setTop(box.y-sh);
40290 this.split.el.setWidth(box.width);
40292 if(this.collapsed){
40293 this.updateBody(box.width, null);
40295 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40299 Roo.bootstrap.layout.East = function(config){
40300 config.region = "east";
40301 config.cursor = "e-resize";
40302 Roo.bootstrap.layout.Split.call(this, config);
40304 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40305 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40306 this.split.el.addClass("roo-layout-split-h");
40310 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40311 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40313 onRender : function(ctr, pos)
40315 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40316 var size = this.config.initialSize || this.config.width;
40317 if(this.el && typeof size != "undefined"){
40318 this.el.setWidth(size);
40323 getBox : function(){
40324 if(this.collapsed){
40325 return this.collapsedEl.getBox();
40327 var box = this.el.getBox();
40329 var sw = this.split.el.getWidth();
40336 updateBox : function(box){
40337 if(this.split && !this.collapsed){
40338 var sw = this.split.el.getWidth();
40340 this.split.el.setLeft(box.x);
40341 this.split.el.setTop(box.y);
40342 this.split.el.setHeight(box.height);
40345 if(this.collapsed){
40346 this.updateBody(null, box.height);
40348 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40352 Roo.bootstrap.layout.West = function(config){
40353 config.region = "west";
40354 config.cursor = "w-resize";
40356 Roo.bootstrap.layout.Split.call(this, config);
40358 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40359 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40360 this.split.el.addClass("roo-layout-split-h");
40364 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40365 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40367 onRender: function(ctr, pos)
40369 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40370 var size = this.config.initialSize || this.config.width;
40371 if(typeof size != "undefined"){
40372 this.el.setWidth(size);
40376 getBox : function(){
40377 if(this.collapsed){
40378 return this.collapsedEl.getBox();
40380 var box = this.el.getBox();
40381 if (box.width == 0) {
40382 box.width = this.config.width; // kludge?
40385 box.width += this.split.el.getWidth();
40390 updateBox : function(box){
40391 if(this.split && !this.collapsed){
40392 var sw = this.split.el.getWidth();
40394 this.split.el.setLeft(box.x+box.width);
40395 this.split.el.setTop(box.y);
40396 this.split.el.setHeight(box.height);
40398 if(this.collapsed){
40399 this.updateBody(null, box.height);
40401 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40403 });Roo.namespace("Roo.bootstrap.panel");/*
40405 * Ext JS Library 1.1.1
40406 * Copyright(c) 2006-2007, Ext JS, LLC.
40408 * Originally Released Under LGPL - original licence link has changed is not relivant.
40411 * <script type="text/javascript">
40414 * @class Roo.ContentPanel
40415 * @extends Roo.util.Observable
40416 * A basic ContentPanel element.
40417 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40418 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40419 * @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
40420 * @cfg {Boolean} closable True if the panel can be closed/removed
40421 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40422 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40423 * @cfg {Toolbar} toolbar A toolbar for this panel
40424 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40425 * @cfg {String} title The title for this panel
40426 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40427 * @cfg {String} url Calls {@link #setUrl} with this value
40428 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40429 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40430 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40431 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40432 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40433 * @cfg {Boolean} badges render the badges
40434 * @cfg {String} cls extra classes to use
40435 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40438 * Create a new ContentPanel.
40439 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40440 * @param {String/Object} config A string to set only the title or a config object
40441 * @param {String} content (optional) Set the HTML content for this panel
40442 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40444 Roo.bootstrap.panel.Content = function( config){
40446 this.tpl = config.tpl || false;
40448 var el = config.el;
40449 var content = config.content;
40451 if(config.autoCreate){ // xtype is available if this is called from factory
40454 this.el = Roo.get(el);
40455 if(!this.el && config && config.autoCreate){
40456 if(typeof config.autoCreate == "object"){
40457 if(!config.autoCreate.id){
40458 config.autoCreate.id = config.id||el;
40460 this.el = Roo.DomHelper.append(document.body,
40461 config.autoCreate, true);
40465 cls: (config.cls || '') +
40466 (config.background ? ' bg-' + config.background : '') +
40467 " roo-layout-inactive-content",
40470 if (config.iframe) {
40474 style : 'border: 0px',
40475 src : 'about:blank'
40481 elcfg.html = config.html;
40485 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40486 if (config.iframe) {
40487 this.iframeEl = this.el.select('iframe',true).first();
40492 this.closable = false;
40493 this.loaded = false;
40494 this.active = false;
40497 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40499 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40501 this.wrapEl = this.el; //this.el.wrap();
40503 if (config.toolbar.items) {
40504 ti = config.toolbar.items ;
40505 delete config.toolbar.items ;
40509 this.toolbar.render(this.wrapEl, 'before');
40510 for(var i =0;i < ti.length;i++) {
40511 // Roo.log(['add child', items[i]]);
40512 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40514 this.toolbar.items = nitems;
40515 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40516 delete config.toolbar;
40520 // xtype created footer. - not sure if will work as we normally have to render first..
40521 if (this.footer && !this.footer.el && this.footer.xtype) {
40522 if (!this.wrapEl) {
40523 this.wrapEl = this.el.wrap();
40526 this.footer.container = this.wrapEl.createChild();
40528 this.footer = Roo.factory(this.footer, Roo);
40533 if(typeof config == "string"){
40534 this.title = config;
40536 Roo.apply(this, config);
40540 this.resizeEl = Roo.get(this.resizeEl, true);
40542 this.resizeEl = this.el;
40544 // handle view.xtype
40552 * Fires when this panel is activated.
40553 * @param {Roo.ContentPanel} this
40557 * @event deactivate
40558 * Fires when this panel is activated.
40559 * @param {Roo.ContentPanel} this
40561 "deactivate" : true,
40565 * Fires when this panel is resized if fitToFrame is true.
40566 * @param {Roo.ContentPanel} this
40567 * @param {Number} width The width after any component adjustments
40568 * @param {Number} height The height after any component adjustments
40574 * Fires when this tab is created
40575 * @param {Roo.ContentPanel} this
40581 * Fires when this content is scrolled
40582 * @param {Roo.ContentPanel} this
40583 * @param {Event} scrollEvent
40594 if(this.autoScroll && !this.iframe){
40595 this.resizeEl.setStyle("overflow", "auto");
40596 this.resizeEl.on('scroll', this.onScroll, this);
40598 // fix randome scrolling
40599 //this.el.on('scroll', function() {
40600 // Roo.log('fix random scolling');
40601 // this.scrollTo('top',0);
40604 content = content || this.content;
40606 this.setContent(content);
40608 if(config && config.url){
40609 this.setUrl(this.url, this.params, this.loadOnce);
40614 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40616 if (this.view && typeof(this.view.xtype) != 'undefined') {
40617 this.view.el = this.el.appendChild(document.createElement("div"));
40618 this.view = Roo.factory(this.view);
40619 this.view.render && this.view.render(false, '');
40623 this.fireEvent('render', this);
40626 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40636 /* Resize Element - use this to work out scroll etc. */
40639 setRegion : function(region){
40640 this.region = region;
40641 this.setActiveClass(region && !this.background);
40645 setActiveClass: function(state)
40648 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40649 this.el.setStyle('position','relative');
40651 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40652 this.el.setStyle('position', 'absolute');
40657 * Returns the toolbar for this Panel if one was configured.
40658 * @return {Roo.Toolbar}
40660 getToolbar : function(){
40661 return this.toolbar;
40664 setActiveState : function(active)
40666 this.active = active;
40667 this.setActiveClass(active);
40669 if(this.fireEvent("deactivate", this) === false){
40674 this.fireEvent("activate", this);
40678 * Updates this panel's element (not for iframe)
40679 * @param {String} content The new content
40680 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40682 setContent : function(content, loadScripts){
40687 this.el.update(content, loadScripts);
40690 ignoreResize : function(w, h){
40691 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40694 this.lastSize = {width: w, height: h};
40699 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40700 * @return {Roo.UpdateManager} The UpdateManager
40702 getUpdateManager : function(){
40706 return this.el.getUpdateManager();
40709 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40710 * Does not work with IFRAME contents
40711 * @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:
40714 url: "your-url.php",
40715 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40716 callback: yourFunction,
40717 scope: yourObject, //(optional scope)
40720 text: "Loading...",
40726 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40727 * 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.
40728 * @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}
40729 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40730 * @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.
40731 * @return {Roo.ContentPanel} this
40739 var um = this.el.getUpdateManager();
40740 um.update.apply(um, arguments);
40746 * 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.
40747 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40748 * @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)
40749 * @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)
40750 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40752 setUrl : function(url, params, loadOnce){
40754 this.iframeEl.dom.src = url;
40758 if(this.refreshDelegate){
40759 this.removeListener("activate", this.refreshDelegate);
40761 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40762 this.on("activate", this.refreshDelegate);
40763 return this.el.getUpdateManager();
40766 _handleRefresh : function(url, params, loadOnce){
40767 if(!loadOnce || !this.loaded){
40768 var updater = this.el.getUpdateManager();
40769 updater.update(url, params, this._setLoaded.createDelegate(this));
40773 _setLoaded : function(){
40774 this.loaded = true;
40778 * Returns this panel's id
40781 getId : function(){
40786 * Returns this panel's element - used by regiosn to add.
40787 * @return {Roo.Element}
40789 getEl : function(){
40790 return this.wrapEl || this.el;
40795 adjustForComponents : function(width, height)
40797 //Roo.log('adjustForComponents ');
40798 if(this.resizeEl != this.el){
40799 width -= this.el.getFrameWidth('lr');
40800 height -= this.el.getFrameWidth('tb');
40803 var te = this.toolbar.getEl();
40804 te.setWidth(width);
40805 height -= te.getHeight();
40808 var te = this.footer.getEl();
40809 te.setWidth(width);
40810 height -= te.getHeight();
40814 if(this.adjustments){
40815 width += this.adjustments[0];
40816 height += this.adjustments[1];
40818 return {"width": width, "height": height};
40821 setSize : function(width, height){
40822 if(this.fitToFrame && !this.ignoreResize(width, height)){
40823 if(this.fitContainer && this.resizeEl != this.el){
40824 this.el.setSize(width, height);
40826 var size = this.adjustForComponents(width, height);
40828 this.iframeEl.setSize(width,height);
40831 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40832 this.fireEvent('resize', this, size.width, size.height);
40839 * Returns this panel's title
40842 getTitle : function(){
40844 if (typeof(this.title) != 'object') {
40849 for (var k in this.title) {
40850 if (!this.title.hasOwnProperty(k)) {
40854 if (k.indexOf('-') >= 0) {
40855 var s = k.split('-');
40856 for (var i = 0; i<s.length; i++) {
40857 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40860 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40867 * Set this panel's title
40868 * @param {String} title
40870 setTitle : function(title){
40871 this.title = title;
40873 this.region.updatePanelTitle(this, title);
40878 * Returns true is this panel was configured to be closable
40879 * @return {Boolean}
40881 isClosable : function(){
40882 return this.closable;
40885 beforeSlide : function(){
40887 this.resizeEl.clip();
40890 afterSlide : function(){
40892 this.resizeEl.unclip();
40896 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40897 * Will fail silently if the {@link #setUrl} method has not been called.
40898 * This does not activate the panel, just updates its content.
40900 refresh : function(){
40901 if(this.refreshDelegate){
40902 this.loaded = false;
40903 this.refreshDelegate();
40908 * Destroys this panel
40910 destroy : function(){
40911 this.el.removeAllListeners();
40912 var tempEl = document.createElement("span");
40913 tempEl.appendChild(this.el.dom);
40914 tempEl.innerHTML = "";
40920 * form - if the content panel contains a form - this is a reference to it.
40921 * @type {Roo.form.Form}
40925 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40926 * This contains a reference to it.
40932 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40942 * @param {Object} cfg Xtype definition of item to add.
40946 getChildContainer: function () {
40947 return this.getEl();
40951 onScroll : function(e)
40953 this.fireEvent('scroll', this, e);
40958 var ret = new Roo.factory(cfg);
40963 if (cfg.xtype.match(/^Form$/)) {
40966 //if (this.footer) {
40967 // el = this.footer.container.insertSibling(false, 'before');
40969 el = this.el.createChild();
40972 this.form = new Roo.form.Form(cfg);
40975 if ( this.form.allItems.length) {
40976 this.form.render(el.dom);
40980 // should only have one of theses..
40981 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40982 // views.. should not be just added - used named prop 'view''
40984 cfg.el = this.el.appendChild(document.createElement("div"));
40987 var ret = new Roo.factory(cfg);
40989 ret.render && ret.render(false, ''); // render blank..
40999 * @class Roo.bootstrap.panel.Grid
41000 * @extends Roo.bootstrap.panel.Content
41002 * Create a new GridPanel.
41003 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41004 * @param {Object} config A the config object
41010 Roo.bootstrap.panel.Grid = function(config)
41014 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41015 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41017 config.el = this.wrapper;
41018 //this.el = this.wrapper;
41020 if (config.container) {
41021 // ctor'ed from a Border/panel.grid
41024 this.wrapper.setStyle("overflow", "hidden");
41025 this.wrapper.addClass('roo-grid-container');
41030 if(config.toolbar){
41031 var tool_el = this.wrapper.createChild();
41032 this.toolbar = Roo.factory(config.toolbar);
41034 if (config.toolbar.items) {
41035 ti = config.toolbar.items ;
41036 delete config.toolbar.items ;
41040 this.toolbar.render(tool_el);
41041 for(var i =0;i < ti.length;i++) {
41042 // Roo.log(['add child', items[i]]);
41043 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41045 this.toolbar.items = nitems;
41047 delete config.toolbar;
41050 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41051 config.grid.scrollBody = true;;
41052 config.grid.monitorWindowResize = false; // turn off autosizing
41053 config.grid.autoHeight = false;
41054 config.grid.autoWidth = false;
41056 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41058 if (config.background) {
41059 // render grid on panel activation (if panel background)
41060 this.on('activate', function(gp) {
41061 if (!gp.grid.rendered) {
41062 gp.grid.render(this.wrapper);
41063 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41068 this.grid.render(this.wrapper);
41069 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41072 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41073 // ??? needed ??? config.el = this.wrapper;
41078 // xtype created footer. - not sure if will work as we normally have to render first..
41079 if (this.footer && !this.footer.el && this.footer.xtype) {
41081 var ctr = this.grid.getView().getFooterPanel(true);
41082 this.footer.dataSource = this.grid.dataSource;
41083 this.footer = Roo.factory(this.footer, Roo);
41084 this.footer.render(ctr);
41094 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41095 getId : function(){
41096 return this.grid.id;
41100 * Returns the grid for this panel
41101 * @return {Roo.bootstrap.Table}
41103 getGrid : function(){
41107 setSize : function(width, height){
41108 if(!this.ignoreResize(width, height)){
41109 var grid = this.grid;
41110 var size = this.adjustForComponents(width, height);
41111 // tfoot is not a footer?
41114 var gridel = grid.getGridEl();
41115 gridel.setSize(size.width, size.height);
41117 var tbd = grid.getGridEl().select('tbody', true).first();
41118 var thd = grid.getGridEl().select('thead',true).first();
41119 var tbf= grid.getGridEl().select('tfoot', true).first();
41122 size.height -= tbf.getHeight();
41125 size.height -= thd.getHeight();
41128 tbd.setSize(size.width, size.height );
41129 // this is for the account management tab -seems to work there.
41130 var thd = grid.getGridEl().select('thead',true).first();
41132 // tbd.setSize(size.width, size.height - thd.getHeight());
41141 beforeSlide : function(){
41142 this.grid.getView().scroller.clip();
41145 afterSlide : function(){
41146 this.grid.getView().scroller.unclip();
41149 destroy : function(){
41150 this.grid.destroy();
41152 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41157 * @class Roo.bootstrap.panel.Nest
41158 * @extends Roo.bootstrap.panel.Content
41160 * Create a new Panel, that can contain a layout.Border.
41163 * @param {Roo.BorderLayout} layout The layout for this panel
41164 * @param {String/Object} config A string to set only the title or a config object
41166 Roo.bootstrap.panel.Nest = function(config)
41168 // construct with only one argument..
41169 /* FIXME - implement nicer consturctors
41170 if (layout.layout) {
41172 layout = config.layout;
41173 delete config.layout;
41175 if (layout.xtype && !layout.getEl) {
41176 // then layout needs constructing..
41177 layout = Roo.factory(layout, Roo);
41181 config.el = config.layout.getEl();
41183 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41185 config.layout.monitorWindowResize = false; // turn off autosizing
41186 this.layout = config.layout;
41187 this.layout.getEl().addClass("roo-layout-nested-layout");
41188 this.layout.parent = this;
41195 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41197 setSize : function(width, height){
41198 if(!this.ignoreResize(width, height)){
41199 var size = this.adjustForComponents(width, height);
41200 var el = this.layout.getEl();
41201 if (size.height < 1) {
41202 el.setWidth(size.width);
41204 el.setSize(size.width, size.height);
41206 var touch = el.dom.offsetWidth;
41207 this.layout.layout();
41208 // ie requires a double layout on the first pass
41209 if(Roo.isIE && !this.initialized){
41210 this.initialized = true;
41211 this.layout.layout();
41216 // activate all subpanels if not currently active..
41218 setActiveState : function(active){
41219 this.active = active;
41220 this.setActiveClass(active);
41223 this.fireEvent("deactivate", this);
41227 this.fireEvent("activate", this);
41228 // not sure if this should happen before or after..
41229 if (!this.layout) {
41230 return; // should not happen..
41233 for (var r in this.layout.regions) {
41234 reg = this.layout.getRegion(r);
41235 if (reg.getActivePanel()) {
41236 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41237 reg.setActivePanel(reg.getActivePanel());
41240 if (!reg.panels.length) {
41243 reg.showPanel(reg.getPanel(0));
41252 * Returns the nested BorderLayout for this panel
41253 * @return {Roo.BorderLayout}
41255 getLayout : function(){
41256 return this.layout;
41260 * Adds a xtype elements to the layout of the nested panel
41264 xtype : 'ContentPanel',
41271 xtype : 'NestedLayoutPanel',
41277 items : [ ... list of content panels or nested layout panels.. ]
41281 * @param {Object} cfg Xtype definition of item to add.
41283 addxtype : function(cfg) {
41284 return this.layout.addxtype(cfg);
41289 * Ext JS Library 1.1.1
41290 * Copyright(c) 2006-2007, Ext JS, LLC.
41292 * Originally Released Under LGPL - original licence link has changed is not relivant.
41295 * <script type="text/javascript">
41298 * @class Roo.TabPanel
41299 * @extends Roo.util.Observable
41300 * A lightweight tab container.
41304 // basic tabs 1, built from existing content
41305 var tabs = new Roo.TabPanel("tabs1");
41306 tabs.addTab("script", "View Script");
41307 tabs.addTab("markup", "View Markup");
41308 tabs.activate("script");
41310 // more advanced tabs, built from javascript
41311 var jtabs = new Roo.TabPanel("jtabs");
41312 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41314 // set up the UpdateManager
41315 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41316 var updater = tab2.getUpdateManager();
41317 updater.setDefaultUrl("ajax1.htm");
41318 tab2.on('activate', updater.refresh, updater, true);
41320 // Use setUrl for Ajax loading
41321 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41322 tab3.setUrl("ajax2.htm", null, true);
41325 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41328 jtabs.activate("jtabs-1");
41331 * Create a new TabPanel.
41332 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41333 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41335 Roo.bootstrap.panel.Tabs = function(config){
41337 * The container element for this TabPanel.
41338 * @type Roo.Element
41340 this.el = Roo.get(config.el);
41343 if(typeof config == "boolean"){
41344 this.tabPosition = config ? "bottom" : "top";
41346 Roo.apply(this, config);
41350 if(this.tabPosition == "bottom"){
41351 // if tabs are at the bottom = create the body first.
41352 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41353 this.el.addClass("roo-tabs-bottom");
41355 // next create the tabs holders
41357 if (this.tabPosition == "west"){
41359 var reg = this.region; // fake it..
41361 if (!reg.mgr.parent) {
41364 reg = reg.mgr.parent.region;
41366 Roo.log("got nest?");
41368 if (reg.mgr.getRegion('west')) {
41369 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41370 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41371 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41372 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41373 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41381 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41382 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41383 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41384 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41389 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41392 // finally - if tabs are at the top, then create the body last..
41393 if(this.tabPosition != "bottom"){
41394 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41395 * @type Roo.Element
41397 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41398 this.el.addClass("roo-tabs-top");
41402 this.bodyEl.setStyle("position", "relative");
41404 this.active = null;
41405 this.activateDelegate = this.activate.createDelegate(this);
41410 * Fires when the active tab changes
41411 * @param {Roo.TabPanel} this
41412 * @param {Roo.TabPanelItem} activePanel The new active tab
41416 * @event beforetabchange
41417 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41418 * @param {Roo.TabPanel} this
41419 * @param {Object} e Set cancel to true on this object to cancel the tab change
41420 * @param {Roo.TabPanelItem} tab The tab being changed to
41422 "beforetabchange" : true
41425 Roo.EventManager.onWindowResize(this.onResize, this);
41426 this.cpad = this.el.getPadding("lr");
41427 this.hiddenCount = 0;
41430 // toolbar on the tabbar support...
41431 if (this.toolbar) {
41432 alert("no toolbar support yet");
41433 this.toolbar = false;
41435 var tcfg = this.toolbar;
41436 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41437 this.toolbar = new Roo.Toolbar(tcfg);
41438 if (Roo.isSafari) {
41439 var tbl = tcfg.container.child('table', true);
41440 tbl.setAttribute('width', '100%');
41448 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41451 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41453 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41455 tabPosition : "top",
41457 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41459 currentTabWidth : 0,
41461 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41465 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41469 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41471 preferredTabWidth : 175,
41473 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41475 resizeTabs : false,
41477 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41479 monitorResize : true,
41481 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41483 toolbar : false, // set by caller..
41485 region : false, /// set by caller
41487 disableTooltips : true, // not used yet...
41490 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41491 * @param {String} id The id of the div to use <b>or create</b>
41492 * @param {String} text The text for the tab
41493 * @param {String} content (optional) Content to put in the TabPanelItem body
41494 * @param {Boolean} closable (optional) True to create a close icon on the tab
41495 * @return {Roo.TabPanelItem} The created TabPanelItem
41497 addTab : function(id, text, content, closable, tpl)
41499 var item = new Roo.bootstrap.panel.TabItem({
41503 closable : closable,
41506 this.addTabItem(item);
41508 item.setContent(content);
41514 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41515 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41516 * @return {Roo.TabPanelItem}
41518 getTab : function(id){
41519 return this.items[id];
41523 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41524 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41526 hideTab : function(id){
41527 var t = this.items[id];
41530 this.hiddenCount++;
41531 this.autoSizeTabs();
41536 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41537 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41539 unhideTab : function(id){
41540 var t = this.items[id];
41542 t.setHidden(false);
41543 this.hiddenCount--;
41544 this.autoSizeTabs();
41549 * Adds an existing {@link Roo.TabPanelItem}.
41550 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41552 addTabItem : function(item)
41554 this.items[item.id] = item;
41555 this.items.push(item);
41556 this.autoSizeTabs();
41557 // if(this.resizeTabs){
41558 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41559 // this.autoSizeTabs();
41561 // item.autoSize();
41566 * Removes a {@link Roo.TabPanelItem}.
41567 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41569 removeTab : function(id){
41570 var items = this.items;
41571 var tab = items[id];
41572 if(!tab) { return; }
41573 var index = items.indexOf(tab);
41574 if(this.active == tab && items.length > 1){
41575 var newTab = this.getNextAvailable(index);
41580 this.stripEl.dom.removeChild(tab.pnode.dom);
41581 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41582 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41584 items.splice(index, 1);
41585 delete this.items[tab.id];
41586 tab.fireEvent("close", tab);
41587 tab.purgeListeners();
41588 this.autoSizeTabs();
41591 getNextAvailable : function(start){
41592 var items = this.items;
41594 // look for a next tab that will slide over to
41595 // replace the one being removed
41596 while(index < items.length){
41597 var item = items[++index];
41598 if(item && !item.isHidden()){
41602 // if one isn't found select the previous tab (on the left)
41605 var item = items[--index];
41606 if(item && !item.isHidden()){
41614 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41615 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41617 disableTab : function(id){
41618 var tab = this.items[id];
41619 if(tab && this.active != tab){
41625 * Enables a {@link Roo.TabPanelItem} that is disabled.
41626 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41628 enableTab : function(id){
41629 var tab = this.items[id];
41634 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41635 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41636 * @return {Roo.TabPanelItem} The TabPanelItem.
41638 activate : function(id)
41640 //Roo.log('activite:' + id);
41642 var tab = this.items[id];
41646 if(tab == this.active || tab.disabled){
41650 this.fireEvent("beforetabchange", this, e, tab);
41651 if(e.cancel !== true && !tab.disabled){
41653 this.active.hide();
41655 this.active = this.items[id];
41656 this.active.show();
41657 this.fireEvent("tabchange", this, this.active);
41663 * Gets the active {@link Roo.TabPanelItem}.
41664 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41666 getActiveTab : function(){
41667 return this.active;
41671 * Updates the tab body element to fit the height of the container element
41672 * for overflow scrolling
41673 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41675 syncHeight : function(targetHeight){
41676 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41677 var bm = this.bodyEl.getMargins();
41678 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41679 this.bodyEl.setHeight(newHeight);
41683 onResize : function(){
41684 if(this.monitorResize){
41685 this.autoSizeTabs();
41690 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41692 beginUpdate : function(){
41693 this.updating = true;
41697 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41699 endUpdate : function(){
41700 this.updating = false;
41701 this.autoSizeTabs();
41705 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41707 autoSizeTabs : function()
41709 var count = this.items.length;
41710 var vcount = count - this.hiddenCount;
41713 this.stripEl.hide();
41715 this.stripEl.show();
41718 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41723 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41724 var availWidth = Math.floor(w / vcount);
41725 var b = this.stripBody;
41726 if(b.getWidth() > w){
41727 var tabs = this.items;
41728 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41729 if(availWidth < this.minTabWidth){
41730 /*if(!this.sleft){ // incomplete scrolling code
41731 this.createScrollButtons();
41734 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41737 if(this.currentTabWidth < this.preferredTabWidth){
41738 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41744 * Returns the number of tabs in this TabPanel.
41747 getCount : function(){
41748 return this.items.length;
41752 * Resizes all the tabs to the passed width
41753 * @param {Number} The new width
41755 setTabWidth : function(width){
41756 this.currentTabWidth = width;
41757 for(var i = 0, len = this.items.length; i < len; i++) {
41758 if(!this.items[i].isHidden()) {
41759 this.items[i].setWidth(width);
41765 * Destroys this TabPanel
41766 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41768 destroy : function(removeEl){
41769 Roo.EventManager.removeResizeListener(this.onResize, this);
41770 for(var i = 0, len = this.items.length; i < len; i++){
41771 this.items[i].purgeListeners();
41773 if(removeEl === true){
41774 this.el.update("");
41779 createStrip : function(container)
41781 var strip = document.createElement("nav");
41782 strip.className = Roo.bootstrap.version == 4 ?
41783 "navbar-light bg-light" :
41784 "navbar navbar-default"; //"x-tabs-wrap";
41785 container.appendChild(strip);
41789 createStripList : function(strip)
41791 // div wrapper for retard IE
41792 // returns the "tr" element.
41793 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41794 //'<div class="x-tabs-strip-wrap">'+
41795 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41796 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41797 return strip.firstChild; //.firstChild.firstChild.firstChild;
41799 createBody : function(container)
41801 var body = document.createElement("div");
41802 Roo.id(body, "tab-body");
41803 //Roo.fly(body).addClass("x-tabs-body");
41804 Roo.fly(body).addClass("tab-content");
41805 container.appendChild(body);
41808 createItemBody :function(bodyEl, id){
41809 var body = Roo.getDom(id);
41811 body = document.createElement("div");
41814 //Roo.fly(body).addClass("x-tabs-item-body");
41815 Roo.fly(body).addClass("tab-pane");
41816 bodyEl.insertBefore(body, bodyEl.firstChild);
41820 createStripElements : function(stripEl, text, closable, tpl)
41822 var td = document.createElement("li"); // was td..
41823 td.className = 'nav-item';
41825 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41828 stripEl.appendChild(td);
41830 td.className = "x-tabs-closable";
41831 if(!this.closeTpl){
41832 this.closeTpl = new Roo.Template(
41833 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41834 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41835 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41838 var el = this.closeTpl.overwrite(td, {"text": text});
41839 var close = el.getElementsByTagName("div")[0];
41840 var inner = el.getElementsByTagName("em")[0];
41841 return {"el": el, "close": close, "inner": inner};
41844 // not sure what this is..
41845 // if(!this.tabTpl){
41846 //this.tabTpl = new Roo.Template(
41847 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41848 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41850 // this.tabTpl = new Roo.Template(
41851 // '<a href="#">' +
41852 // '<span unselectable="on"' +
41853 // (this.disableTooltips ? '' : ' title="{text}"') +
41854 // ' >{text}</span></a>'
41860 var template = tpl || this.tabTpl || false;
41863 template = new Roo.Template(
41864 Roo.bootstrap.version == 4 ?
41866 '<a class="nav-link" href="#" unselectable="on"' +
41867 (this.disableTooltips ? '' : ' title="{text}"') +
41870 '<a class="nav-link" href="#">' +
41871 '<span unselectable="on"' +
41872 (this.disableTooltips ? '' : ' title="{text}"') +
41873 ' >{text}</span></a>'
41878 switch (typeof(template)) {
41882 template = new Roo.Template(template);
41888 var el = template.overwrite(td, {"text": text});
41890 var inner = el.getElementsByTagName("span")[0];
41892 return {"el": el, "inner": inner};
41900 * @class Roo.TabPanelItem
41901 * @extends Roo.util.Observable
41902 * Represents an individual item (tab plus body) in a TabPanel.
41903 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41904 * @param {String} id The id of this TabPanelItem
41905 * @param {String} text The text for the tab of this TabPanelItem
41906 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41908 Roo.bootstrap.panel.TabItem = function(config){
41910 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41911 * @type Roo.TabPanel
41913 this.tabPanel = config.panel;
41915 * The id for this TabPanelItem
41918 this.id = config.id;
41920 this.disabled = false;
41922 this.text = config.text;
41924 this.loaded = false;
41925 this.closable = config.closable;
41928 * The body element for this TabPanelItem.
41929 * @type Roo.Element
41931 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41932 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41933 this.bodyEl.setStyle("display", "block");
41934 this.bodyEl.setStyle("zoom", "1");
41935 //this.hideAction();
41937 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41939 this.el = Roo.get(els.el);
41940 this.inner = Roo.get(els.inner, true);
41941 this.textEl = Roo.bootstrap.version == 4 ?
41942 this.el : Roo.get(this.el.dom.firstChild, true);
41944 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41945 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41948 // this.el.on("mousedown", this.onTabMouseDown, this);
41949 this.el.on("click", this.onTabClick, this);
41951 if(config.closable){
41952 var c = Roo.get(els.close, true);
41953 c.dom.title = this.closeText;
41954 c.addClassOnOver("close-over");
41955 c.on("click", this.closeClick, this);
41961 * Fires when this tab becomes the active tab.
41962 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41963 * @param {Roo.TabPanelItem} this
41967 * @event beforeclose
41968 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41969 * @param {Roo.TabPanelItem} this
41970 * @param {Object} e Set cancel to true on this object to cancel the close.
41972 "beforeclose": true,
41975 * Fires when this tab is closed.
41976 * @param {Roo.TabPanelItem} this
41980 * @event deactivate
41981 * Fires when this tab is no longer the active tab.
41982 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41983 * @param {Roo.TabPanelItem} this
41985 "deactivate" : true
41987 this.hidden = false;
41989 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41992 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41994 purgeListeners : function(){
41995 Roo.util.Observable.prototype.purgeListeners.call(this);
41996 this.el.removeAllListeners();
41999 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42002 this.status_node.addClass("active");
42005 this.tabPanel.stripWrap.repaint();
42007 this.fireEvent("activate", this.tabPanel, this);
42011 * Returns true if this tab is the active tab.
42012 * @return {Boolean}
42014 isActive : function(){
42015 return this.tabPanel.getActiveTab() == this;
42019 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42022 this.status_node.removeClass("active");
42024 this.fireEvent("deactivate", this.tabPanel, this);
42027 hideAction : function(){
42028 this.bodyEl.hide();
42029 this.bodyEl.setStyle("position", "absolute");
42030 this.bodyEl.setLeft("-20000px");
42031 this.bodyEl.setTop("-20000px");
42034 showAction : function(){
42035 this.bodyEl.setStyle("position", "relative");
42036 this.bodyEl.setTop("");
42037 this.bodyEl.setLeft("");
42038 this.bodyEl.show();
42042 * Set the tooltip for the tab.
42043 * @param {String} tooltip The tab's tooltip
42045 setTooltip : function(text){
42046 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42047 this.textEl.dom.qtip = text;
42048 this.textEl.dom.removeAttribute('title');
42050 this.textEl.dom.title = text;
42054 onTabClick : function(e){
42055 e.preventDefault();
42056 this.tabPanel.activate(this.id);
42059 onTabMouseDown : function(e){
42060 e.preventDefault();
42061 this.tabPanel.activate(this.id);
42064 getWidth : function(){
42065 return this.inner.getWidth();
42068 setWidth : function(width){
42069 var iwidth = width - this.linode.getPadding("lr");
42070 this.inner.setWidth(iwidth);
42071 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42072 this.linode.setWidth(width);
42076 * Show or hide the tab
42077 * @param {Boolean} hidden True to hide or false to show.
42079 setHidden : function(hidden){
42080 this.hidden = hidden;
42081 this.linode.setStyle("display", hidden ? "none" : "");
42085 * Returns true if this tab is "hidden"
42086 * @return {Boolean}
42088 isHidden : function(){
42089 return this.hidden;
42093 * Returns the text for this tab
42096 getText : function(){
42100 autoSize : function(){
42101 //this.el.beginMeasure();
42102 this.textEl.setWidth(1);
42104 * #2804 [new] Tabs in Roojs
42105 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42107 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42108 //this.el.endMeasure();
42112 * Sets the text for the tab (Note: this also sets the tooltip text)
42113 * @param {String} text The tab's text and tooltip
42115 setText : function(text){
42117 this.textEl.update(text);
42118 this.setTooltip(text);
42119 //if(!this.tabPanel.resizeTabs){
42120 // this.autoSize();
42124 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42126 activate : function(){
42127 this.tabPanel.activate(this.id);
42131 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42133 disable : function(){
42134 if(this.tabPanel.active != this){
42135 this.disabled = true;
42136 this.status_node.addClass("disabled");
42141 * Enables this TabPanelItem if it was previously disabled.
42143 enable : function(){
42144 this.disabled = false;
42145 this.status_node.removeClass("disabled");
42149 * Sets the content for this TabPanelItem.
42150 * @param {String} content The content
42151 * @param {Boolean} loadScripts true to look for and load scripts
42153 setContent : function(content, loadScripts){
42154 this.bodyEl.update(content, loadScripts);
42158 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42159 * @return {Roo.UpdateManager} The UpdateManager
42161 getUpdateManager : function(){
42162 return this.bodyEl.getUpdateManager();
42166 * Set a URL to be used to load the content for this TabPanelItem.
42167 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42168 * @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)
42169 * @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)
42170 * @return {Roo.UpdateManager} The UpdateManager
42172 setUrl : function(url, params, loadOnce){
42173 if(this.refreshDelegate){
42174 this.un('activate', this.refreshDelegate);
42176 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42177 this.on("activate", this.refreshDelegate);
42178 return this.bodyEl.getUpdateManager();
42182 _handleRefresh : function(url, params, loadOnce){
42183 if(!loadOnce || !this.loaded){
42184 var updater = this.bodyEl.getUpdateManager();
42185 updater.update(url, params, this._setLoaded.createDelegate(this));
42190 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42191 * Will fail silently if the setUrl method has not been called.
42192 * This does not activate the panel, just updates its content.
42194 refresh : function(){
42195 if(this.refreshDelegate){
42196 this.loaded = false;
42197 this.refreshDelegate();
42202 _setLoaded : function(){
42203 this.loaded = true;
42207 closeClick : function(e){
42210 this.fireEvent("beforeclose", this, o);
42211 if(o.cancel !== true){
42212 this.tabPanel.removeTab(this.id);
42216 * The text displayed in the tooltip for the close icon.
42219 closeText : "Close this tab"
42222 * This script refer to:
42223 * Title: International Telephone Input
42224 * Author: Jack O'Connor
42225 * Code version: v12.1.12
42226 * Availability: https://github.com/jackocnr/intl-tel-input.git
42229 Roo.bootstrap.PhoneInputData = function() {
42232 "Afghanistan (افغانستان)",
42237 "Albania (Shqipëri)",
42242 "Algeria (الجزائر)",
42267 "Antigua and Barbuda",
42277 "Armenia (Հայաստան)",
42293 "Austria (Österreich)",
42298 "Azerbaijan (Azərbaycan)",
42308 "Bahrain (البحرين)",
42313 "Bangladesh (বাংলাদেশ)",
42323 "Belarus (Беларусь)",
42328 "Belgium (België)",
42358 "Bosnia and Herzegovina (Босна и Херцеговина)",
42373 "British Indian Ocean Territory",
42378 "British Virgin Islands",
42388 "Bulgaria (България)",
42398 "Burundi (Uburundi)",
42403 "Cambodia (កម្ពុជា)",
42408 "Cameroon (Cameroun)",
42417 ["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"]
42420 "Cape Verde (Kabu Verdi)",
42425 "Caribbean Netherlands",
42436 "Central African Republic (République centrafricaine)",
42456 "Christmas Island",
42462 "Cocos (Keeling) Islands",
42473 "Comoros (جزر القمر)",
42478 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42483 "Congo (Republic) (Congo-Brazzaville)",
42503 "Croatia (Hrvatska)",
42524 "Czech Republic (Česká republika)",
42529 "Denmark (Danmark)",
42544 "Dominican Republic (República Dominicana)",
42548 ["809", "829", "849"]
42566 "Equatorial Guinea (Guinea Ecuatorial)",
42586 "Falkland Islands (Islas Malvinas)",
42591 "Faroe Islands (Føroyar)",
42612 "French Guiana (Guyane française)",
42617 "French Polynesia (Polynésie française)",
42632 "Georgia (საქართველო)",
42637 "Germany (Deutschland)",
42657 "Greenland (Kalaallit Nunaat)",
42694 "Guinea-Bissau (Guiné Bissau)",
42719 "Hungary (Magyarország)",
42724 "Iceland (Ísland)",
42744 "Iraq (العراق)",
42760 "Israel (ישראל)",
42787 "Jordan (الأردن)",
42792 "Kazakhstan (Казахстан)",
42813 "Kuwait (الكويت)",
42818 "Kyrgyzstan (Кыргызстан)",
42828 "Latvia (Latvija)",
42833 "Lebanon (لبنان)",
42848 "Libya (ليبيا)",
42858 "Lithuania (Lietuva)",
42873 "Macedonia (FYROM) (Македонија)",
42878 "Madagascar (Madagasikara)",
42908 "Marshall Islands",
42918 "Mauritania (موريتانيا)",
42923 "Mauritius (Moris)",
42944 "Moldova (Republica Moldova)",
42954 "Mongolia (Монгол)",
42959 "Montenegro (Crna Gora)",
42969 "Morocco (المغرب)",
42975 "Mozambique (Moçambique)",
42980 "Myanmar (Burma) (မြန်မာ)",
42985 "Namibia (Namibië)",
43000 "Netherlands (Nederland)",
43005 "New Caledonia (Nouvelle-Calédonie)",
43040 "North Korea (조선 민주주의 인민 공화국)",
43045 "Northern Mariana Islands",
43061 "Pakistan (پاکستان)",
43071 "Palestine (فلسطين)",
43081 "Papua New Guinea",
43123 "Réunion (La Réunion)",
43129 "Romania (România)",
43145 "Saint Barthélemy",
43156 "Saint Kitts and Nevis",
43166 "Saint Martin (Saint-Martin (partie française))",
43172 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43177 "Saint Vincent and the Grenadines",
43192 "São Tomé and Príncipe (São Tomé e Príncipe)",
43197 "Saudi Arabia (المملكة العربية السعودية)",
43202 "Senegal (Sénégal)",
43232 "Slovakia (Slovensko)",
43237 "Slovenia (Slovenija)",
43247 "Somalia (Soomaaliya)",
43257 "South Korea (대한민국)",
43262 "South Sudan (جنوب السودان)",
43272 "Sri Lanka (ශ්රී ලංකාව)",
43277 "Sudan (السودان)",
43287 "Svalbard and Jan Mayen",
43298 "Sweden (Sverige)",
43303 "Switzerland (Schweiz)",
43308 "Syria (سوريا)",
43353 "Trinidad and Tobago",
43358 "Tunisia (تونس)",
43363 "Turkey (Türkiye)",
43373 "Turks and Caicos Islands",
43383 "U.S. Virgin Islands",
43393 "Ukraine (Україна)",
43398 "United Arab Emirates (الإمارات العربية المتحدة)",
43420 "Uzbekistan (Oʻzbekiston)",
43430 "Vatican City (Città del Vaticano)",
43441 "Vietnam (Việt Nam)",
43446 "Wallis and Futuna (Wallis-et-Futuna)",
43451 "Western Sahara (الصحراء الغربية)",
43457 "Yemen (اليمن)",
43481 * This script refer to:
43482 * Title: International Telephone Input
43483 * Author: Jack O'Connor
43484 * Code version: v12.1.12
43485 * Availability: https://github.com/jackocnr/intl-tel-input.git
43489 * @class Roo.bootstrap.PhoneInput
43490 * @extends Roo.bootstrap.TriggerField
43491 * An input with International dial-code selection
43493 * @cfg {String} defaultDialCode default '+852'
43494 * @cfg {Array} preferedCountries default []
43497 * Create a new PhoneInput.
43498 * @param {Object} config Configuration options
43501 Roo.bootstrap.PhoneInput = function(config) {
43502 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43505 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43507 listWidth: undefined,
43509 selectedClass: 'active',
43511 invalidClass : "has-warning",
43513 validClass: 'has-success',
43515 allowed: '0123456789',
43520 * @cfg {String} defaultDialCode The default dial code when initializing the input
43522 defaultDialCode: '+852',
43525 * @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
43527 preferedCountries: false,
43529 getAutoCreate : function()
43531 var data = Roo.bootstrap.PhoneInputData();
43532 var align = this.labelAlign || this.parentLabelAlign();
43535 this.allCountries = [];
43536 this.dialCodeMapping = [];
43538 for (var i = 0; i < data.length; i++) {
43540 this.allCountries[i] = {
43544 priority: c[3] || 0,
43545 areaCodes: c[4] || null
43547 this.dialCodeMapping[c[2]] = {
43550 priority: c[3] || 0,
43551 areaCodes: c[4] || null
43563 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43564 maxlength: this.max_length,
43565 cls : 'form-control tel-input',
43566 autocomplete: 'new-password'
43569 var hiddenInput = {
43572 cls: 'hidden-tel-input'
43576 hiddenInput.name = this.name;
43579 if (this.disabled) {
43580 input.disabled = true;
43583 var flag_container = {
43600 cls: this.hasFeedback ? 'has-feedback' : '',
43606 cls: 'dial-code-holder',
43613 cls: 'roo-select2-container input-group',
43620 if (this.fieldLabel.length) {
43623 tooltip: 'This field is required'
43629 cls: 'control-label',
43635 html: this.fieldLabel
43638 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43644 if(this.indicatorpos == 'right') {
43645 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43652 if(align == 'left') {
43660 if(this.labelWidth > 12){
43661 label.style = "width: " + this.labelWidth + 'px';
43663 if(this.labelWidth < 13 && this.labelmd == 0){
43664 this.labelmd = this.labelWidth;
43666 if(this.labellg > 0){
43667 label.cls += ' col-lg-' + this.labellg;
43668 input.cls += ' col-lg-' + (12 - this.labellg);
43670 if(this.labelmd > 0){
43671 label.cls += ' col-md-' + this.labelmd;
43672 container.cls += ' col-md-' + (12 - this.labelmd);
43674 if(this.labelsm > 0){
43675 label.cls += ' col-sm-' + this.labelsm;
43676 container.cls += ' col-sm-' + (12 - this.labelsm);
43678 if(this.labelxs > 0){
43679 label.cls += ' col-xs-' + this.labelxs;
43680 container.cls += ' col-xs-' + (12 - this.labelxs);
43690 var settings = this;
43692 ['xs','sm','md','lg'].map(function(size){
43693 if (settings[size]) {
43694 cfg.cls += ' col-' + size + '-' + settings[size];
43698 this.store = new Roo.data.Store({
43699 proxy : new Roo.data.MemoryProxy({}),
43700 reader : new Roo.data.JsonReader({
43711 'name' : 'dialCode',
43715 'name' : 'priority',
43719 'name' : 'areaCodes',
43726 if(!this.preferedCountries) {
43727 this.preferedCountries = [
43734 var p = this.preferedCountries.reverse();
43737 for (var i = 0; i < p.length; i++) {
43738 for (var j = 0; j < this.allCountries.length; j++) {
43739 if(this.allCountries[j].iso2 == p[i]) {
43740 var t = this.allCountries[j];
43741 this.allCountries.splice(j,1);
43742 this.allCountries.unshift(t);
43748 this.store.proxy.data = {
43750 data: this.allCountries
43756 initEvents : function()
43759 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43761 this.indicator = this.indicatorEl();
43762 this.flag = this.flagEl();
43763 this.dialCodeHolder = this.dialCodeHolderEl();
43765 this.trigger = this.el.select('div.flag-box',true).first();
43766 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43771 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43772 _this.list.setWidth(lw);
43775 this.list.on('mouseover', this.onViewOver, this);
43776 this.list.on('mousemove', this.onViewMove, this);
43777 this.inputEl().on("keyup", this.onKeyUp, this);
43778 this.inputEl().on("keypress", this.onKeyPress, this);
43780 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43782 this.view = new Roo.View(this.list, this.tpl, {
43783 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43786 this.view.on('click', this.onViewClick, this);
43787 this.setValue(this.defaultDialCode);
43790 onTriggerClick : function(e)
43792 Roo.log('trigger click');
43797 if(this.isExpanded()){
43799 this.hasFocus = false;
43801 this.store.load({});
43802 this.hasFocus = true;
43807 isExpanded : function()
43809 return this.list.isVisible();
43812 collapse : function()
43814 if(!this.isExpanded()){
43818 Roo.get(document).un('mousedown', this.collapseIf, this);
43819 Roo.get(document).un('mousewheel', this.collapseIf, this);
43820 this.fireEvent('collapse', this);
43824 expand : function()
43828 if(this.isExpanded() || !this.hasFocus){
43832 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43833 this.list.setWidth(lw);
43836 this.restrictHeight();
43838 Roo.get(document).on('mousedown', this.collapseIf, this);
43839 Roo.get(document).on('mousewheel', this.collapseIf, this);
43841 this.fireEvent('expand', this);
43844 restrictHeight : function()
43846 this.list.alignTo(this.inputEl(), this.listAlign);
43847 this.list.alignTo(this.inputEl(), this.listAlign);
43850 onViewOver : function(e, t)
43852 if(this.inKeyMode){
43855 var item = this.view.findItemFromChild(t);
43858 var index = this.view.indexOf(item);
43859 this.select(index, false);
43864 onViewClick : function(view, doFocus, el, e)
43866 var index = this.view.getSelectedIndexes()[0];
43868 var r = this.store.getAt(index);
43871 this.onSelect(r, index);
43873 if(doFocus !== false && !this.blockFocus){
43874 this.inputEl().focus();
43878 onViewMove : function(e, t)
43880 this.inKeyMode = false;
43883 select : function(index, scrollIntoView)
43885 this.selectedIndex = index;
43886 this.view.select(index);
43887 if(scrollIntoView !== false){
43888 var el = this.view.getNode(index);
43890 this.list.scrollChildIntoView(el, false);
43895 createList : function()
43897 this.list = Roo.get(document.body).createChild({
43899 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43900 style: 'display:none'
43903 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43906 collapseIf : function(e)
43908 var in_combo = e.within(this.el);
43909 var in_list = e.within(this.list);
43910 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43912 if (in_combo || in_list || is_list) {
43918 onSelect : function(record, index)
43920 if(this.fireEvent('beforeselect', this, record, index) !== false){
43922 this.setFlagClass(record.data.iso2);
43923 this.setDialCode(record.data.dialCode);
43924 this.hasFocus = false;
43926 this.fireEvent('select', this, record, index);
43930 flagEl : function()
43932 var flag = this.el.select('div.flag',true).first();
43939 dialCodeHolderEl : function()
43941 var d = this.el.select('input.dial-code-holder',true).first();
43948 setDialCode : function(v)
43950 this.dialCodeHolder.dom.value = '+'+v;
43953 setFlagClass : function(n)
43955 this.flag.dom.className = 'flag '+n;
43958 getValue : function()
43960 var v = this.inputEl().getValue();
43961 if(this.dialCodeHolder) {
43962 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43967 setValue : function(v)
43969 var d = this.getDialCode(v);
43971 //invalid dial code
43972 if(v.length == 0 || !d || d.length == 0) {
43974 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43975 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43981 this.setFlagClass(this.dialCodeMapping[d].iso2);
43982 this.setDialCode(d);
43983 this.inputEl().dom.value = v.replace('+'+d,'');
43984 this.hiddenEl().dom.value = this.getValue();
43989 getDialCode : function(v)
43993 if (v.length == 0) {
43994 return this.dialCodeHolder.dom.value;
43998 if (v.charAt(0) != "+") {
44001 var numericChars = "";
44002 for (var i = 1; i < v.length; i++) {
44003 var c = v.charAt(i);
44006 if (this.dialCodeMapping[numericChars]) {
44007 dialCode = v.substr(1, i);
44009 if (numericChars.length == 4) {
44019 this.setValue(this.defaultDialCode);
44023 hiddenEl : function()
44025 return this.el.select('input.hidden-tel-input',true).first();
44028 // after setting val
44029 onKeyUp : function(e){
44030 this.setValue(this.getValue());
44033 onKeyPress : function(e){
44034 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44041 * @class Roo.bootstrap.MoneyField
44042 * @extends Roo.bootstrap.ComboBox
44043 * Bootstrap MoneyField class
44046 * Create a new MoneyField.
44047 * @param {Object} config Configuration options
44050 Roo.bootstrap.MoneyField = function(config) {
44052 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44056 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44059 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44061 allowDecimals : true,
44063 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44065 decimalSeparator : ".",
44067 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44069 decimalPrecision : 0,
44071 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44073 allowNegative : true,
44075 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44079 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44081 minValue : Number.NEGATIVE_INFINITY,
44083 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44085 maxValue : Number.MAX_VALUE,
44087 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44089 minText : "The minimum value for this field is {0}",
44091 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44093 maxText : "The maximum value for this field is {0}",
44095 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44096 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44098 nanText : "{0} is not a valid number",
44100 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44104 * @cfg {String} defaults currency of the MoneyField
44105 * value should be in lkey
44107 defaultCurrency : false,
44109 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44111 thousandsDelimiter : false,
44113 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44124 getAutoCreate : function()
44126 var align = this.labelAlign || this.parentLabelAlign();
44138 cls : 'form-control roo-money-amount-input',
44139 autocomplete: 'new-password'
44142 var hiddenInput = {
44146 cls: 'hidden-number-input'
44149 if(this.max_length) {
44150 input.maxlength = this.max_length;
44154 hiddenInput.name = this.name;
44157 if (this.disabled) {
44158 input.disabled = true;
44161 var clg = 12 - this.inputlg;
44162 var cmd = 12 - this.inputmd;
44163 var csm = 12 - this.inputsm;
44164 var cxs = 12 - this.inputxs;
44168 cls : 'row roo-money-field',
44172 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44176 cls: 'roo-select2-container input-group',
44180 cls : 'form-control roo-money-currency-input',
44181 autocomplete: 'new-password',
44183 name : this.currencyName
44187 cls : 'input-group-addon',
44201 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44205 cls: this.hasFeedback ? 'has-feedback' : '',
44216 if (this.fieldLabel.length) {
44219 tooltip: 'This field is required'
44225 cls: 'control-label',
44231 html: this.fieldLabel
44234 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44240 if(this.indicatorpos == 'right') {
44241 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44248 if(align == 'left') {
44256 if(this.labelWidth > 12){
44257 label.style = "width: " + this.labelWidth + 'px';
44259 if(this.labelWidth < 13 && this.labelmd == 0){
44260 this.labelmd = this.labelWidth;
44262 if(this.labellg > 0){
44263 label.cls += ' col-lg-' + this.labellg;
44264 input.cls += ' col-lg-' + (12 - this.labellg);
44266 if(this.labelmd > 0){
44267 label.cls += ' col-md-' + this.labelmd;
44268 container.cls += ' col-md-' + (12 - this.labelmd);
44270 if(this.labelsm > 0){
44271 label.cls += ' col-sm-' + this.labelsm;
44272 container.cls += ' col-sm-' + (12 - this.labelsm);
44274 if(this.labelxs > 0){
44275 label.cls += ' col-xs-' + this.labelxs;
44276 container.cls += ' col-xs-' + (12 - this.labelxs);
44287 var settings = this;
44289 ['xs','sm','md','lg'].map(function(size){
44290 if (settings[size]) {
44291 cfg.cls += ' col-' + size + '-' + settings[size];
44298 initEvents : function()
44300 this.indicator = this.indicatorEl();
44302 this.initCurrencyEvent();
44304 this.initNumberEvent();
44307 initCurrencyEvent : function()
44310 throw "can not find store for combo";
44313 this.store = Roo.factory(this.store, Roo.data);
44314 this.store.parent = this;
44318 this.triggerEl = this.el.select('.input-group-addon', true).first();
44320 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44325 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44326 _this.list.setWidth(lw);
44329 this.list.on('mouseover', this.onViewOver, this);
44330 this.list.on('mousemove', this.onViewMove, this);
44331 this.list.on('scroll', this.onViewScroll, this);
44334 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44337 this.view = new Roo.View(this.list, this.tpl, {
44338 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44341 this.view.on('click', this.onViewClick, this);
44343 this.store.on('beforeload', this.onBeforeLoad, this);
44344 this.store.on('load', this.onLoad, this);
44345 this.store.on('loadexception', this.onLoadException, this);
44347 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44348 "up" : function(e){
44349 this.inKeyMode = true;
44353 "down" : function(e){
44354 if(!this.isExpanded()){
44355 this.onTriggerClick();
44357 this.inKeyMode = true;
44362 "enter" : function(e){
44365 if(this.fireEvent("specialkey", this, e)){
44366 this.onViewClick(false);
44372 "esc" : function(e){
44376 "tab" : function(e){
44379 if(this.fireEvent("specialkey", this, e)){
44380 this.onViewClick(false);
44388 doRelay : function(foo, bar, hname){
44389 if(hname == 'down' || this.scope.isExpanded()){
44390 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44398 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44402 initNumberEvent : function(e)
44404 this.inputEl().on("keydown" , this.fireKey, this);
44405 this.inputEl().on("focus", this.onFocus, this);
44406 this.inputEl().on("blur", this.onBlur, this);
44408 this.inputEl().relayEvent('keyup', this);
44410 if(this.indicator){
44411 this.indicator.addClass('invisible');
44414 this.originalValue = this.getValue();
44416 if(this.validationEvent == 'keyup'){
44417 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44418 this.inputEl().on('keyup', this.filterValidation, this);
44420 else if(this.validationEvent !== false){
44421 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44424 if(this.selectOnFocus){
44425 this.on("focus", this.preFocus, this);
44428 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44429 this.inputEl().on("keypress", this.filterKeys, this);
44431 this.inputEl().relayEvent('keypress', this);
44434 var allowed = "0123456789";
44436 if(this.allowDecimals){
44437 allowed += this.decimalSeparator;
44440 if(this.allowNegative){
44444 if(this.thousandsDelimiter) {
44448 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44450 var keyPress = function(e){
44452 var k = e.getKey();
44454 var c = e.getCharCode();
44457 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44458 allowed.indexOf(String.fromCharCode(c)) === -1
44464 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44468 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44473 this.inputEl().on("keypress", keyPress, this);
44477 onTriggerClick : function(e)
44484 this.loadNext = false;
44486 if(this.isExpanded()){
44491 this.hasFocus = true;
44493 if(this.triggerAction == 'all') {
44494 this.doQuery(this.allQuery, true);
44498 this.doQuery(this.getRawValue());
44501 getCurrency : function()
44503 var v = this.currencyEl().getValue();
44508 restrictHeight : function()
44510 this.list.alignTo(this.currencyEl(), this.listAlign);
44511 this.list.alignTo(this.currencyEl(), this.listAlign);
44514 onViewClick : function(view, doFocus, el, e)
44516 var index = this.view.getSelectedIndexes()[0];
44518 var r = this.store.getAt(index);
44521 this.onSelect(r, index);
44525 onSelect : function(record, index){
44527 if(this.fireEvent('beforeselect', this, record, index) !== false){
44529 this.setFromCurrencyData(index > -1 ? record.data : false);
44533 this.fireEvent('select', this, record, index);
44537 setFromCurrencyData : function(o)
44541 this.lastCurrency = o;
44543 if (this.currencyField) {
44544 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44546 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44549 this.lastSelectionText = currency;
44551 //setting default currency
44552 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44553 this.setCurrency(this.defaultCurrency);
44557 this.setCurrency(currency);
44560 setFromData : function(o)
44564 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44566 this.setFromCurrencyData(c);
44571 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44573 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44576 this.setValue(value);
44580 setCurrency : function(v)
44582 this.currencyValue = v;
44585 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44590 setValue : function(v)
44592 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44598 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44600 this.inputEl().dom.value = (v == '') ? '' :
44601 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44603 if(!this.allowZero && v === '0') {
44604 this.hiddenEl().dom.value = '';
44605 this.inputEl().dom.value = '';
44612 getRawValue : function()
44614 var v = this.inputEl().getValue();
44619 getValue : function()
44621 return this.fixPrecision(this.parseValue(this.getRawValue()));
44624 parseValue : function(value)
44626 if(this.thousandsDelimiter) {
44628 r = new RegExp(",", "g");
44629 value = value.replace(r, "");
44632 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44633 return isNaN(value) ? '' : value;
44637 fixPrecision : function(value)
44639 if(this.thousandsDelimiter) {
44641 r = new RegExp(",", "g");
44642 value = value.replace(r, "");
44645 var nan = isNaN(value);
44647 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44648 return nan ? '' : value;
44650 return parseFloat(value).toFixed(this.decimalPrecision);
44653 decimalPrecisionFcn : function(v)
44655 return Math.floor(v);
44658 validateValue : function(value)
44660 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44664 var num = this.parseValue(value);
44667 this.markInvalid(String.format(this.nanText, value));
44671 if(num < this.minValue){
44672 this.markInvalid(String.format(this.minText, this.minValue));
44676 if(num > this.maxValue){
44677 this.markInvalid(String.format(this.maxText, this.maxValue));
44684 validate : function()
44686 if(this.disabled || this.allowBlank){
44691 var currency = this.getCurrency();
44693 if(this.validateValue(this.getRawValue()) && currency.length){
44698 this.markInvalid();
44702 getName: function()
44707 beforeBlur : function()
44713 var v = this.parseValue(this.getRawValue());
44720 onBlur : function()
44724 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44725 //this.el.removeClass(this.focusClass);
44728 this.hasFocus = false;
44730 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44734 var v = this.getValue();
44736 if(String(v) !== String(this.startValue)){
44737 this.fireEvent('change', this, v, this.startValue);
44740 this.fireEvent("blur", this);
44743 inputEl : function()
44745 return this.el.select('.roo-money-amount-input', true).first();
44748 currencyEl : function()
44750 return this.el.select('.roo-money-currency-input', true).first();
44753 hiddenEl : function()
44755 return this.el.select('input.hidden-number-input',true).first();
44759 * @class Roo.bootstrap.BezierSignature
44760 * @extends Roo.bootstrap.Component
44761 * Bootstrap BezierSignature class
44762 * This script refer to:
44763 * Title: Signature Pad
44765 * Availability: https://github.com/szimek/signature_pad
44768 * Create a new BezierSignature
44769 * @param {Object} config The config object
44772 Roo.bootstrap.BezierSignature = function(config){
44773 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44779 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44786 mouse_btn_down: true,
44789 * @cfg {int} canvas height
44791 canvas_height: '200px',
44794 * @cfg {float|function} Radius of a single dot.
44799 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44804 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44809 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44814 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44819 * @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.
44821 bg_color: 'rgba(0, 0, 0, 0)',
44824 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44826 dot_color: 'black',
44829 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44831 velocity_filter_weight: 0.7,
44834 * @cfg {function} Callback when stroke begin.
44839 * @cfg {function} Callback when stroke end.
44843 getAutoCreate : function()
44845 var cls = 'roo-signature column';
44848 cls += ' ' + this.cls;
44858 for(var i = 0; i < col_sizes.length; i++) {
44859 if(this[col_sizes[i]]) {
44860 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44870 cls: 'roo-signature-body',
44874 cls: 'roo-signature-body-canvas',
44875 height: this.canvas_height,
44876 width: this.canvas_width
44883 style: 'display: none'
44891 initEvents: function()
44893 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44895 var canvas = this.canvasEl();
44897 // mouse && touch event swapping...
44898 canvas.dom.style.touchAction = 'none';
44899 canvas.dom.style.msTouchAction = 'none';
44901 this.mouse_btn_down = false;
44902 canvas.on('mousedown', this._handleMouseDown, this);
44903 canvas.on('mousemove', this._handleMouseMove, this);
44904 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44906 if (window.PointerEvent) {
44907 canvas.on('pointerdown', this._handleMouseDown, this);
44908 canvas.on('pointermove', this._handleMouseMove, this);
44909 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44912 if ('ontouchstart' in window) {
44913 canvas.on('touchstart', this._handleTouchStart, this);
44914 canvas.on('touchmove', this._handleTouchMove, this);
44915 canvas.on('touchend', this._handleTouchEnd, this);
44918 Roo.EventManager.onWindowResize(this.resize, this, true);
44920 // file input event
44921 this.fileEl().on('change', this.uploadImage, this);
44928 resize: function(){
44930 var canvas = this.canvasEl().dom;
44931 var ctx = this.canvasElCtx();
44932 var img_data = false;
44934 if(canvas.width > 0) {
44935 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44937 // setting canvas width will clean img data
44940 var style = window.getComputedStyle ?
44941 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44943 var padding_left = parseInt(style.paddingLeft) || 0;
44944 var padding_right = parseInt(style.paddingRight) || 0;
44946 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44949 ctx.putImageData(img_data, 0, 0);
44953 _handleMouseDown: function(e)
44955 if (e.browserEvent.which === 1) {
44956 this.mouse_btn_down = true;
44957 this.strokeBegin(e);
44961 _handleMouseMove: function (e)
44963 if (this.mouse_btn_down) {
44964 this.strokeMoveUpdate(e);
44968 _handleMouseUp: function (e)
44970 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44971 this.mouse_btn_down = false;
44976 _handleTouchStart: function (e) {
44978 e.preventDefault();
44979 if (e.browserEvent.targetTouches.length === 1) {
44980 // var touch = e.browserEvent.changedTouches[0];
44981 // this.strokeBegin(touch);
44983 this.strokeBegin(e); // assume e catching the correct xy...
44987 _handleTouchMove: function (e) {
44988 e.preventDefault();
44989 // var touch = event.targetTouches[0];
44990 // _this._strokeMoveUpdate(touch);
44991 this.strokeMoveUpdate(e);
44994 _handleTouchEnd: function (e) {
44995 var wasCanvasTouched = e.target === this.canvasEl().dom;
44996 if (wasCanvasTouched) {
44997 e.preventDefault();
44998 // var touch = event.changedTouches[0];
44999 // _this._strokeEnd(touch);
45004 reset: function () {
45005 this._lastPoints = [];
45006 this._lastVelocity = 0;
45007 this._lastWidth = (this.min_width + this.max_width) / 2;
45008 this.canvasElCtx().fillStyle = this.dot_color;
45011 strokeMoveUpdate: function(e)
45013 this.strokeUpdate(e);
45015 if (this.throttle) {
45016 this.throttleStroke(this.strokeUpdate, this.throttle);
45019 this.strokeUpdate(e);
45023 strokeBegin: function(e)
45025 var newPointGroup = {
45026 color: this.dot_color,
45030 if (typeof this.onBegin === 'function') {
45034 this.curve_data.push(newPointGroup);
45036 this.strokeUpdate(e);
45039 strokeUpdate: function(e)
45041 var rect = this.canvasEl().dom.getBoundingClientRect();
45042 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45043 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45044 var lastPoints = lastPointGroup.points;
45045 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45046 var isLastPointTooClose = lastPoint
45047 ? point.distanceTo(lastPoint) <= this.min_distance
45049 var color = lastPointGroup.color;
45050 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45051 var curve = this.addPoint(point);
45053 this.drawDot({color: color, point: point});
45056 this.drawCurve({color: color, curve: curve});
45066 strokeEnd: function(e)
45068 this.strokeUpdate(e);
45069 if (typeof this.onEnd === 'function') {
45074 addPoint: function (point) {
45075 var _lastPoints = this._lastPoints;
45076 _lastPoints.push(point);
45077 if (_lastPoints.length > 2) {
45078 if (_lastPoints.length === 3) {
45079 _lastPoints.unshift(_lastPoints[0]);
45081 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45082 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45083 _lastPoints.shift();
45089 calculateCurveWidths: function (startPoint, endPoint) {
45090 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45091 (1 - this.velocity_filter_weight) * this._lastVelocity;
45093 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45096 start: this._lastWidth
45099 this._lastVelocity = velocity;
45100 this._lastWidth = newWidth;
45104 drawDot: function (_a) {
45105 var color = _a.color, point = _a.point;
45106 var ctx = this.canvasElCtx();
45107 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45109 this.drawCurveSegment(point.x, point.y, width);
45111 ctx.fillStyle = color;
45115 drawCurve: function (_a) {
45116 var color = _a.color, curve = _a.curve;
45117 var ctx = this.canvasElCtx();
45118 var widthDelta = curve.endWidth - curve.startWidth;
45119 var drawSteps = Math.floor(curve.length()) * 2;
45121 ctx.fillStyle = color;
45122 for (var i = 0; i < drawSteps; i += 1) {
45123 var t = i / drawSteps;
45129 var x = uuu * curve.startPoint.x;
45130 x += 3 * uu * t * curve.control1.x;
45131 x += 3 * u * tt * curve.control2.x;
45132 x += ttt * curve.endPoint.x;
45133 var y = uuu * curve.startPoint.y;
45134 y += 3 * uu * t * curve.control1.y;
45135 y += 3 * u * tt * curve.control2.y;
45136 y += ttt * curve.endPoint.y;
45137 var width = curve.startWidth + ttt * widthDelta;
45138 this.drawCurveSegment(x, y, width);
45144 drawCurveSegment: function (x, y, width) {
45145 var ctx = this.canvasElCtx();
45147 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45148 this.is_empty = false;
45153 var ctx = this.canvasElCtx();
45154 var canvas = this.canvasEl().dom;
45155 ctx.fillStyle = this.bg_color;
45156 ctx.clearRect(0, 0, canvas.width, canvas.height);
45157 ctx.fillRect(0, 0, canvas.width, canvas.height);
45158 this.curve_data = [];
45160 this.is_empty = true;
45165 return this.el.select('input',true).first();
45168 canvasEl: function()
45170 return this.el.select('canvas',true).first();
45173 canvasElCtx: function()
45175 return this.el.select('canvas',true).first().dom.getContext('2d');
45178 getImage: function(type)
45180 if(this.is_empty) {
45185 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45188 drawFromImage: function(img_src)
45190 var img = new Image();
45192 img.onload = function(){
45193 this.canvasElCtx().drawImage(img, 0, 0);
45198 this.is_empty = false;
45201 selectImage: function()
45203 this.fileEl().dom.click();
45206 uploadImage: function(e)
45208 var reader = new FileReader();
45210 reader.onload = function(e){
45211 var img = new Image();
45212 img.onload = function(){
45214 this.canvasElCtx().drawImage(img, 0, 0);
45216 img.src = e.target.result;
45219 reader.readAsDataURL(e.target.files[0]);
45222 // Bezier Point Constructor
45223 Point: (function () {
45224 function Point(x, y, time) {
45227 this.time = time || Date.now();
45229 Point.prototype.distanceTo = function (start) {
45230 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45232 Point.prototype.equals = function (other) {
45233 return this.x === other.x && this.y === other.y && this.time === other.time;
45235 Point.prototype.velocityFrom = function (start) {
45236 return this.time !== start.time
45237 ? this.distanceTo(start) / (this.time - start.time)
45244 // Bezier Constructor
45245 Bezier: (function () {
45246 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45247 this.startPoint = startPoint;
45248 this.control2 = control2;
45249 this.control1 = control1;
45250 this.endPoint = endPoint;
45251 this.startWidth = startWidth;
45252 this.endWidth = endWidth;
45254 Bezier.fromPoints = function (points, widths, scope) {
45255 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45256 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45257 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45259 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45260 var dx1 = s1.x - s2.x;
45261 var dy1 = s1.y - s2.y;
45262 var dx2 = s2.x - s3.x;
45263 var dy2 = s2.y - s3.y;
45264 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45265 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45266 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45267 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45268 var dxm = m1.x - m2.x;
45269 var dym = m1.y - m2.y;
45270 var k = l2 / (l1 + l2);
45271 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45272 var tx = s2.x - cm.x;
45273 var ty = s2.y - cm.y;
45275 c1: new scope.Point(m1.x + tx, m1.y + ty),
45276 c2: new scope.Point(m2.x + tx, m2.y + ty)
45279 Bezier.prototype.length = function () {
45284 for (var i = 0; i <= steps; i += 1) {
45286 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45287 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45289 var xdiff = cx - px;
45290 var ydiff = cy - py;
45291 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45298 Bezier.prototype.point = function (t, start, c1, c2, end) {
45299 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45300 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45301 + (3.0 * c2 * (1.0 - t) * t * t)
45302 + (end * t * t * t);
45307 throttleStroke: function(fn, wait) {
45308 if (wait === void 0) { wait = 250; }
45310 var timeout = null;
45314 var later = function () {
45315 previous = Date.now();
45317 result = fn.apply(storedContext, storedArgs);
45319 storedContext = null;
45323 return function wrapper() {
45325 for (var _i = 0; _i < arguments.length; _i++) {
45326 args[_i] = arguments[_i];
45328 var now = Date.now();
45329 var remaining = wait - (now - previous);
45330 storedContext = this;
45332 if (remaining <= 0 || remaining > wait) {
45334 clearTimeout(timeout);
45338 result = fn.apply(storedContext, storedArgs);
45340 storedContext = null;
45344 else if (!timeout) {
45345 timeout = window.setTimeout(later, remaining);