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">
7312 * @class Roo.grid.AbstractSelectionModel
7313 * @extends Roo.util.Observable
7314 * Abstract base class for grid SelectionModels. It provides the interface that should be
7315 * implemented by descendant classes. This class should not be directly instantiated.
7318 Roo.grid.AbstractSelectionModel = function(){
7319 this.locked = false;
7320 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7324 /** @ignore Called by the grid automatically. Do not call directly. */
7325 init : function(grid){
7331 * Locks the selections.
7338 * Unlocks the selections.
7340 unlock : function(){
7341 this.locked = false;
7345 * Returns true if the selections are locked.
7348 isLocked : function(){
7353 * Ext JS Library 1.1.1
7354 * Copyright(c) 2006-2007, Ext JS, LLC.
7356 * Originally Released Under LGPL - original licence link has changed is not relivant.
7359 * <script type="text/javascript">
7362 * @extends Roo.grid.AbstractSelectionModel
7363 * @class Roo.grid.RowSelectionModel
7364 * The default SelectionModel used by {@link Roo.grid.Grid}.
7365 * It supports multiple selections and keyboard selection/navigation.
7367 * @param {Object} config
7369 Roo.grid.RowSelectionModel = function(config){
7370 Roo.apply(this, config);
7371 this.selections = new Roo.util.MixedCollection(false, function(o){
7376 this.lastActive = false;
7380 * @event selectionchange
7381 * Fires when the selection changes
7382 * @param {SelectionModel} this
7384 "selectionchange" : true,
7386 * @event afterselectionchange
7387 * Fires after the selection changes (eg. by key press or clicking)
7388 * @param {SelectionModel} this
7390 "afterselectionchange" : true,
7392 * @event beforerowselect
7393 * Fires when a row is selected being selected, return false to cancel.
7394 * @param {SelectionModel} this
7395 * @param {Number} rowIndex The selected index
7396 * @param {Boolean} keepExisting False if other selections will be cleared
7398 "beforerowselect" : true,
7401 * Fires when a row is selected.
7402 * @param {SelectionModel} this
7403 * @param {Number} rowIndex The selected index
7404 * @param {Roo.data.Record} r The record
7408 * @event rowdeselect
7409 * Fires when a row is deselected.
7410 * @param {SelectionModel} this
7411 * @param {Number} rowIndex The selected index
7413 "rowdeselect" : true
7415 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416 this.locked = false;
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7421 * @cfg {Boolean} singleSelect
7422 * True to allow selection of only one row at a time (defaults to false)
7424 singleSelect : false,
7427 initEvents : function(){
7429 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430 this.grid.on("mousedown", this.handleMouseDown, this);
7431 }else{ // allow click to work like normal
7432 this.grid.on("rowclick", this.handleDragableRowClick, this);
7434 // bootstrap does not have a view..
7435 var view = this.grid.view ? this.grid.view : this.grid;
7436 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7439 this.selectPrevious(e.shiftKey);
7440 }else if(this.last !== false && this.lastActive !== false){
7441 var last = this.last;
7442 this.selectRange(this.last, this.lastActive-1);
7443 view.focusRow(this.lastActive);
7448 this.selectFirstRow();
7450 this.fireEvent("afterselectionchange", this);
7452 "down" : function(e){
7454 this.selectNext(e.shiftKey);
7455 }else if(this.last !== false && this.lastActive !== false){
7456 var last = this.last;
7457 this.selectRange(this.last, this.lastActive+1);
7458 view.focusRow(this.lastActive);
7463 this.selectFirstRow();
7465 this.fireEvent("afterselectionchange", this);
7471 view.on("refresh", this.onRefresh, this);
7472 view.on("rowupdated", this.onRowUpdated, this);
7473 view.on("rowremoved", this.onRemove, this);
7477 onRefresh : function(){
7478 var ds = this.grid.ds, i, v = this.grid.view;
7479 var s = this.selections;
7481 if((i = ds.indexOfId(r.id)) != -1){
7483 s.add(ds.getAt(i)); // updating the selection relate data
7491 onRemove : function(v, index, r){
7492 this.selections.remove(r);
7496 onRowUpdated : function(v, index, r){
7497 if(this.isSelected(r)){
7498 v.onRowSelect(index);
7504 * @param {Array} records The records to select
7505 * @param {Boolean} keepExisting (optional) True to keep existing selections
7507 selectRecords : function(records, keepExisting){
7509 this.clearSelections();
7511 var ds = this.grid.ds;
7512 for(var i = 0, len = records.length; i < len; i++){
7513 this.selectRow(ds.indexOf(records[i]), true);
7518 * Gets the number of selected rows.
7521 getCount : function(){
7522 return this.selections.length;
7526 * Selects the first row in the grid.
7528 selectFirstRow : function(){
7533 * Select the last row.
7534 * @param {Boolean} keepExisting (optional) True to keep existing selections
7536 selectLastRow : function(keepExisting){
7537 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7541 * Selects the row immediately following the last selected row.
7542 * @param {Boolean} keepExisting (optional) True to keep existing selections
7544 selectNext : function(keepExisting){
7545 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7546 this.selectRow(this.last+1, keepExisting);
7547 var view = this.grid.view ? this.grid.view : this.grid;
7548 view.focusRow(this.last);
7553 * Selects the row that precedes the last selected row.
7554 * @param {Boolean} keepExisting (optional) True to keep existing selections
7556 selectPrevious : function(keepExisting){
7558 this.selectRow(this.last-1, keepExisting);
7559 var view = this.grid.view ? this.grid.view : this.grid;
7560 view.focusRow(this.last);
7565 * Returns the selected records
7566 * @return {Array} Array of selected records
7568 getSelections : function(){
7569 return [].concat(this.selections.items);
7573 * Returns the first selected record.
7576 getSelected : function(){
7577 return this.selections.itemAt(0);
7582 * Clears all selections.
7584 clearSelections : function(fast){
7589 var ds = this.grid.ds;
7590 var s = this.selections;
7592 this.deselectRow(ds.indexOfId(r.id));
7596 this.selections.clear();
7605 selectAll : function(){
7609 this.selections.clear();
7610 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7611 this.selectRow(i, true);
7616 * Returns True if there is a selection.
7619 hasSelection : function(){
7620 return this.selections.length > 0;
7624 * Returns True if the specified row is selected.
7625 * @param {Number/Record} record The record or index of the record to check
7628 isSelected : function(index){
7629 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7630 return (r && this.selections.key(r.id) ? true : false);
7634 * Returns True if the specified record id is selected.
7635 * @param {String} id The id of record to check
7638 isIdSelected : function(id){
7639 return (this.selections.key(id) ? true : false);
7643 handleMouseDown : function(e, t)
7645 var view = this.grid.view ? this.grid.view : this.grid;
7647 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7650 if(e.shiftKey && this.last !== false){
7651 var last = this.last;
7652 this.selectRange(last, rowIndex, e.ctrlKey);
7653 this.last = last; // reset the last
7654 view.focusRow(rowIndex);
7656 var isSelected = this.isSelected(rowIndex);
7657 if(e.button !== 0 && isSelected){
7658 view.focusRow(rowIndex);
7659 }else if(e.ctrlKey && isSelected){
7660 this.deselectRow(rowIndex);
7661 }else if(!isSelected){
7662 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7663 view.focusRow(rowIndex);
7666 this.fireEvent("afterselectionchange", this);
7669 handleDragableRowClick : function(grid, rowIndex, e)
7671 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7672 this.selectRow(rowIndex, false);
7673 var view = this.grid.view ? this.grid.view : this.grid;
7674 view.focusRow(rowIndex);
7675 this.fireEvent("afterselectionchange", this);
7680 * Selects multiple rows.
7681 * @param {Array} rows Array of the indexes of the row to select
7682 * @param {Boolean} keepExisting (optional) True to keep existing selections
7684 selectRows : function(rows, keepExisting){
7686 this.clearSelections();
7688 for(var i = 0, len = rows.length; i < len; i++){
7689 this.selectRow(rows[i], true);
7694 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7695 * @param {Number} startRow The index of the first row in the range
7696 * @param {Number} endRow The index of the last row in the range
7697 * @param {Boolean} keepExisting (optional) True to retain existing selections
7699 selectRange : function(startRow, endRow, keepExisting){
7704 this.clearSelections();
7706 if(startRow <= endRow){
7707 for(var i = startRow; i <= endRow; i++){
7708 this.selectRow(i, true);
7711 for(var i = startRow; i >= endRow; i--){
7712 this.selectRow(i, true);
7718 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7719 * @param {Number} startRow The index of the first row in the range
7720 * @param {Number} endRow The index of the last row in the range
7722 deselectRange : function(startRow, endRow, preventViewNotify){
7726 for(var i = startRow; i <= endRow; i++){
7727 this.deselectRow(i, preventViewNotify);
7733 * @param {Number} row The index of the row to select
7734 * @param {Boolean} keepExisting (optional) True to keep existing selections
7736 selectRow : function(index, keepExisting, preventViewNotify){
7737 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7740 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7741 if(!keepExisting || this.singleSelect){
7742 this.clearSelections();
7744 var r = this.grid.ds.getAt(index);
7745 this.selections.add(r);
7746 this.last = this.lastActive = index;
7747 if(!preventViewNotify){
7748 var view = this.grid.view ? this.grid.view : this.grid;
7749 view.onRowSelect(index);
7751 this.fireEvent("rowselect", this, index, r);
7752 this.fireEvent("selectionchange", this);
7758 * @param {Number} row The index of the row to deselect
7760 deselectRow : function(index, preventViewNotify){
7764 if(this.last == index){
7767 if(this.lastActive == index){
7768 this.lastActive = false;
7770 var r = this.grid.ds.getAt(index);
7771 this.selections.remove(r);
7772 if(!preventViewNotify){
7773 var view = this.grid.view ? this.grid.view : this.grid;
7774 view.onRowDeselect(index);
7776 this.fireEvent("rowdeselect", this, index);
7777 this.fireEvent("selectionchange", this);
7781 restoreLast : function(){
7783 this.last = this._last;
7788 acceptsNav : function(row, col, cm){
7789 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7793 onEditorKey : function(field, e){
7794 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7799 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7801 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7803 }else if(k == e.ENTER && !e.ctrlKey){
7807 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7809 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7811 }else if(k == e.ESC){
7815 g.startEditing(newCell[0], newCell[1]);
7820 * Ext JS Library 1.1.1
7821 * Copyright(c) 2006-2007, Ext JS, LLC.
7823 * Originally Released Under LGPL - original licence link has changed is not relivant.
7826 * <script type="text/javascript">
7831 * @class Roo.grid.ColumnModel
7832 * @extends Roo.util.Observable
7833 * This is the default implementation of a ColumnModel used by the Grid. It defines
7834 * the columns in the grid.
7837 var colModel = new Roo.grid.ColumnModel([
7838 {header: "Ticker", width: 60, sortable: true, locked: true},
7839 {header: "Company Name", width: 150, sortable: true},
7840 {header: "Market Cap.", width: 100, sortable: true},
7841 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7842 {header: "Employees", width: 100, sortable: true, resizable: false}
7847 * The config options listed for this class are options which may appear in each
7848 * individual column definition.
7849 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7851 * @param {Object} config An Array of column config objects. See this class's
7852 * config objects for details.
7854 Roo.grid.ColumnModel = function(config){
7856 * The config passed into the constructor
7858 this.config = []; //config;
7861 // if no id, create one
7862 // if the column does not have a dataIndex mapping,
7863 // map it to the order it is in the config
7864 for(var i = 0, len = config.length; i < len; i++){
7865 this.addColumn(config[i]);
7870 * The width of columns which have no width specified (defaults to 100)
7873 this.defaultWidth = 100;
7876 * Default sortable of columns which have no sortable specified (defaults to false)
7879 this.defaultSortable = false;
7883 * @event widthchange
7884 * Fires when the width of a column changes.
7885 * @param {ColumnModel} this
7886 * @param {Number} columnIndex The column index
7887 * @param {Number} newWidth The new width
7889 "widthchange": true,
7891 * @event headerchange
7892 * Fires when the text of a header changes.
7893 * @param {ColumnModel} this
7894 * @param {Number} columnIndex The column index
7895 * @param {Number} newText The new header text
7897 "headerchange": true,
7899 * @event hiddenchange
7900 * Fires when a column is hidden or "unhidden".
7901 * @param {ColumnModel} this
7902 * @param {Number} columnIndex The column index
7903 * @param {Boolean} hidden true if hidden, false otherwise
7905 "hiddenchange": true,
7907 * @event columnmoved
7908 * Fires when a column is moved.
7909 * @param {ColumnModel} this
7910 * @param {Number} oldIndex
7911 * @param {Number} newIndex
7913 "columnmoved" : true,
7915 * @event columlockchange
7916 * Fires when a column's locked state is changed
7917 * @param {ColumnModel} this
7918 * @param {Number} colIndex
7919 * @param {Boolean} locked true if locked
7921 "columnlockchange" : true
7923 Roo.grid.ColumnModel.superclass.constructor.call(this);
7925 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7927 * @cfg {String} header The header text to display in the Grid view.
7930 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7931 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7932 * specified, the column's index is used as an index into the Record's data Array.
7935 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7936 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7939 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7940 * Defaults to the value of the {@link #defaultSortable} property.
7941 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7944 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7947 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7950 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7953 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7956 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7957 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7958 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7959 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7962 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7965 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7968 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7971 * @cfg {String} cursor (Optional)
7974 * @cfg {String} tooltip (Optional)
7977 * @cfg {Number} xs (Optional)
7980 * @cfg {Number} sm (Optional)
7983 * @cfg {Number} md (Optional)
7986 * @cfg {Number} lg (Optional)
7989 * Returns the id of the column at the specified index.
7990 * @param {Number} index The column index
7991 * @return {String} the id
7993 getColumnId : function(index){
7994 return this.config[index].id;
7998 * Returns the column for a specified id.
7999 * @param {String} id The column id
8000 * @return {Object} the column
8002 getColumnById : function(id){
8003 return this.lookup[id];
8008 * Returns the column Object for a specified dataIndex.
8009 * @param {String} dataIndex The column dataIndex
8010 * @return {Object|Boolean} the column or false if not found
8012 getColumnByDataIndex: function(dataIndex){
8013 var index = this.findColumnIndex(dataIndex);
8014 return index > -1 ? this.config[index] : false;
8018 * Returns the index for a specified column id.
8019 * @param {String} id The column id
8020 * @return {Number} the index, or -1 if not found
8022 getIndexById : function(id){
8023 for(var i = 0, len = this.config.length; i < len; i++){
8024 if(this.config[i].id == id){
8032 * Returns the index for a specified column dataIndex.
8033 * @param {String} dataIndex The column dataIndex
8034 * @return {Number} the index, or -1 if not found
8037 findColumnIndex : function(dataIndex){
8038 for(var i = 0, len = this.config.length; i < len; i++){
8039 if(this.config[i].dataIndex == dataIndex){
8047 moveColumn : function(oldIndex, newIndex){
8048 var c = this.config[oldIndex];
8049 this.config.splice(oldIndex, 1);
8050 this.config.splice(newIndex, 0, c);
8051 this.dataMap = null;
8052 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8055 isLocked : function(colIndex){
8056 return this.config[colIndex].locked === true;
8059 setLocked : function(colIndex, value, suppressEvent){
8060 if(this.isLocked(colIndex) == value){
8063 this.config[colIndex].locked = value;
8065 this.fireEvent("columnlockchange", this, colIndex, value);
8069 getTotalLockedWidth : function(){
8071 for(var i = 0; i < this.config.length; i++){
8072 if(this.isLocked(i) && !this.isHidden(i)){
8073 this.totalWidth += this.getColumnWidth(i);
8079 getLockedCount : function(){
8080 for(var i = 0, len = this.config.length; i < len; i++){
8081 if(!this.isLocked(i)){
8086 return this.config.length;
8090 * Returns the number of columns.
8093 getColumnCount : function(visibleOnly){
8094 if(visibleOnly === true){
8096 for(var i = 0, len = this.config.length; i < len; i++){
8097 if(!this.isHidden(i)){
8103 return this.config.length;
8107 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8108 * @param {Function} fn
8109 * @param {Object} scope (optional)
8110 * @return {Array} result
8112 getColumnsBy : function(fn, scope){
8114 for(var i = 0, len = this.config.length; i < len; i++){
8115 var c = this.config[i];
8116 if(fn.call(scope||this, c, i) === true){
8124 * Returns true if the specified column is sortable.
8125 * @param {Number} col The column index
8128 isSortable : function(col){
8129 if(typeof this.config[col].sortable == "undefined"){
8130 return this.defaultSortable;
8132 return this.config[col].sortable;
8136 * Returns the rendering (formatting) function defined for the column.
8137 * @param {Number} col The column index.
8138 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8140 getRenderer : function(col){
8141 if(!this.config[col].renderer){
8142 return Roo.grid.ColumnModel.defaultRenderer;
8144 return this.config[col].renderer;
8148 * Sets the rendering (formatting) function for a column.
8149 * @param {Number} col The column index
8150 * @param {Function} fn The function to use to process the cell's raw data
8151 * to return HTML markup for the grid view. The render function is called with
8152 * the following parameters:<ul>
8153 * <li>Data value.</li>
8154 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8155 * <li>css A CSS style string to apply to the table cell.</li>
8156 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8157 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8158 * <li>Row index</li>
8159 * <li>Column index</li>
8160 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8162 setRenderer : function(col, fn){
8163 this.config[col].renderer = fn;
8167 * Returns the width for the specified column.
8168 * @param {Number} col The column index
8171 getColumnWidth : function(col){
8172 return this.config[col].width * 1 || this.defaultWidth;
8176 * Sets the width for a column.
8177 * @param {Number} col The column index
8178 * @param {Number} width The new width
8180 setColumnWidth : function(col, width, suppressEvent){
8181 this.config[col].width = width;
8182 this.totalWidth = null;
8184 this.fireEvent("widthchange", this, col, width);
8189 * Returns the total width of all columns.
8190 * @param {Boolean} includeHidden True to include hidden column widths
8193 getTotalWidth : function(includeHidden){
8194 if(!this.totalWidth){
8195 this.totalWidth = 0;
8196 for(var i = 0, len = this.config.length; i < len; i++){
8197 if(includeHidden || !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8202 return this.totalWidth;
8206 * Returns the header for the specified column.
8207 * @param {Number} col The column index
8210 getColumnHeader : function(col){
8211 return this.config[col].header;
8215 * Sets the header for a column.
8216 * @param {Number} col The column index
8217 * @param {String} header The new header
8219 setColumnHeader : function(col, header){
8220 this.config[col].header = header;
8221 this.fireEvent("headerchange", this, col, header);
8225 * Returns the tooltip for the specified column.
8226 * @param {Number} col The column index
8229 getColumnTooltip : function(col){
8230 return this.config[col].tooltip;
8233 * Sets the tooltip for a column.
8234 * @param {Number} col The column index
8235 * @param {String} tooltip The new tooltip
8237 setColumnTooltip : function(col, tooltip){
8238 this.config[col].tooltip = tooltip;
8242 * Returns the dataIndex for the specified column.
8243 * @param {Number} col The column index
8246 getDataIndex : function(col){
8247 return this.config[col].dataIndex;
8251 * Sets the dataIndex for a column.
8252 * @param {Number} col The column index
8253 * @param {Number} dataIndex The new dataIndex
8255 setDataIndex : function(col, dataIndex){
8256 this.config[col].dataIndex = dataIndex;
8262 * Returns true if the cell is editable.
8263 * @param {Number} colIndex The column index
8264 * @param {Number} rowIndex The row index - this is nto actually used..?
8267 isCellEditable : function(colIndex, rowIndex){
8268 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8272 * Returns the editor defined for the cell/column.
8273 * return false or null to disable editing.
8274 * @param {Number} colIndex The column index
8275 * @param {Number} rowIndex The row index
8278 getCellEditor : function(colIndex, rowIndex){
8279 return this.config[colIndex].editor;
8283 * Sets if a column is editable.
8284 * @param {Number} col The column index
8285 * @param {Boolean} editable True if the column is editable
8287 setEditable : function(col, editable){
8288 this.config[col].editable = editable;
8293 * Returns true if the column is hidden.
8294 * @param {Number} colIndex The column index
8297 isHidden : function(colIndex){
8298 return this.config[colIndex].hidden;
8303 * Returns true if the column width cannot be changed
8305 isFixed : function(colIndex){
8306 return this.config[colIndex].fixed;
8310 * Returns true if the column can be resized
8313 isResizable : function(colIndex){
8314 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8317 * Sets if a column is hidden.
8318 * @param {Number} colIndex The column index
8319 * @param {Boolean} hidden True if the column is hidden
8321 setHidden : function(colIndex, hidden){
8322 this.config[colIndex].hidden = hidden;
8323 this.totalWidth = null;
8324 this.fireEvent("hiddenchange", this, colIndex, hidden);
8328 * Sets the editor for a column.
8329 * @param {Number} col The column index
8330 * @param {Object} editor The editor object
8332 setEditor : function(col, editor){
8333 this.config[col].editor = editor;
8336 * Add a column (experimental...) - defaults to adding to the end..
8337 * @param {Object} config
8339 addColumn : function(c)
8342 var i = this.config.length;
8345 if(typeof c.dataIndex == "undefined"){
8348 if(typeof c.renderer == "string"){
8349 c.renderer = Roo.util.Format[c.renderer];
8351 if(typeof c.id == "undefined"){
8354 if(c.editor && c.editor.xtype){
8355 c.editor = Roo.factory(c.editor, Roo.grid);
8357 if(c.editor && c.editor.isFormField){
8358 c.editor = new Roo.grid.GridEditor(c.editor);
8360 this.lookup[c.id] = c;
8365 Roo.grid.ColumnModel.defaultRenderer = function(value)
8367 if(typeof value == "object") {
8370 if(typeof value == "string" && value.length < 1){
8374 return String.format("{0}", value);
8377 // Alias for backwards compatibility
8378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8381 * Ext JS Library 1.1.1
8382 * Copyright(c) 2006-2007, Ext JS, LLC.
8384 * Originally Released Under LGPL - original licence link has changed is not relivant.
8387 * <script type="text/javascript">
8391 * @class Roo.LoadMask
8392 * A simple utility class for generically masking elements while loading data. If the element being masked has
8393 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8394 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8395 * element's UpdateManager load indicator and will be destroyed after the initial load.
8397 * Create a new LoadMask
8398 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8399 * @param {Object} config The config object
8401 Roo.LoadMask = function(el, config){
8402 this.el = Roo.get(el);
8403 Roo.apply(this, config);
8405 this.store.on('beforeload', this.onBeforeLoad, this);
8406 this.store.on('load', this.onLoad, this);
8407 this.store.on('loadexception', this.onLoadException, this);
8408 this.removeMask = false;
8410 var um = this.el.getUpdateManager();
8411 um.showLoadIndicator = false; // disable the default indicator
8412 um.on('beforeupdate', this.onBeforeLoad, this);
8413 um.on('update', this.onLoad, this);
8414 um.on('failure', this.onLoad, this);
8415 this.removeMask = true;
8419 Roo.LoadMask.prototype = {
8421 * @cfg {Boolean} removeMask
8422 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8423 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8427 * The text to display in a centered loading message box (defaults to 'Loading...')
8431 * @cfg {String} msgCls
8432 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8434 msgCls : 'x-mask-loading',
8437 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8443 * Disables the mask to prevent it from being displayed
8445 disable : function(){
8446 this.disabled = true;
8450 * Enables the mask so that it can be displayed
8452 enable : function(){
8453 this.disabled = false;
8456 onLoadException : function()
8460 if (typeof(arguments[3]) != 'undefined') {
8461 Roo.MessageBox.alert("Error loading",arguments[3]);
8465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8473 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8478 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8482 onBeforeLoad : function(){
8484 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8489 destroy : function(){
8491 this.store.un('beforeload', this.onBeforeLoad, this);
8492 this.store.un('load', this.onLoad, this);
8493 this.store.un('loadexception', this.onLoadException, this);
8495 var um = this.el.getUpdateManager();
8496 um.un('beforeupdate', this.onBeforeLoad, this);
8497 um.un('update', this.onLoad, this);
8498 um.un('failure', this.onLoad, this);
8502 * @class Roo.bootstrap.Table
8504 * @extends Roo.bootstrap.Component
8505 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8506 * Similar to Roo.grid.Grid
8508 var table = Roo.factory({
8510 xns : Roo.bootstrap,
8511 autoSizeColumns: true,
8518 sortInfo : { direction : 'ASC', field: 'name' },
8520 xtype : 'HttpProxy',
8523 url : 'https://example.com/some.data.url.json'
8526 xtype : 'JsonReader',
8528 fields : [ 'id', 'name', whatever' ],
8535 xtype : 'ColumnModel',
8539 dataIndex : 'is_in_group',
8542 renderer : function(v, x , r) {
8544 return String.format("{0}", v)
8550 xtype : 'RowSelectionModel',
8551 xns : Roo.bootstrap.Table
8552 // you can add listeners to catch selection change here....
8558 grid.render(Roo.get("some-div"));
8561 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8566 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8567 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8568 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8570 * @cfg {String} cls table class
8573 * @cfg {boolean} striped Should the rows be alternative striped
8574 * @cfg {boolean} bordered Add borders to the table
8575 * @cfg {boolean} hover Add hover highlighting
8576 * @cfg {boolean} condensed Format condensed
8577 * @cfg {boolean} responsive Format condensed
8578 * @cfg {Boolean} loadMask (true|false) default false
8579 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8580 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8581 * @cfg {Boolean} rowSelection (true|false) default false
8582 * @cfg {Boolean} cellSelection (true|false) default false
8583 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8584 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8585 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8586 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8590 * Create a new Table
8591 * @param {Object} config The config object
8594 Roo.bootstrap.Table = function(config)
8596 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8599 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8600 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8601 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8602 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8604 this.view = this; // compat with grid.
8606 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8608 this.sm.grid = this;
8609 this.selModel = Roo.factory(this.sm, Roo.grid);
8610 this.sm = this.selModel;
8611 this.sm.xmodule = this.xmodule || false;
8614 if (this.cm && typeof(this.cm.config) == 'undefined') {
8615 this.colModel = new Roo.grid.ColumnModel(this.cm);
8616 this.cm = this.colModel;
8617 this.cm.xmodule = this.xmodule || false;
8620 this.store= Roo.factory(this.store, Roo.data);
8621 this.ds = this.store;
8622 this.ds.xmodule = this.xmodule || false;
8625 if (this.footer && this.store) {
8626 this.footer.dataSource = this.ds;
8627 this.footer = Roo.factory(this.footer);
8634 * Fires when a cell is clicked
8635 * @param {Roo.bootstrap.Table} this
8636 * @param {Roo.Element} el
8637 * @param {Number} rowIndex
8638 * @param {Number} columnIndex
8639 * @param {Roo.EventObject} e
8643 * @event celldblclick
8644 * Fires when a cell is double clicked
8645 * @param {Roo.bootstrap.Table} this
8646 * @param {Roo.Element} el
8647 * @param {Number} rowIndex
8648 * @param {Number} columnIndex
8649 * @param {Roo.EventObject} e
8651 "celldblclick" : true,
8654 * Fires when a row is clicked
8655 * @param {Roo.bootstrap.Table} this
8656 * @param {Roo.Element} el
8657 * @param {Number} rowIndex
8658 * @param {Roo.EventObject} e
8662 * @event rowdblclick
8663 * Fires when a row is double clicked
8664 * @param {Roo.bootstrap.Table} this
8665 * @param {Roo.Element} el
8666 * @param {Number} rowIndex
8667 * @param {Roo.EventObject} e
8669 "rowdblclick" : true,
8672 * Fires when a mouseover occur
8673 * @param {Roo.bootstrap.Table} this
8674 * @param {Roo.Element} el
8675 * @param {Number} rowIndex
8676 * @param {Number} columnIndex
8677 * @param {Roo.EventObject} e
8682 * Fires when a mouseout occur
8683 * @param {Roo.bootstrap.Table} this
8684 * @param {Roo.Element} el
8685 * @param {Number} rowIndex
8686 * @param {Number} columnIndex
8687 * @param {Roo.EventObject} e
8692 * Fires when a row is rendered, so you can change add a style to it.
8693 * @param {Roo.bootstrap.Table} this
8694 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8698 * @event rowsrendered
8699 * Fires when all the rows have been rendered
8700 * @param {Roo.bootstrap.Table} this
8702 'rowsrendered' : true,
8704 * @event contextmenu
8705 * The raw contextmenu event for the entire grid.
8706 * @param {Roo.EventObject} e
8708 "contextmenu" : true,
8710 * @event rowcontextmenu
8711 * Fires when a row is right clicked
8712 * @param {Roo.bootstrap.Table} this
8713 * @param {Number} rowIndex
8714 * @param {Roo.EventObject} e
8716 "rowcontextmenu" : true,
8718 * @event cellcontextmenu
8719 * Fires when a cell is right clicked
8720 * @param {Roo.bootstrap.Table} this
8721 * @param {Number} rowIndex
8722 * @param {Number} cellIndex
8723 * @param {Roo.EventObject} e
8725 "cellcontextmenu" : true,
8727 * @event headercontextmenu
8728 * Fires when a header is right clicked
8729 * @param {Roo.bootstrap.Table} this
8730 * @param {Number} columnIndex
8731 * @param {Roo.EventObject} e
8733 "headercontextmenu" : true,
8736 * The raw mousedown event for the entire grid.
8737 * @param {Roo.EventObject} e
8744 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8761 rowSelection : false,
8762 cellSelection : false,
8765 // Roo.Element - the tbody
8766 bodyEl: false, // <tbody> Roo.Element - thead element
8768 headEl: false, // <thead> Roo.Element - thead element
8770 container: false, // used by gridpanel...
8776 auto_hide_footer : false,
8778 view: false, // actually points to this..
8780 getAutoCreate : function()
8782 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8789 // this get's auto added by panel.Grid
8790 if (this.scrollBody) {
8791 cfg.cls += ' table-body-fixed';
8794 cfg.cls += ' table-striped';
8798 cfg.cls += ' table-hover';
8800 if (this.bordered) {
8801 cfg.cls += ' table-bordered';
8803 if (this.condensed) {
8804 cfg.cls += ' table-condensed';
8807 if (this.responsive) {
8808 cfg.cls += ' table-responsive';
8812 cfg.cls+= ' ' +this.cls;
8818 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8821 if(this.store || this.cm){
8822 if(this.headerShow){
8823 cfg.cn.push(this.renderHeader());
8826 cfg.cn.push(this.renderBody());
8828 if(this.footerShow){
8829 cfg.cn.push(this.renderFooter());
8831 // where does this come from?
8832 //cfg.cls+= ' TableGrid';
8835 return { cn : [ cfg ] };
8838 initEvents : function()
8840 if(!this.store || !this.cm){
8843 if (this.selModel) {
8844 this.selModel.initEvents();
8848 //Roo.log('initEvents with ds!!!!');
8850 this.bodyEl = this.el.select('tbody', true).first();
8851 this.headEl = this.el.select('thead', true).first();
8852 this.mainFoot = this.el.select('tfoot', true).first();
8857 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8858 e.on('click', this.sort, this);
8862 // why is this done????? = it breaks dialogs??
8863 //this.parent().el.setStyle('position', 'relative');
8867 this.footer.parentId = this.id;
8868 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8871 this.el.select('tfoot tr td').first().addClass('hide');
8876 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8879 this.store.on('load', this.onLoad, this);
8880 this.store.on('beforeload', this.onBeforeLoad, this);
8881 this.store.on('update', this.onUpdate, this);
8882 this.store.on('add', this.onAdd, this);
8883 this.store.on("clear", this.clear, this);
8885 this.el.on("contextmenu", this.onContextMenu, this);
8888 this.cm.on("headerchange", this.onHeaderChange, this);
8889 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8891 //?? does bodyEl get replaced on render?
8892 this.bodyEl.on("click", this.onClick, this);
8893 this.bodyEl.on("dblclick", this.onDblClick, this);
8894 this.bodyEl.on('scroll', this.onBodyScroll, this);
8896 // guessing mainbody will work - this relays usually caught by selmodel at present.
8897 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8903 // Compatibility with grid - we implement all the view features at present.
8904 getView : function()
8909 onContextMenu : function(e, t)
8911 this.processEvent("contextmenu", e);
8914 processEvent : function(name, e)
8916 if (name != 'touchstart' ) {
8917 this.fireEvent(name, e);
8920 var t = e.getTarget();
8922 var cell = Roo.get(t);
8928 if(cell.findParent('tfoot', false, true)){
8932 if(cell.findParent('thead', false, true)){
8934 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8935 cell = Roo.get(t).findParent('th', false, true);
8937 Roo.log("failed to find th in thead?");
8938 Roo.log(e.getTarget());
8943 var cellIndex = cell.dom.cellIndex;
8945 var ename = name == 'touchstart' ? 'click' : name;
8946 this.fireEvent("header" + ename, this, cellIndex, e);
8951 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8952 cell = Roo.get(t).findParent('td', false, true);
8954 Roo.log("failed to find th in tbody?");
8955 Roo.log(e.getTarget());
8960 var row = cell.findParent('tr', false, true);
8961 var cellIndex = cell.dom.cellIndex;
8962 var rowIndex = row.dom.rowIndex - 1;
8966 this.fireEvent("row" + name, this, rowIndex, e);
8970 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8976 onMouseover : function(e, el)
8978 var cell = Roo.get(el);
8984 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8985 cell = cell.findParent('td', false, true);
8988 var row = cell.findParent('tr', false, true);
8989 var cellIndex = cell.dom.cellIndex;
8990 var rowIndex = row.dom.rowIndex - 1; // start from 0
8992 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8996 onMouseout : function(e, el)
8998 var cell = Roo.get(el);
9004 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9005 cell = cell.findParent('td', false, true);
9008 var row = cell.findParent('tr', false, true);
9009 var cellIndex = cell.dom.cellIndex;
9010 var rowIndex = row.dom.rowIndex - 1; // start from 0
9012 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9016 onClick : function(e, el)
9018 var cell = Roo.get(el);
9020 if(!cell || (!this.cellSelection && !this.rowSelection)){
9024 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9025 cell = cell.findParent('td', false, true);
9028 if(!cell || typeof(cell) == 'undefined'){
9032 var row = cell.findParent('tr', false, true);
9034 if(!row || typeof(row) == 'undefined'){
9038 var cellIndex = cell.dom.cellIndex;
9039 var rowIndex = this.getRowIndex(row);
9041 // why??? - should these not be based on SelectionModel?
9042 //if(this.cellSelection){
9043 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9046 //if(this.rowSelection){
9047 this.fireEvent('rowclick', this, row, rowIndex, e);
9052 onDblClick : function(e,el)
9054 var cell = Roo.get(el);
9056 if(!cell || (!this.cellSelection && !this.rowSelection)){
9060 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9061 cell = cell.findParent('td', false, true);
9064 if(!cell || typeof(cell) == 'undefined'){
9068 var row = cell.findParent('tr', false, true);
9070 if(!row || typeof(row) == 'undefined'){
9074 var cellIndex = cell.dom.cellIndex;
9075 var rowIndex = this.getRowIndex(row);
9077 if(this.cellSelection){
9078 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9081 if(this.rowSelection){
9082 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9085 findRowIndex : function(el)
9087 var cell = Roo.get(el);
9091 var row = cell.findParent('tr', false, true);
9093 if(!row || typeof(row) == 'undefined'){
9096 return this.getRowIndex(row);
9098 sort : function(e,el)
9100 var col = Roo.get(el);
9102 if(!col.hasClass('sortable')){
9106 var sort = col.attr('sort');
9109 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9113 this.store.sortInfo = {field : sort, direction : dir};
9116 Roo.log("calling footer first");
9117 this.footer.onClick('first');
9120 this.store.load({ params : { start : 0 } });
9124 renderHeader : function()
9132 this.totalWidth = 0;
9134 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9136 var config = cm.config[i];
9140 cls : 'x-hcol-' + i,
9143 html: cm.getColumnHeader(i)
9146 var tooltip = cm.getColumnTooltip(i);
9148 c.tooltip = tooltip;
9154 if(typeof(config.sortable) != 'undefined' && config.sortable){
9156 c.html = '<i class="fa"></i>' + c.html;
9159 // could use BS4 hidden-..-down
9161 if(typeof(config.lgHeader) != 'undefined'){
9162 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9165 if(typeof(config.mdHeader) != 'undefined'){
9166 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9169 if(typeof(config.smHeader) != 'undefined'){
9170 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9173 if(typeof(config.xsHeader) != 'undefined'){
9174 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9181 if(typeof(config.tooltip) != 'undefined'){
9182 c.tooltip = config.tooltip;
9185 if(typeof(config.colspan) != 'undefined'){
9186 c.colspan = config.colspan;
9189 if(typeof(config.hidden) != 'undefined' && config.hidden){
9192 c.cls += ' d-block';
9195 if(typeof(config.dataIndex) != 'undefined'){
9196 c.sort = config.dataIndex;
9201 if(typeof(config.align) != 'undefined' && config.align.length){
9202 c.style += ' text-align:' + config.align + ';';
9205 if(typeof(config.width) != 'undefined'){
9206 c.style += ' width:' + config.width + 'px;';
9207 this.totalWidth += config.width;
9209 this.totalWidth += 100; // assume minimum of 100 per column?
9212 if(typeof(config.cls) != 'undefined'){
9213 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9216 ['xs','sm','md','lg'].map(function(size){
9218 if(typeof(config[size]) == 'undefined'){
9222 if (!config[size]) { // 0 = hidden
9223 // BS 4 '0' is treated as hide that column and below.
9224 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9228 c.cls += ' col-' + size + '-' + config[size] + (
9229 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9235 c.html +=' <span class="roo-hd-split"></span>';
9244 renderBody : function()
9254 colspan : this.cm.getColumnCount()
9264 renderFooter : function()
9274 colspan : this.cm.getColumnCount()
9288 // Roo.log('ds onload');
9293 var ds = this.store;
9295 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9296 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9297 if (_this.store.sortInfo) {
9299 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9300 e.select('i', true).addClass(['fa-arrow-up']);
9303 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9304 e.select('i', true).addClass(['fa-arrow-down']);
9309 var tbody = this.bodyEl;
9311 if(ds.getCount() > 0){
9312 ds.data.each(function(d,rowIndex){
9313 var row = this.renderRow(cm, ds, rowIndex);
9315 tbody.createChild(row);
9319 if(row.cellObjects.length){
9320 Roo.each(row.cellObjects, function(r){
9321 _this.renderCellObject(r);
9328 var tfoot = this.el.select('tfoot', true).first();
9330 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9332 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9334 var total = this.ds.getTotalCount();
9336 if(this.footer.pageSize < total){
9337 this.mainFoot.show();
9341 Roo.each(this.el.select('tbody td', true).elements, function(e){
9342 e.on('mouseover', _this.onMouseover, _this);
9345 Roo.each(this.el.select('tbody td', true).elements, function(e){
9346 e.on('mouseout', _this.onMouseout, _this);
9348 this.fireEvent('rowsrendered', this);
9354 onUpdate : function(ds,record)
9356 this.refreshRow(record);
9360 onRemove : function(ds, record, index, isUpdate){
9361 if(isUpdate !== true){
9362 this.fireEvent("beforerowremoved", this, index, record);
9364 var bt = this.bodyEl.dom;
9366 var rows = this.el.select('tbody > tr', true).elements;
9368 if(typeof(rows[index]) != 'undefined'){
9369 bt.removeChild(rows[index].dom);
9372 // if(bt.rows[index]){
9373 // bt.removeChild(bt.rows[index]);
9376 if(isUpdate !== true){
9377 //this.stripeRows(index);
9378 //this.syncRowHeights(index, index);
9380 this.fireEvent("rowremoved", this, index, record);
9384 onAdd : function(ds, records, rowIndex)
9386 //Roo.log('on Add called');
9387 // - note this does not handle multiple adding very well..
9388 var bt = this.bodyEl.dom;
9389 for (var i =0 ; i < records.length;i++) {
9390 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9391 //Roo.log(records[i]);
9392 //Roo.log(this.store.getAt(rowIndex+i));
9393 this.insertRow(this.store, rowIndex + i, false);
9400 refreshRow : function(record){
9401 var ds = this.store, index;
9402 if(typeof record == 'number'){
9404 record = ds.getAt(index);
9406 index = ds.indexOf(record);
9408 return; // should not happen - but seems to
9411 this.insertRow(ds, index, true);
9413 this.onRemove(ds, record, index+1, true);
9415 //this.syncRowHeights(index, index);
9417 this.fireEvent("rowupdated", this, index, record);
9420 onRowSelect : function(rowIndex){
9421 var row = this.getRowDom(rowIndex);
9422 row.addClass(['bg-info','info']);
9425 onRowDeselect : function(rowIndex){
9426 var row = this.getRowDom(rowIndex);
9427 row.removeClass(['bg-info','info']);
9430 * Focuses the specified row.
9431 * @param {Number} row The row index
9433 focusRow : function(row)
9435 //Roo.log('GridView.focusRow');
9436 var x = this.bodyEl.dom.scrollLeft;
9437 this.focusCell(row, 0, false);
9438 this.bodyEl.dom.scrollLeft = x;
9442 * Focuses the specified cell.
9443 * @param {Number} row The row index
9444 * @param {Number} col The column index
9445 * @param {Boolean} hscroll false to disable horizontal scrolling
9447 focusCell : function(row, col, hscroll)
9449 //Roo.log('GridView.focusCell');
9450 var el = this.ensureVisible(row, col, hscroll);
9451 // not sure what focusEL achives = it's a <a> pos relative
9452 //this.focusEl.alignTo(el, "tl-tl");
9454 // this.focusEl.focus();
9456 // this.focusEl.focus.defer(1, this.focusEl);
9461 * Scrolls the specified cell into view
9462 * @param {Number} row The row index
9463 * @param {Number} col The column index
9464 * @param {Boolean} hscroll false to disable horizontal scrolling
9466 ensureVisible : function(row, col, hscroll)
9468 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9469 //return null; //disable for testing.
9470 if(typeof row != "number"){
9473 if(row < 0 && row >= this.ds.getCount()){
9476 col = (col !== undefined ? col : 0);
9478 while(cm.isHidden(col)){
9482 var el = this.getCellDom(row, col);
9486 var c = this.bodyEl.dom;
9488 var ctop = parseInt(el.offsetTop, 10);
9489 var cleft = parseInt(el.offsetLeft, 10);
9490 var cbot = ctop + el.offsetHeight;
9491 var cright = cleft + el.offsetWidth;
9493 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9494 var ch = 0; //?? header is not withing the area?
9495 var stop = parseInt(c.scrollTop, 10);
9496 var sleft = parseInt(c.scrollLeft, 10);
9497 var sbot = stop + ch;
9498 var sright = sleft + c.clientWidth;
9500 Roo.log('GridView.ensureVisible:' +
9502 ' c.clientHeight:' + c.clientHeight +
9503 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9512 //Roo.log("set scrolltop to ctop DISABLE?");
9513 }else if(cbot > sbot){
9514 //Roo.log("set scrolltop to cbot-ch");
9515 c.scrollTop = cbot-ch;
9518 if(hscroll !== false){
9520 c.scrollLeft = cleft;
9521 }else if(cright > sright){
9522 c.scrollLeft = cright-c.clientWidth;
9530 insertRow : function(dm, rowIndex, isUpdate){
9533 this.fireEvent("beforerowsinserted", this, rowIndex);
9535 //var s = this.getScrollState();
9536 var row = this.renderRow(this.cm, this.store, rowIndex);
9537 // insert before rowIndex..
9538 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9542 if(row.cellObjects.length){
9543 Roo.each(row.cellObjects, function(r){
9544 _this.renderCellObject(r);
9549 this.fireEvent("rowsinserted", this, rowIndex);
9550 //this.syncRowHeights(firstRow, lastRow);
9551 //this.stripeRows(firstRow);
9558 getRowDom : function(rowIndex)
9560 var rows = this.el.select('tbody > tr', true).elements;
9562 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9565 getCellDom : function(rowIndex, colIndex)
9567 var row = this.getRowDom(rowIndex);
9568 if (row === false) {
9571 var cols = row.select('td', true).elements;
9572 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9576 // returns the object tree for a tr..
9579 renderRow : function(cm, ds, rowIndex)
9581 var d = ds.getAt(rowIndex);
9585 cls : 'x-row-' + rowIndex,
9589 var cellObjects = [];
9591 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9592 var config = cm.config[i];
9594 var renderer = cm.getRenderer(i);
9598 if(typeof(renderer) !== 'undefined'){
9599 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9601 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9602 // and are rendered into the cells after the row is rendered - using the id for the element.
9604 if(typeof(value) === 'object'){
9614 rowIndex : rowIndex,
9619 this.fireEvent('rowclass', this, rowcfg);
9623 // this might end up displaying HTML?
9624 // this is too messy... - better to only do it on columsn you know are going to be too long
9625 //tooltip : (typeof(value) === 'object') ? '' : value,
9626 cls : rowcfg.rowClass + ' x-col-' + i,
9628 html: (typeof(value) === 'object') ? '' : value
9635 if(typeof(config.colspan) != 'undefined'){
9636 td.colspan = config.colspan;
9639 if(typeof(config.hidden) != 'undefined' && config.hidden){
9640 td.cls += ' d-none';
9642 td.cls += ' d-block';
9645 if(typeof(config.align) != 'undefined' && config.align.length){
9646 td.style += ' text-align:' + config.align + ';';
9648 if(typeof(config.valign) != 'undefined' && config.valign.length){
9649 td.style += ' vertical-align:' + config.valign + ';';
9652 if(typeof(config.width) != 'undefined'){
9653 td.style += ' width:' + config.width + 'px;';
9656 if(typeof(config.cursor) != 'undefined'){
9657 td.style += ' cursor:' + config.cursor + ';';
9660 if(typeof(config.cls) != 'undefined'){
9661 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9664 ['xs','sm','md','lg'].map(function(size){
9666 if(typeof(config[size]) == 'undefined'){
9672 if (!config[size]) { // 0 = hidden
9673 // BS 4 '0' is treated as hide that column and below.
9674 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9678 td.cls += ' col-' + size + '-' + config[size] + (
9679 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9689 row.cellObjects = cellObjects;
9697 onBeforeLoad : function()
9706 this.el.select('tbody', true).first().dom.innerHTML = '';
9709 * Show or hide a row.
9710 * @param {Number} rowIndex to show or hide
9711 * @param {Boolean} state hide
9713 setRowVisibility : function(rowIndex, state)
9715 var bt = this.bodyEl.dom;
9717 var rows = this.el.select('tbody > tr', true).elements;
9719 if(typeof(rows[rowIndex]) == 'undefined'){
9722 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9727 getSelectionModel : function(){
9729 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9731 return this.selModel;
9734 * Render the Roo.bootstrap object from renderder
9736 renderCellObject : function(r)
9740 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9742 var t = r.cfg.render(r.container);
9745 Roo.each(r.cfg.cn, function(c){
9747 container: t.getChildContainer(),
9750 _this.renderCellObject(child);
9755 getRowIndex : function(row)
9759 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9770 * Returns the grid's underlying element = used by panel.Grid
9771 * @return {Element} The element
9773 getGridEl : function(){
9777 * Forces a resize - used by panel.Grid
9778 * @return {Element} The element
9780 autoSize : function()
9782 //var ctr = Roo.get(this.container.dom.parentElement);
9783 var ctr = Roo.get(this.el.dom);
9785 var thd = this.getGridEl().select('thead',true).first();
9786 var tbd = this.getGridEl().select('tbody', true).first();
9787 var tfd = this.getGridEl().select('tfoot', true).first();
9789 var cw = ctr.getWidth();
9790 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9794 tbd.setWidth(ctr.getWidth());
9795 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9796 // this needs fixing for various usage - currently only hydra job advers I think..
9798 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9800 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9803 cw = Math.max(cw, this.totalWidth);
9804 this.getGridEl().select('tbody tr',true).setWidth(cw);
9806 // resize 'expandable coloumn?
9808 return; // we doe not have a view in this design..
9811 onBodyScroll: function()
9813 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9815 this.headEl.setStyle({
9816 'position' : 'relative',
9817 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
9823 var scrollHeight = this.bodyEl.dom.scrollHeight;
9825 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
9827 var height = this.bodyEl.getHeight();
9829 if(scrollHeight - height == scrollTop) {
9831 var total = this.ds.getTotalCount();
9833 if(this.footer.cursor + this.footer.pageSize < total){
9835 this.footer.ds.load({
9837 start : this.footer.cursor + this.footer.pageSize,
9838 limit : this.footer.pageSize
9848 onHeaderChange : function()
9850 var header = this.renderHeader();
9851 var table = this.el.select('table', true).first();
9853 this.headEl.remove();
9854 this.headEl = table.createChild(header, this.bodyEl, false);
9856 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9857 e.on('click', this.sort, this);
9863 onHiddenChange : function(colModel, colIndex, hidden)
9865 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9866 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9868 //this.CSS.updateRule(thSelector, "display", "");
9869 var cols = this.headEl.select('th', true).elements;
9870 if (typeof(cols[colIndex]) != 'undefined') {
9871 cols[colIndex].removeClass(['d-none', 'd-block']);
9872 cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
9874 this.CSS.updateRule(tdSelector, "display", "");
9877 // this.CSS.updateRule(thSelector, "display", "none");
9878 this.CSS.updateRule(tdSelector, "display", "none");
9881 this.onHeaderChange();
9885 setColumnWidth: function(col_index, width)
9887 // width = "md-2 xs-2..."
9888 if(!this.colModel.config[col_index]) {
9892 var w = width.split(" ");
9894 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9896 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9899 for(var j = 0; j < w.length; j++) {
9905 var size_cls = w[j].split("-");
9907 if(!Number.isInteger(size_cls[1] * 1)) {
9911 if(!this.colModel.config[col_index][size_cls[0]]) {
9915 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9919 h_row[0].classList.replace(
9920 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9921 "col-"+size_cls[0]+"-"+size_cls[1]
9924 for(var i = 0; i < rows.length; i++) {
9926 var size_cls = w[j].split("-");
9928 if(!Number.isInteger(size_cls[1] * 1)) {
9932 if(!this.colModel.config[col_index][size_cls[0]]) {
9936 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9940 rows[i].classList.replace(
9941 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9942 "col-"+size_cls[0]+"-"+size_cls[1]
9946 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9956 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9957 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9965 * @class Roo.bootstrap.TableCell
9966 * @extends Roo.bootstrap.Component
9967 * Bootstrap TableCell class
9968 * @cfg {String} html cell contain text
9969 * @cfg {String} cls cell class
9970 * @cfg {String} tag cell tag (td|th) default td
9971 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9972 * @cfg {String} align Aligns the content in a cell
9973 * @cfg {String} axis Categorizes cells
9974 * @cfg {String} bgcolor Specifies the background color of a cell
9975 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9976 * @cfg {Number} colspan Specifies the number of columns a cell should span
9977 * @cfg {String} headers Specifies one or more header cells a cell is related to
9978 * @cfg {Number} height Sets the height of a cell
9979 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9980 * @cfg {Number} rowspan Sets the number of rows a cell should span
9981 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9982 * @cfg {String} valign Vertical aligns the content in a cell
9983 * @cfg {Number} width Specifies the width of a cell
9986 * Create a new TableCell
9987 * @param {Object} config The config object
9990 Roo.bootstrap.TableCell = function(config){
9991 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9994 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10014 getAutoCreate : function(){
10015 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10022 cfg.tag = this.tag;
10035 cfg.align=this.align
10040 if (this.bgcolor) {
10041 cfg.bgcolor=this.bgcolor
10043 if (this.charoff) {
10044 cfg.charoff=this.charoff
10046 if (this.colspan) {
10047 cfg.colspan=this.colspan
10049 if (this.headers) {
10050 cfg.headers=this.headers
10053 cfg.height=this.height
10056 cfg.nowrap=this.nowrap
10058 if (this.rowspan) {
10059 cfg.rowspan=this.rowspan
10062 cfg.scope=this.scope
10065 cfg.valign=this.valign
10068 cfg.width=this.width
10087 * @class Roo.bootstrap.TableRow
10088 * @extends Roo.bootstrap.Component
10089 * Bootstrap TableRow class
10090 * @cfg {String} cls row class
10091 * @cfg {String} align Aligns the content in a table row
10092 * @cfg {String} bgcolor Specifies a background color for a table row
10093 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10094 * @cfg {String} valign Vertical aligns the content in a table row
10097 * Create a new TableRow
10098 * @param {Object} config The config object
10101 Roo.bootstrap.TableRow = function(config){
10102 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10105 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10113 getAutoCreate : function(){
10114 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10121 cfg.cls = this.cls;
10124 cfg.align = this.align;
10127 cfg.bgcolor = this.bgcolor;
10130 cfg.charoff = this.charoff;
10133 cfg.valign = this.valign;
10151 * @class Roo.bootstrap.TableBody
10152 * @extends Roo.bootstrap.Component
10153 * Bootstrap TableBody class
10154 * @cfg {String} cls element class
10155 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10156 * @cfg {String} align Aligns the content inside the element
10157 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10158 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10161 * Create a new TableBody
10162 * @param {Object} config The config object
10165 Roo.bootstrap.TableBody = function(config){
10166 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10169 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10177 getAutoCreate : function(){
10178 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10188 cfg.tag = this.tag;
10192 cfg.align = this.align;
10195 cfg.charoff = this.charoff;
10198 cfg.valign = this.valign;
10205 // initEvents : function()
10208 // if(!this.store){
10212 // this.store = Roo.factory(this.store, Roo.data);
10213 // this.store.on('load', this.onLoad, this);
10215 // this.store.load();
10219 // onLoad: function ()
10221 // this.fireEvent('load', this);
10231 * Ext JS Library 1.1.1
10232 * Copyright(c) 2006-2007, Ext JS, LLC.
10234 * Originally Released Under LGPL - original licence link has changed is not relivant.
10237 * <script type="text/javascript">
10240 // as we use this in bootstrap.
10241 Roo.namespace('Roo.form');
10243 * @class Roo.form.Action
10244 * Internal Class used to handle form actions
10246 * @param {Roo.form.BasicForm} el The form element or its id
10247 * @param {Object} config Configuration options
10252 // define the action interface
10253 Roo.form.Action = function(form, options){
10255 this.options = options || {};
10258 * Client Validation Failed
10261 Roo.form.Action.CLIENT_INVALID = 'client';
10263 * Server Validation Failed
10266 Roo.form.Action.SERVER_INVALID = 'server';
10268 * Connect to Server Failed
10271 Roo.form.Action.CONNECT_FAILURE = 'connect';
10273 * Reading Data from Server Failed
10276 Roo.form.Action.LOAD_FAILURE = 'load';
10278 Roo.form.Action.prototype = {
10280 failureType : undefined,
10281 response : undefined,
10282 result : undefined,
10284 // interface method
10285 run : function(options){
10289 // interface method
10290 success : function(response){
10294 // interface method
10295 handleResponse : function(response){
10299 // default connection failure
10300 failure : function(response){
10302 this.response = response;
10303 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10304 this.form.afterAction(this, false);
10307 processResponse : function(response){
10308 this.response = response;
10309 if(!response.responseText){
10312 this.result = this.handleResponse(response);
10313 return this.result;
10316 // utility functions used internally
10317 getUrl : function(appendParams){
10318 var url = this.options.url || this.form.url || this.form.el.dom.action;
10320 var p = this.getParams();
10322 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10328 getMethod : function(){
10329 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10332 getParams : function(){
10333 var bp = this.form.baseParams;
10334 var p = this.options.params;
10336 if(typeof p == "object"){
10337 p = Roo.urlEncode(Roo.applyIf(p, bp));
10338 }else if(typeof p == 'string' && bp){
10339 p += '&' + Roo.urlEncode(bp);
10342 p = Roo.urlEncode(bp);
10347 createCallback : function(){
10349 success: this.success,
10350 failure: this.failure,
10352 timeout: (this.form.timeout*1000),
10353 upload: this.form.fileUpload ? this.success : undefined
10358 Roo.form.Action.Submit = function(form, options){
10359 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10362 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10365 haveProgress : false,
10366 uploadComplete : false,
10368 // uploadProgress indicator.
10369 uploadProgress : function()
10371 if (!this.form.progressUrl) {
10375 if (!this.haveProgress) {
10376 Roo.MessageBox.progress("Uploading", "Uploading");
10378 if (this.uploadComplete) {
10379 Roo.MessageBox.hide();
10383 this.haveProgress = true;
10385 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10387 var c = new Roo.data.Connection();
10389 url : this.form.progressUrl,
10394 success : function(req){
10395 //console.log(data);
10399 rdata = Roo.decode(req.responseText)
10401 Roo.log("Invalid data from server..");
10405 if (!rdata || !rdata.success) {
10407 Roo.MessageBox.alert(Roo.encode(rdata));
10410 var data = rdata.data;
10412 if (this.uploadComplete) {
10413 Roo.MessageBox.hide();
10418 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10419 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10422 this.uploadProgress.defer(2000,this);
10425 failure: function(data) {
10426 Roo.log('progress url failed ');
10437 // run get Values on the form, so it syncs any secondary forms.
10438 this.form.getValues();
10440 var o = this.options;
10441 var method = this.getMethod();
10442 var isPost = method == 'POST';
10443 if(o.clientValidation === false || this.form.isValid()){
10445 if (this.form.progressUrl) {
10446 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10447 (new Date() * 1) + '' + Math.random());
10452 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10453 form:this.form.el.dom,
10454 url:this.getUrl(!isPost),
10456 params:isPost ? this.getParams() : null,
10457 isUpload: this.form.fileUpload,
10458 formData : this.form.formData
10461 this.uploadProgress();
10463 }else if (o.clientValidation !== false){ // client validation failed
10464 this.failureType = Roo.form.Action.CLIENT_INVALID;
10465 this.form.afterAction(this, false);
10469 success : function(response)
10471 this.uploadComplete= true;
10472 if (this.haveProgress) {
10473 Roo.MessageBox.hide();
10477 var result = this.processResponse(response);
10478 if(result === true || result.success){
10479 this.form.afterAction(this, true);
10483 this.form.markInvalid(result.errors);
10484 this.failureType = Roo.form.Action.SERVER_INVALID;
10486 this.form.afterAction(this, false);
10488 failure : function(response)
10490 this.uploadComplete= true;
10491 if (this.haveProgress) {
10492 Roo.MessageBox.hide();
10495 this.response = response;
10496 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10497 this.form.afterAction(this, false);
10500 handleResponse : function(response){
10501 if(this.form.errorReader){
10502 var rs = this.form.errorReader.read(response);
10505 for(var i = 0, len = rs.records.length; i < len; i++) {
10506 var r = rs.records[i];
10507 errors[i] = r.data;
10510 if(errors.length < 1){
10514 success : rs.success,
10520 ret = Roo.decode(response.responseText);
10524 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10534 Roo.form.Action.Load = function(form, options){
10535 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10536 this.reader = this.form.reader;
10539 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10544 Roo.Ajax.request(Roo.apply(
10545 this.createCallback(), {
10546 method:this.getMethod(),
10547 url:this.getUrl(false),
10548 params:this.getParams()
10552 success : function(response){
10554 var result = this.processResponse(response);
10555 if(result === true || !result.success || !result.data){
10556 this.failureType = Roo.form.Action.LOAD_FAILURE;
10557 this.form.afterAction(this, false);
10560 this.form.clearInvalid();
10561 this.form.setValues(result.data);
10562 this.form.afterAction(this, true);
10565 handleResponse : function(response){
10566 if(this.form.reader){
10567 var rs = this.form.reader.read(response);
10568 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10570 success : rs.success,
10574 return Roo.decode(response.responseText);
10578 Roo.form.Action.ACTION_TYPES = {
10579 'load' : Roo.form.Action.Load,
10580 'submit' : Roo.form.Action.Submit
10589 * @class Roo.bootstrap.Form
10590 * @extends Roo.bootstrap.Component
10591 * Bootstrap Form class
10592 * @cfg {String} method GET | POST (default POST)
10593 * @cfg {String} labelAlign top | left (default top)
10594 * @cfg {String} align left | right - for navbars
10595 * @cfg {Boolean} loadMask load mask when submit (default true)
10599 * Create a new Form
10600 * @param {Object} config The config object
10604 Roo.bootstrap.Form = function(config){
10606 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10608 Roo.bootstrap.Form.popover.apply();
10612 * @event clientvalidation
10613 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10614 * @param {Form} this
10615 * @param {Boolean} valid true if the form has passed client-side validation
10617 clientvalidation: true,
10619 * @event beforeaction
10620 * Fires before any action is performed. Return false to cancel the action.
10621 * @param {Form} this
10622 * @param {Action} action The action to be performed
10624 beforeaction: true,
10626 * @event actionfailed
10627 * Fires when an action fails.
10628 * @param {Form} this
10629 * @param {Action} action The action that failed
10631 actionfailed : true,
10633 * @event actioncomplete
10634 * Fires when an action is completed.
10635 * @param {Form} this
10636 * @param {Action} action The action that completed
10638 actioncomplete : true
10642 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10645 * @cfg {String} method
10646 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10650 * @cfg {String} url
10651 * The URL to use for form actions if one isn't supplied in the action options.
10654 * @cfg {Boolean} fileUpload
10655 * Set to true if this form is a file upload.
10659 * @cfg {Object} baseParams
10660 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10664 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10668 * @cfg {Sting} align (left|right) for navbar forms
10673 activeAction : null,
10676 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10677 * element by passing it or its id or mask the form itself by passing in true.
10680 waitMsgTarget : false,
10685 * @cfg {Boolean} errorMask (true|false) default false
10690 * @cfg {Number} maskOffset Default 100
10695 * @cfg {Boolean} maskBody
10699 getAutoCreate : function(){
10703 method : this.method || 'POST',
10704 id : this.id || Roo.id(),
10707 if (this.parent().xtype.match(/^Nav/)) {
10708 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10712 if (this.labelAlign == 'left' ) {
10713 cfg.cls += ' form-horizontal';
10719 initEvents : function()
10721 this.el.on('submit', this.onSubmit, this);
10722 // this was added as random key presses on the form where triggering form submit.
10723 this.el.on('keypress', function(e) {
10724 if (e.getCharCode() != 13) {
10727 // we might need to allow it for textareas.. and some other items.
10728 // check e.getTarget().
10730 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10734 Roo.log("keypress blocked");
10736 e.preventDefault();
10742 onSubmit : function(e){
10747 * Returns true if client-side validation on the form is successful.
10750 isValid : function(){
10751 var items = this.getItems();
10753 var target = false;
10755 items.each(function(f){
10761 Roo.log('invalid field: ' + f.name);
10765 if(!target && f.el.isVisible(true)){
10771 if(this.errorMask && !valid){
10772 Roo.bootstrap.Form.popover.mask(this, target);
10779 * Returns true if any fields in this form have changed since their original load.
10782 isDirty : function(){
10784 var items = this.getItems();
10785 items.each(function(f){
10795 * Performs a predefined action (submit or load) or custom actions you define on this form.
10796 * @param {String} actionName The name of the action type
10797 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10798 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10799 * accept other config options):
10801 Property Type Description
10802 ---------------- --------------- ----------------------------------------------------------------------------------
10803 url String The url for the action (defaults to the form's url)
10804 method String The form method to use (defaults to the form's method, or POST if not defined)
10805 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10806 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10807 validate the form on the client (defaults to false)
10809 * @return {BasicForm} this
10811 doAction : function(action, options){
10812 if(typeof action == 'string'){
10813 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10815 if(this.fireEvent('beforeaction', this, action) !== false){
10816 this.beforeAction(action);
10817 action.run.defer(100, action);
10823 beforeAction : function(action){
10824 var o = action.options;
10829 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10831 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10834 // not really supported yet.. ??
10836 //if(this.waitMsgTarget === true){
10837 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10838 //}else if(this.waitMsgTarget){
10839 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10840 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10842 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10848 afterAction : function(action, success){
10849 this.activeAction = null;
10850 var o = action.options;
10855 Roo.get(document.body).unmask();
10861 //if(this.waitMsgTarget === true){
10862 // this.el.unmask();
10863 //}else if(this.waitMsgTarget){
10864 // this.waitMsgTarget.unmask();
10866 // Roo.MessageBox.updateProgress(1);
10867 // Roo.MessageBox.hide();
10874 Roo.callback(o.success, o.scope, [this, action]);
10875 this.fireEvent('actioncomplete', this, action);
10879 // failure condition..
10880 // we have a scenario where updates need confirming.
10881 // eg. if a locking scenario exists..
10882 // we look for { errors : { needs_confirm : true }} in the response.
10884 (typeof(action.result) != 'undefined') &&
10885 (typeof(action.result.errors) != 'undefined') &&
10886 (typeof(action.result.errors.needs_confirm) != 'undefined')
10889 Roo.log("not supported yet");
10892 Roo.MessageBox.confirm(
10893 "Change requires confirmation",
10894 action.result.errorMsg,
10899 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10909 Roo.callback(o.failure, o.scope, [this, action]);
10910 // show an error message if no failed handler is set..
10911 if (!this.hasListener('actionfailed')) {
10912 Roo.log("need to add dialog support");
10914 Roo.MessageBox.alert("Error",
10915 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10916 action.result.errorMsg :
10917 "Saving Failed, please check your entries or try again"
10922 this.fireEvent('actionfailed', this, action);
10927 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10928 * @param {String} id The value to search for
10931 findField : function(id){
10932 var items = this.getItems();
10933 var field = items.get(id);
10935 items.each(function(f){
10936 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10943 return field || null;
10946 * Mark fields in this form invalid in bulk.
10947 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10948 * @return {BasicForm} this
10950 markInvalid : function(errors){
10951 if(errors instanceof Array){
10952 for(var i = 0, len = errors.length; i < len; i++){
10953 var fieldError = errors[i];
10954 var f = this.findField(fieldError.id);
10956 f.markInvalid(fieldError.msg);
10962 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10963 field.markInvalid(errors[id]);
10967 //Roo.each(this.childForms || [], function (f) {
10968 // f.markInvalid(errors);
10975 * Set values for fields in this form in bulk.
10976 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10977 * @return {BasicForm} this
10979 setValues : function(values){
10980 if(values instanceof Array){ // array of objects
10981 for(var i = 0, len = values.length; i < len; i++){
10983 var f = this.findField(v.id);
10985 f.setValue(v.value);
10986 if(this.trackResetOnLoad){
10987 f.originalValue = f.getValue();
10991 }else{ // object hash
10994 if(typeof values[id] != 'function' && (field = this.findField(id))){
10996 if (field.setFromData &&
10997 field.valueField &&
10998 field.displayField &&
10999 // combos' with local stores can
11000 // be queried via setValue()
11001 // to set their value..
11002 (field.store && !field.store.isLocal)
11006 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11007 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11008 field.setFromData(sd);
11010 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11012 field.setFromData(values);
11015 field.setValue(values[id]);
11019 if(this.trackResetOnLoad){
11020 field.originalValue = field.getValue();
11026 //Roo.each(this.childForms || [], function (f) {
11027 // f.setValues(values);
11034 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11035 * they are returned as an array.
11036 * @param {Boolean} asString
11039 getValues : function(asString){
11040 //if (this.childForms) {
11041 // copy values from the child forms
11042 // Roo.each(this.childForms, function (f) {
11043 // this.setValues(f.getValues());
11049 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11050 if(asString === true){
11053 return Roo.urlDecode(fs);
11057 * Returns the fields in this form as an object with key/value pairs.
11058 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11061 getFieldValues : function(with_hidden)
11063 var items = this.getItems();
11065 items.each(function(f){
11067 if (!f.getName()) {
11071 var v = f.getValue();
11073 if (f.inputType =='radio') {
11074 if (typeof(ret[f.getName()]) == 'undefined') {
11075 ret[f.getName()] = ''; // empty..
11078 if (!f.el.dom.checked) {
11082 v = f.el.dom.value;
11086 if(f.xtype == 'MoneyField'){
11087 ret[f.currencyName] = f.getCurrency();
11090 // not sure if this supported any more..
11091 if ((typeof(v) == 'object') && f.getRawValue) {
11092 v = f.getRawValue() ; // dates..
11094 // combo boxes where name != hiddenName...
11095 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11096 ret[f.name] = f.getRawValue();
11098 ret[f.getName()] = v;
11105 * Clears all invalid messages in this form.
11106 * @return {BasicForm} this
11108 clearInvalid : function(){
11109 var items = this.getItems();
11111 items.each(function(f){
11119 * Resets this form.
11120 * @return {BasicForm} this
11122 reset : function(){
11123 var items = this.getItems();
11124 items.each(function(f){
11128 Roo.each(this.childForms || [], function (f) {
11136 getItems : function()
11138 var r=new Roo.util.MixedCollection(false, function(o){
11139 return o.id || (o.id = Roo.id());
11141 var iter = function(el) {
11148 Roo.each(el.items,function(e) {
11157 hideFields : function(items)
11159 Roo.each(items, function(i){
11161 var f = this.findField(i);
11172 showFields : function(items)
11174 Roo.each(items, function(i){
11176 var f = this.findField(i);
11189 Roo.apply(Roo.bootstrap.Form, {
11205 intervalID : false,
11211 if(this.isApplied){
11216 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11217 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11218 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11219 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11222 this.maskEl.top.enableDisplayMode("block");
11223 this.maskEl.left.enableDisplayMode("block");
11224 this.maskEl.bottom.enableDisplayMode("block");
11225 this.maskEl.right.enableDisplayMode("block");
11227 this.toolTip = new Roo.bootstrap.Tooltip({
11228 cls : 'roo-form-error-popover',
11230 'left' : ['r-l', [-2,0], 'right'],
11231 'right' : ['l-r', [2,0], 'left'],
11232 'bottom' : ['tl-bl', [0,2], 'top'],
11233 'top' : [ 'bl-tl', [0,-2], 'bottom']
11237 this.toolTip.render(Roo.get(document.body));
11239 this.toolTip.el.enableDisplayMode("block");
11241 Roo.get(document.body).on('click', function(){
11245 Roo.get(document.body).on('touchstart', function(){
11249 this.isApplied = true
11252 mask : function(form, target)
11256 this.target = target;
11258 if(!this.form.errorMask || !target.el){
11262 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11264 Roo.log(scrollable);
11266 var ot = this.target.el.calcOffsetsTo(scrollable);
11268 var scrollTo = ot[1] - this.form.maskOffset;
11270 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11272 scrollable.scrollTo('top', scrollTo);
11274 var box = this.target.el.getBox();
11276 var zIndex = Roo.bootstrap.Modal.zIndex++;
11279 this.maskEl.top.setStyle('position', 'absolute');
11280 this.maskEl.top.setStyle('z-index', zIndex);
11281 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11282 this.maskEl.top.setLeft(0);
11283 this.maskEl.top.setTop(0);
11284 this.maskEl.top.show();
11286 this.maskEl.left.setStyle('position', 'absolute');
11287 this.maskEl.left.setStyle('z-index', zIndex);
11288 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11289 this.maskEl.left.setLeft(0);
11290 this.maskEl.left.setTop(box.y - this.padding);
11291 this.maskEl.left.show();
11293 this.maskEl.bottom.setStyle('position', 'absolute');
11294 this.maskEl.bottom.setStyle('z-index', zIndex);
11295 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11296 this.maskEl.bottom.setLeft(0);
11297 this.maskEl.bottom.setTop(box.bottom + this.padding);
11298 this.maskEl.bottom.show();
11300 this.maskEl.right.setStyle('position', 'absolute');
11301 this.maskEl.right.setStyle('z-index', zIndex);
11302 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11303 this.maskEl.right.setLeft(box.right + this.padding);
11304 this.maskEl.right.setTop(box.y - this.padding);
11305 this.maskEl.right.show();
11307 this.toolTip.bindEl = this.target.el;
11309 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11311 var tip = this.target.blankText;
11313 if(this.target.getValue() !== '' ) {
11315 if (this.target.invalidText.length) {
11316 tip = this.target.invalidText;
11317 } else if (this.target.regexText.length){
11318 tip = this.target.regexText;
11322 this.toolTip.show(tip);
11324 this.intervalID = window.setInterval(function() {
11325 Roo.bootstrap.Form.popover.unmask();
11328 window.onwheel = function(){ return false;};
11330 (function(){ this.isMasked = true; }).defer(500, this);
11334 unmask : function()
11336 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11340 this.maskEl.top.setStyle('position', 'absolute');
11341 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11342 this.maskEl.top.hide();
11344 this.maskEl.left.setStyle('position', 'absolute');
11345 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11346 this.maskEl.left.hide();
11348 this.maskEl.bottom.setStyle('position', 'absolute');
11349 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11350 this.maskEl.bottom.hide();
11352 this.maskEl.right.setStyle('position', 'absolute');
11353 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11354 this.maskEl.right.hide();
11356 this.toolTip.hide();
11358 this.toolTip.el.hide();
11360 window.onwheel = function(){ return true;};
11362 if(this.intervalID){
11363 window.clearInterval(this.intervalID);
11364 this.intervalID = false;
11367 this.isMasked = false;
11377 * Ext JS Library 1.1.1
11378 * Copyright(c) 2006-2007, Ext JS, LLC.
11380 * Originally Released Under LGPL - original licence link has changed is not relivant.
11383 * <script type="text/javascript">
11386 * @class Roo.form.VTypes
11387 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11390 Roo.form.VTypes = function(){
11391 // closure these in so they are only created once.
11392 var alpha = /^[a-zA-Z_]+$/;
11393 var alphanum = /^[a-zA-Z0-9_]+$/;
11394 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11395 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11397 // All these messages and functions are configurable
11400 * The function used to validate email addresses
11401 * @param {String} value The email address
11403 'email' : function(v){
11404 return email.test(v);
11407 * The error text to display when the email validation function returns false
11410 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11412 * The keystroke filter mask to be applied on email input
11415 'emailMask' : /[a-z0-9_\.\-@]/i,
11418 * The function used to validate URLs
11419 * @param {String} value The URL
11421 'url' : function(v){
11422 return url.test(v);
11425 * The error text to display when the url validation function returns false
11428 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11431 * The function used to validate alpha values
11432 * @param {String} value The value
11434 'alpha' : function(v){
11435 return alpha.test(v);
11438 * The error text to display when the alpha validation function returns false
11441 'alphaText' : 'This field should only contain letters and _',
11443 * The keystroke filter mask to be applied on alpha input
11446 'alphaMask' : /[a-z_]/i,
11449 * The function used to validate alphanumeric values
11450 * @param {String} value The value
11452 'alphanum' : function(v){
11453 return alphanum.test(v);
11456 * The error text to display when the alphanumeric validation function returns false
11459 'alphanumText' : 'This field should only contain letters, numbers and _',
11461 * The keystroke filter mask to be applied on alphanumeric input
11464 'alphanumMask' : /[a-z0-9_]/i
11474 * @class Roo.bootstrap.Input
11475 * @extends Roo.bootstrap.Component
11476 * Bootstrap Input class
11477 * @cfg {Boolean} disabled is it disabled
11478 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11479 * @cfg {String} name name of the input
11480 * @cfg {string} fieldLabel - the label associated
11481 * @cfg {string} placeholder - placeholder to put in text.
11482 * @cfg {string} before - input group add on before
11483 * @cfg {string} after - input group add on after
11484 * @cfg {string} size - (lg|sm) or leave empty..
11485 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11486 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11487 * @cfg {Number} md colspan out of 12 for computer-sized screens
11488 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11489 * @cfg {string} value default value of the input
11490 * @cfg {Number} labelWidth set the width of label
11491 * @cfg {Number} labellg set the width of label (1-12)
11492 * @cfg {Number} labelmd set the width of label (1-12)
11493 * @cfg {Number} labelsm set the width of label (1-12)
11494 * @cfg {Number} labelxs set the width of label (1-12)
11495 * @cfg {String} labelAlign (top|left)
11496 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11497 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11498 * @cfg {String} indicatorpos (left|right) default left
11499 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11500 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11501 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11503 * @cfg {String} align (left|center|right) Default left
11504 * @cfg {Boolean} forceFeedback (true|false) Default false
11507 * Create a new Input
11508 * @param {Object} config The config object
11511 Roo.bootstrap.Input = function(config){
11513 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11518 * Fires when this field receives input focus.
11519 * @param {Roo.form.Field} this
11524 * Fires when this field loses input focus.
11525 * @param {Roo.form.Field} this
11529 * @event specialkey
11530 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11531 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11532 * @param {Roo.form.Field} this
11533 * @param {Roo.EventObject} e The event object
11538 * Fires just before the field blurs if the field value has changed.
11539 * @param {Roo.form.Field} this
11540 * @param {Mixed} newValue The new value
11541 * @param {Mixed} oldValue The original value
11546 * Fires after the field has been marked as invalid.
11547 * @param {Roo.form.Field} this
11548 * @param {String} msg The validation message
11553 * Fires after the field has been validated with no errors.
11554 * @param {Roo.form.Field} this
11559 * Fires after the key up
11560 * @param {Roo.form.Field} this
11561 * @param {Roo.EventObject} e The event Object
11566 * Fires after the user pastes into input
11567 * @param {Roo.form.Field} this
11568 * @param {Roo.EventObject} e The event Object
11574 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11576 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11577 automatic validation (defaults to "keyup").
11579 validationEvent : "keyup",
11581 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11583 validateOnBlur : true,
11585 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11587 validationDelay : 250,
11589 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11591 focusClass : "x-form-focus", // not needed???
11595 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11597 invalidClass : "has-warning",
11600 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11602 validClass : "has-success",
11605 * @cfg {Boolean} hasFeedback (true|false) default true
11607 hasFeedback : true,
11610 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11612 invalidFeedbackClass : "glyphicon-warning-sign",
11615 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11617 validFeedbackClass : "glyphicon-ok",
11620 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11622 selectOnFocus : false,
11625 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11629 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11634 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11636 disableKeyFilter : false,
11639 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11643 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11647 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11649 blankText : "Please complete this mandatory field",
11652 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11656 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11658 maxLength : Number.MAX_VALUE,
11660 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11662 minLengthText : "The minimum length for this field is {0}",
11664 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11666 maxLengthText : "The maximum length for this field is {0}",
11670 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11671 * If available, this function will be called only after the basic validators all return true, and will be passed the
11672 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11676 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11677 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11678 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11682 * @cfg {String} regexText -- Depricated - use Invalid Text
11687 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11693 autocomplete: false,
11697 inputType : 'text',
11700 placeholder: false,
11705 preventMark: false,
11706 isFormField : true,
11709 labelAlign : false,
11712 formatedValue : false,
11713 forceFeedback : false,
11715 indicatorpos : 'left',
11725 parentLabelAlign : function()
11728 while (parent.parent()) {
11729 parent = parent.parent();
11730 if (typeof(parent.labelAlign) !='undefined') {
11731 return parent.labelAlign;
11738 getAutoCreate : function()
11740 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11746 if(this.inputType != 'hidden'){
11747 cfg.cls = 'form-group' //input-group
11753 type : this.inputType,
11754 value : this.value,
11755 cls : 'form-control',
11756 placeholder : this.placeholder || '',
11757 autocomplete : this.autocomplete || 'new-password'
11759 if (this.inputType == 'file') {
11760 input.style = 'overflow:hidden'; // why not in CSS?
11763 if(this.capture.length){
11764 input.capture = this.capture;
11767 if(this.accept.length){
11768 input.accept = this.accept + "/*";
11772 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11775 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11776 input.maxLength = this.maxLength;
11779 if (this.disabled) {
11780 input.disabled=true;
11783 if (this.readOnly) {
11784 input.readonly=true;
11788 input.name = this.name;
11792 input.cls += ' input-' + this.size;
11796 ['xs','sm','md','lg'].map(function(size){
11797 if (settings[size]) {
11798 cfg.cls += ' col-' + size + '-' + settings[size];
11802 var inputblock = input;
11806 cls: 'glyphicon form-control-feedback'
11809 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11812 cls : 'has-feedback',
11820 if (this.before || this.after) {
11823 cls : 'input-group',
11827 if (this.before && typeof(this.before) == 'string') {
11829 inputblock.cn.push({
11831 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11835 if (this.before && typeof(this.before) == 'object') {
11836 this.before = Roo.factory(this.before);
11838 inputblock.cn.push({
11840 cls : 'roo-input-before input-group-prepend input-group-' +
11841 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11845 inputblock.cn.push(input);
11847 if (this.after && typeof(this.after) == 'string') {
11848 inputblock.cn.push({
11850 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11854 if (this.after && typeof(this.after) == 'object') {
11855 this.after = Roo.factory(this.after);
11857 inputblock.cn.push({
11859 cls : 'roo-input-after input-group-append input-group-' +
11860 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11864 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11865 inputblock.cls += ' has-feedback';
11866 inputblock.cn.push(feedback);
11871 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11872 tooltip : 'This field is required'
11874 if (this.allowBlank ) {
11875 indicator.style = this.allowBlank ? ' display:none' : '';
11877 if (align ==='left' && this.fieldLabel.length) {
11879 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11886 cls : 'control-label col-form-label',
11887 html : this.fieldLabel
11898 var labelCfg = cfg.cn[1];
11899 var contentCfg = cfg.cn[2];
11901 if(this.indicatorpos == 'right'){
11906 cls : 'control-label col-form-label',
11910 html : this.fieldLabel
11924 labelCfg = cfg.cn[0];
11925 contentCfg = cfg.cn[1];
11929 if(this.labelWidth > 12){
11930 labelCfg.style = "width: " + this.labelWidth + 'px';
11933 if(this.labelWidth < 13 && this.labelmd == 0){
11934 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11937 if(this.labellg > 0){
11938 labelCfg.cls += ' col-lg-' + this.labellg;
11939 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11942 if(this.labelmd > 0){
11943 labelCfg.cls += ' col-md-' + this.labelmd;
11944 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11947 if(this.labelsm > 0){
11948 labelCfg.cls += ' col-sm-' + this.labelsm;
11949 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11952 if(this.labelxs > 0){
11953 labelCfg.cls += ' col-xs-' + this.labelxs;
11954 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11958 } else if ( this.fieldLabel.length) {
11965 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11966 tooltip : 'This field is required',
11967 style : this.allowBlank ? ' display:none' : ''
11971 //cls : 'input-group-addon',
11972 html : this.fieldLabel
11980 if(this.indicatorpos == 'right'){
11985 //cls : 'input-group-addon',
11986 html : this.fieldLabel
11991 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11992 tooltip : 'This field is required',
11993 style : this.allowBlank ? ' display:none' : ''
12013 if (this.parentType === 'Navbar' && this.parent().bar) {
12014 cfg.cls += ' navbar-form';
12017 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12018 // on BS4 we do this only if not form
12019 cfg.cls += ' navbar-form';
12027 * return the real input element.
12029 inputEl: function ()
12031 return this.el.select('input.form-control',true).first();
12034 tooltipEl : function()
12036 return this.inputEl();
12039 indicatorEl : function()
12041 if (Roo.bootstrap.version == 4) {
12042 return false; // not enabled in v4 yet.
12045 var indicator = this.el.select('i.roo-required-indicator',true).first();
12055 setDisabled : function(v)
12057 var i = this.inputEl().dom;
12059 i.removeAttribute('disabled');
12063 i.setAttribute('disabled','true');
12065 initEvents : function()
12068 this.inputEl().on("keydown" , this.fireKey, this);
12069 this.inputEl().on("focus", this.onFocus, this);
12070 this.inputEl().on("blur", this.onBlur, this);
12072 this.inputEl().relayEvent('keyup', this);
12073 this.inputEl().relayEvent('paste', this);
12075 this.indicator = this.indicatorEl();
12077 if(this.indicator){
12078 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12081 // reference to original value for reset
12082 this.originalValue = this.getValue();
12083 //Roo.form.TextField.superclass.initEvents.call(this);
12084 if(this.validationEvent == 'keyup'){
12085 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12086 this.inputEl().on('keyup', this.filterValidation, this);
12088 else if(this.validationEvent !== false){
12089 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12092 if(this.selectOnFocus){
12093 this.on("focus", this.preFocus, this);
12096 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12097 this.inputEl().on("keypress", this.filterKeys, this);
12099 this.inputEl().relayEvent('keypress', this);
12102 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12103 this.el.on("click", this.autoSize, this);
12106 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12107 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12110 if (typeof(this.before) == 'object') {
12111 this.before.render(this.el.select('.roo-input-before',true).first());
12113 if (typeof(this.after) == 'object') {
12114 this.after.render(this.el.select('.roo-input-after',true).first());
12117 this.inputEl().on('change', this.onChange, this);
12120 filterValidation : function(e){
12121 if(!e.isNavKeyPress()){
12122 this.validationTask.delay(this.validationDelay);
12126 * Validates the field value
12127 * @return {Boolean} True if the value is valid, else false
12129 validate : function(){
12130 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12131 if(this.disabled || this.validateValue(this.getRawValue())){
12136 this.markInvalid();
12142 * Validates a value according to the field's validation rules and marks the field as invalid
12143 * if the validation fails
12144 * @param {Mixed} value The value to validate
12145 * @return {Boolean} True if the value is valid, else false
12147 validateValue : function(value)
12149 if(this.getVisibilityEl().hasClass('hidden')){
12153 if(value.length < 1) { // if it's blank
12154 if(this.allowBlank){
12160 if(value.length < this.minLength){
12163 if(value.length > this.maxLength){
12167 var vt = Roo.form.VTypes;
12168 if(!vt[this.vtype](value, this)){
12172 if(typeof this.validator == "function"){
12173 var msg = this.validator(value);
12177 if (typeof(msg) == 'string') {
12178 this.invalidText = msg;
12182 if(this.regex && !this.regex.test(value)){
12190 fireKey : function(e){
12191 //Roo.log('field ' + e.getKey());
12192 if(e.isNavKeyPress()){
12193 this.fireEvent("specialkey", this, e);
12196 focus : function (selectText){
12198 this.inputEl().focus();
12199 if(selectText === true){
12200 this.inputEl().dom.select();
12206 onFocus : function(){
12207 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12208 // this.el.addClass(this.focusClass);
12210 if(!this.hasFocus){
12211 this.hasFocus = true;
12212 this.startValue = this.getValue();
12213 this.fireEvent("focus", this);
12217 beforeBlur : Roo.emptyFn,
12221 onBlur : function(){
12223 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12224 //this.el.removeClass(this.focusClass);
12226 this.hasFocus = false;
12227 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12230 var v = this.getValue();
12231 if(String(v) !== String(this.startValue)){
12232 this.fireEvent('change', this, v, this.startValue);
12234 this.fireEvent("blur", this);
12237 onChange : function(e)
12239 var v = this.getValue();
12240 if(String(v) !== String(this.startValue)){
12241 this.fireEvent('change', this, v, this.startValue);
12247 * Resets the current field value to the originally loaded value and clears any validation messages
12249 reset : function(){
12250 this.setValue(this.originalValue);
12254 * Returns the name of the field
12255 * @return {Mixed} name The name field
12257 getName: function(){
12261 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12262 * @return {Mixed} value The field value
12264 getValue : function(){
12266 var v = this.inputEl().getValue();
12271 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12272 * @return {Mixed} value The field value
12274 getRawValue : function(){
12275 var v = this.inputEl().getValue();
12281 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12282 * @param {Mixed} value The value to set
12284 setRawValue : function(v){
12285 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12288 selectText : function(start, end){
12289 var v = this.getRawValue();
12291 start = start === undefined ? 0 : start;
12292 end = end === undefined ? v.length : end;
12293 var d = this.inputEl().dom;
12294 if(d.setSelectionRange){
12295 d.setSelectionRange(start, end);
12296 }else if(d.createTextRange){
12297 var range = d.createTextRange();
12298 range.moveStart("character", start);
12299 range.moveEnd("character", v.length-end);
12306 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12307 * @param {Mixed} value The value to set
12309 setValue : function(v){
12312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12318 processValue : function(value){
12319 if(this.stripCharsRe){
12320 var newValue = value.replace(this.stripCharsRe, '');
12321 if(newValue !== value){
12322 this.setRawValue(newValue);
12329 preFocus : function(){
12331 if(this.selectOnFocus){
12332 this.inputEl().dom.select();
12335 filterKeys : function(e){
12336 var k = e.getKey();
12337 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12340 var c = e.getCharCode(), cc = String.fromCharCode(c);
12341 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12344 if(!this.maskRe.test(cc)){
12349 * Clear any invalid styles/messages for this field
12351 clearInvalid : function(){
12353 if(!this.el || this.preventMark){ // not rendered
12358 this.el.removeClass([this.invalidClass, 'is-invalid']);
12360 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12362 var feedback = this.el.select('.form-control-feedback', true).first();
12365 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12370 if(this.indicator){
12371 this.indicator.removeClass('visible');
12372 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12375 this.fireEvent('valid', this);
12379 * Mark this field as valid
12381 markValid : function()
12383 if(!this.el || this.preventMark){ // not rendered...
12387 this.el.removeClass([this.invalidClass, this.validClass]);
12388 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12390 var feedback = this.el.select('.form-control-feedback', true).first();
12393 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12396 if(this.indicator){
12397 this.indicator.removeClass('visible');
12398 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12406 if(this.allowBlank && !this.getRawValue().length){
12409 if (Roo.bootstrap.version == 3) {
12410 this.el.addClass(this.validClass);
12412 this.inputEl().addClass('is-valid');
12415 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12417 var feedback = this.el.select('.form-control-feedback', true).first();
12420 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12421 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12426 this.fireEvent('valid', this);
12430 * Mark this field as invalid
12431 * @param {String} msg The validation message
12433 markInvalid : function(msg)
12435 if(!this.el || this.preventMark){ // not rendered
12439 this.el.removeClass([this.invalidClass, this.validClass]);
12440 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12442 var feedback = this.el.select('.form-control-feedback', true).first();
12445 this.el.select('.form-control-feedback', true).first().removeClass(
12446 [this.invalidFeedbackClass, this.validFeedbackClass]);
12453 if(this.allowBlank && !this.getRawValue().length){
12457 if(this.indicator){
12458 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12459 this.indicator.addClass('visible');
12461 if (Roo.bootstrap.version == 3) {
12462 this.el.addClass(this.invalidClass);
12464 this.inputEl().addClass('is-invalid');
12469 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12471 var feedback = this.el.select('.form-control-feedback', true).first();
12474 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12476 if(this.getValue().length || this.forceFeedback){
12477 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12484 this.fireEvent('invalid', this, msg);
12487 SafariOnKeyDown : function(event)
12489 // this is a workaround for a password hang bug on chrome/ webkit.
12490 if (this.inputEl().dom.type != 'password') {
12494 var isSelectAll = false;
12496 if(this.inputEl().dom.selectionEnd > 0){
12497 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12499 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12500 event.preventDefault();
12505 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12507 event.preventDefault();
12508 // this is very hacky as keydown always get's upper case.
12510 var cc = String.fromCharCode(event.getCharCode());
12511 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12515 adjustWidth : function(tag, w){
12516 tag = tag.toLowerCase();
12517 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12518 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12519 if(tag == 'input'){
12522 if(tag == 'textarea'){
12525 }else if(Roo.isOpera){
12526 if(tag == 'input'){
12529 if(tag == 'textarea'){
12537 setFieldLabel : function(v)
12539 if(!this.rendered){
12543 if(this.indicatorEl()){
12544 var ar = this.el.select('label > span',true);
12546 if (ar.elements.length) {
12547 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12548 this.fieldLabel = v;
12552 var br = this.el.select('label',true);
12554 if(br.elements.length) {
12555 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12556 this.fieldLabel = v;
12560 Roo.log('Cannot Found any of label > span || label in input');
12564 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12565 this.fieldLabel = v;
12580 * @class Roo.bootstrap.TextArea
12581 * @extends Roo.bootstrap.Input
12582 * Bootstrap TextArea class
12583 * @cfg {Number} cols Specifies the visible width of a text area
12584 * @cfg {Number} rows Specifies the visible number of lines in a text area
12585 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12586 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12587 * @cfg {string} html text
12590 * Create a new TextArea
12591 * @param {Object} config The config object
12594 Roo.bootstrap.TextArea = function(config){
12595 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12599 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12609 getAutoCreate : function(){
12611 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12617 if(this.inputType != 'hidden'){
12618 cfg.cls = 'form-group' //input-group
12626 value : this.value || '',
12627 html: this.html || '',
12628 cls : 'form-control',
12629 placeholder : this.placeholder || ''
12633 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12634 input.maxLength = this.maxLength;
12638 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12642 input.cols = this.cols;
12645 if (this.readOnly) {
12646 input.readonly = true;
12650 input.name = this.name;
12654 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12658 ['xs','sm','md','lg'].map(function(size){
12659 if (settings[size]) {
12660 cfg.cls += ' col-' + size + '-' + settings[size];
12664 var inputblock = input;
12666 if(this.hasFeedback && !this.allowBlank){
12670 cls: 'glyphicon form-control-feedback'
12674 cls : 'has-feedback',
12683 if (this.before || this.after) {
12686 cls : 'input-group',
12690 inputblock.cn.push({
12692 cls : 'input-group-addon',
12697 inputblock.cn.push(input);
12699 if(this.hasFeedback && !this.allowBlank){
12700 inputblock.cls += ' has-feedback';
12701 inputblock.cn.push(feedback);
12705 inputblock.cn.push({
12707 cls : 'input-group-addon',
12714 if (align ==='left' && this.fieldLabel.length) {
12719 cls : 'control-label',
12720 html : this.fieldLabel
12731 if(this.labelWidth > 12){
12732 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12735 if(this.labelWidth < 13 && this.labelmd == 0){
12736 this.labelmd = this.labelWidth;
12739 if(this.labellg > 0){
12740 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12741 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12744 if(this.labelmd > 0){
12745 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12746 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12749 if(this.labelsm > 0){
12750 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12751 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12754 if(this.labelxs > 0){
12755 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12756 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12759 } else if ( this.fieldLabel.length) {
12764 //cls : 'input-group-addon',
12765 html : this.fieldLabel
12783 if (this.disabled) {
12784 input.disabled=true;
12791 * return the real textarea element.
12793 inputEl: function ()
12795 return this.el.select('textarea.form-control',true).first();
12799 * Clear any invalid styles/messages for this field
12801 clearInvalid : function()
12804 if(!this.el || this.preventMark){ // not rendered
12808 var label = this.el.select('label', true).first();
12809 var icon = this.el.select('i.fa-star', true).first();
12814 this.el.removeClass( this.validClass);
12815 this.inputEl().removeClass('is-invalid');
12817 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12819 var feedback = this.el.select('.form-control-feedback', true).first();
12822 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12827 this.fireEvent('valid', this);
12831 * Mark this field as valid
12833 markValid : function()
12835 if(!this.el || this.preventMark){ // not rendered
12839 this.el.removeClass([this.invalidClass, this.validClass]);
12840 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12842 var feedback = this.el.select('.form-control-feedback', true).first();
12845 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12848 if(this.disabled || this.allowBlank){
12852 var label = this.el.select('label', true).first();
12853 var icon = this.el.select('i.fa-star', true).first();
12858 if (Roo.bootstrap.version == 3) {
12859 this.el.addClass(this.validClass);
12861 this.inputEl().addClass('is-valid');
12865 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12867 var feedback = this.el.select('.form-control-feedback', true).first();
12870 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12871 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12876 this.fireEvent('valid', this);
12880 * Mark this field as invalid
12881 * @param {String} msg The validation message
12883 markInvalid : function(msg)
12885 if(!this.el || this.preventMark){ // not rendered
12889 this.el.removeClass([this.invalidClass, this.validClass]);
12890 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12892 var feedback = this.el.select('.form-control-feedback', true).first();
12895 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12898 if(this.disabled || this.allowBlank){
12902 var label = this.el.select('label', true).first();
12903 var icon = this.el.select('i.fa-star', true).first();
12905 if(!this.getValue().length && label && !icon){
12906 this.el.createChild({
12908 cls : 'text-danger fa fa-lg fa-star',
12909 tooltip : 'This field is required',
12910 style : 'margin-right:5px;'
12914 if (Roo.bootstrap.version == 3) {
12915 this.el.addClass(this.invalidClass);
12917 this.inputEl().addClass('is-invalid');
12920 // fixme ... this may be depricated need to test..
12921 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12923 var feedback = this.el.select('.form-control-feedback', true).first();
12926 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12928 if(this.getValue().length || this.forceFeedback){
12929 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12936 this.fireEvent('invalid', this, msg);
12944 * trigger field - base class for combo..
12949 * @class Roo.bootstrap.TriggerField
12950 * @extends Roo.bootstrap.Input
12951 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12952 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12953 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12954 * for which you can provide a custom implementation. For example:
12956 var trigger = new Roo.bootstrap.TriggerField();
12957 trigger.onTriggerClick = myTriggerFn;
12958 trigger.applyTo('my-field');
12961 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12962 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12963 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12964 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12965 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12968 * Create a new TriggerField.
12969 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12970 * to the base TextField)
12972 Roo.bootstrap.TriggerField = function(config){
12973 this.mimicing = false;
12974 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12979 * @cfg {String} triggerClass A CSS class to apply to the trigger
12982 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12987 * @cfg {Boolean} removable (true|false) special filter default false
12991 /** @cfg {Boolean} grow @hide */
12992 /** @cfg {Number} growMin @hide */
12993 /** @cfg {Number} growMax @hide */
12999 autoSize: Roo.emptyFn,
13003 deferHeight : true,
13006 actionMode : 'wrap',
13011 getAutoCreate : function(){
13013 var align = this.labelAlign || this.parentLabelAlign();
13018 cls: 'form-group' //input-group
13025 type : this.inputType,
13026 cls : 'form-control',
13027 autocomplete: 'new-password',
13028 placeholder : this.placeholder || ''
13032 input.name = this.name;
13035 input.cls += ' input-' + this.size;
13038 if (this.disabled) {
13039 input.disabled=true;
13042 var inputblock = input;
13044 if(this.hasFeedback && !this.allowBlank){
13048 cls: 'glyphicon form-control-feedback'
13051 if(this.removable && !this.editable ){
13053 cls : 'has-feedback',
13059 cls : 'roo-combo-removable-btn close'
13066 cls : 'has-feedback',
13075 if(this.removable && !this.editable ){
13077 cls : 'roo-removable',
13083 cls : 'roo-combo-removable-btn close'
13090 if (this.before || this.after) {
13093 cls : 'input-group',
13097 inputblock.cn.push({
13099 cls : 'input-group-addon input-group-prepend input-group-text',
13104 inputblock.cn.push(input);
13106 if(this.hasFeedback && !this.allowBlank){
13107 inputblock.cls += ' has-feedback';
13108 inputblock.cn.push(feedback);
13112 inputblock.cn.push({
13114 cls : 'input-group-addon input-group-append input-group-text',
13123 var ibwrap = inputblock;
13128 cls: 'roo-select2-choices',
13132 cls: 'roo-select2-search-field',
13144 cls: 'roo-select2-container input-group',
13149 cls: 'form-hidden-field'
13155 if(!this.multiple && this.showToggleBtn){
13161 if (this.caret != false) {
13164 cls: 'fa fa-' + this.caret
13171 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13173 Roo.bootstrap.version == 3 ? caret : '',
13176 cls: 'combobox-clear',
13190 combobox.cls += ' roo-select2-container-multi';
13194 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13195 tooltip : 'This field is required'
13197 if (Roo.bootstrap.version == 4) {
13200 style : 'display:none'
13205 if (align ==='left' && this.fieldLabel.length) {
13207 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13214 cls : 'control-label',
13215 html : this.fieldLabel
13227 var labelCfg = cfg.cn[1];
13228 var contentCfg = cfg.cn[2];
13230 if(this.indicatorpos == 'right'){
13235 cls : 'control-label',
13239 html : this.fieldLabel
13253 labelCfg = cfg.cn[0];
13254 contentCfg = cfg.cn[1];
13257 if(this.labelWidth > 12){
13258 labelCfg.style = "width: " + this.labelWidth + 'px';
13261 if(this.labelWidth < 13 && this.labelmd == 0){
13262 this.labelmd = this.labelWidth;
13265 if(this.labellg > 0){
13266 labelCfg.cls += ' col-lg-' + this.labellg;
13267 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13270 if(this.labelmd > 0){
13271 labelCfg.cls += ' col-md-' + this.labelmd;
13272 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13275 if(this.labelsm > 0){
13276 labelCfg.cls += ' col-sm-' + this.labelsm;
13277 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13280 if(this.labelxs > 0){
13281 labelCfg.cls += ' col-xs-' + this.labelxs;
13282 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13285 } else if ( this.fieldLabel.length) {
13286 // Roo.log(" label");
13291 //cls : 'input-group-addon',
13292 html : this.fieldLabel
13300 if(this.indicatorpos == 'right'){
13308 html : this.fieldLabel
13322 // Roo.log(" no label && no align");
13329 ['xs','sm','md','lg'].map(function(size){
13330 if (settings[size]) {
13331 cfg.cls += ' col-' + size + '-' + settings[size];
13342 onResize : function(w, h){
13343 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13344 // if(typeof w == 'number'){
13345 // var x = w - this.trigger.getWidth();
13346 // this.inputEl().setWidth(this.adjustWidth('input', x));
13347 // this.trigger.setStyle('left', x+'px');
13352 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13355 getResizeEl : function(){
13356 return this.inputEl();
13360 getPositionEl : function(){
13361 return this.inputEl();
13365 alignErrorIcon : function(){
13366 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13370 initEvents : function(){
13374 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13375 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13376 if(!this.multiple && this.showToggleBtn){
13377 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13378 if(this.hideTrigger){
13379 this.trigger.setDisplayed(false);
13381 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13385 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13388 if(this.removable && !this.editable && !this.tickable){
13389 var close = this.closeTriggerEl();
13392 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13393 close.on('click', this.removeBtnClick, this, close);
13397 //this.trigger.addClassOnOver('x-form-trigger-over');
13398 //this.trigger.addClassOnClick('x-form-trigger-click');
13401 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13405 closeTriggerEl : function()
13407 var close = this.el.select('.roo-combo-removable-btn', true).first();
13408 return close ? close : false;
13411 removeBtnClick : function(e, h, el)
13413 e.preventDefault();
13415 if(this.fireEvent("remove", this) !== false){
13417 this.fireEvent("afterremove", this)
13421 createList : function()
13423 this.list = Roo.get(document.body).createChild({
13424 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13425 cls: 'typeahead typeahead-long dropdown-menu shadow',
13426 style: 'display:none'
13429 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13434 initTrigger : function(){
13439 onDestroy : function(){
13441 this.trigger.removeAllListeners();
13442 // this.trigger.remove();
13445 // this.wrap.remove();
13447 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13451 onFocus : function(){
13452 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13454 if(!this.mimicing){
13455 this.wrap.addClass('x-trigger-wrap-focus');
13456 this.mimicing = true;
13457 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13458 if(this.monitorTab){
13459 this.el.on("keydown", this.checkTab, this);
13466 checkTab : function(e){
13467 if(e.getKey() == e.TAB){
13468 this.triggerBlur();
13473 onBlur : function(){
13478 mimicBlur : function(e, t){
13480 if(!this.wrap.contains(t) && this.validateBlur()){
13481 this.triggerBlur();
13487 triggerBlur : function(){
13488 this.mimicing = false;
13489 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13490 if(this.monitorTab){
13491 this.el.un("keydown", this.checkTab, this);
13493 //this.wrap.removeClass('x-trigger-wrap-focus');
13494 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13498 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13499 validateBlur : function(e, t){
13504 onDisable : function(){
13505 this.inputEl().dom.disabled = true;
13506 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13508 // this.wrap.addClass('x-item-disabled');
13513 onEnable : function(){
13514 this.inputEl().dom.disabled = false;
13515 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13517 // this.el.removeClass('x-item-disabled');
13522 onShow : function(){
13523 var ae = this.getActionEl();
13526 ae.dom.style.display = '';
13527 ae.dom.style.visibility = 'visible';
13533 onHide : function(){
13534 var ae = this.getActionEl();
13535 ae.dom.style.display = 'none';
13539 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13540 * by an implementing function.
13542 * @param {EventObject} e
13544 onTriggerClick : Roo.emptyFn
13552 * @class Roo.bootstrap.CardUploader
13553 * @extends Roo.bootstrap.Button
13554 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13555 * @cfg {Number} errorTimeout default 3000
13556 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13557 * @cfg {Array} html The button text.
13561 * Create a new CardUploader
13562 * @param {Object} config The config object
13565 Roo.bootstrap.CardUploader = function(config){
13569 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13572 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13580 * When a image is clicked on - and needs to display a slideshow or similar..
13581 * @param {Roo.bootstrap.Card} this
13582 * @param {Object} The image information data
13588 * When a the download link is clicked
13589 * @param {Roo.bootstrap.Card} this
13590 * @param {Object} The image information data contains
13597 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13600 errorTimeout : 3000,
13604 fileCollection : false,
13607 getAutoCreate : function()
13611 cls :'form-group' ,
13616 //cls : 'input-group-addon',
13617 html : this.fieldLabel
13625 value : this.value,
13626 cls : 'd-none form-control'
13631 multiple : 'multiple',
13633 cls : 'd-none roo-card-upload-selector'
13637 cls : 'roo-card-uploader-button-container w-100 mb-2'
13640 cls : 'card-columns roo-card-uploader-container'
13650 getChildContainer : function() /// what children are added to.
13652 return this.containerEl;
13655 getButtonContainer : function() /// what children are added to.
13657 return this.el.select(".roo-card-uploader-button-container").first();
13660 initEvents : function()
13663 Roo.bootstrap.Input.prototype.initEvents.call(this);
13667 xns: Roo.bootstrap,
13670 container_method : 'getButtonContainer' ,
13671 html : this.html, // fix changable?
13674 'click' : function(btn, e) {
13683 this.urlAPI = (window.createObjectURL && window) ||
13684 (window.URL && URL.revokeObjectURL && URL) ||
13685 (window.webkitURL && webkitURL);
13690 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13692 this.selectorEl.on('change', this.onFileSelected, this);
13695 this.images.forEach(function(img) {
13698 this.images = false;
13700 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13706 onClick : function(e)
13708 e.preventDefault();
13710 this.selectorEl.dom.click();
13714 onFileSelected : function(e)
13716 e.preventDefault();
13718 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13722 Roo.each(this.selectorEl.dom.files, function(file){
13723 this.addFile(file);
13732 addFile : function(file)
13735 if(typeof(file) === 'string'){
13736 throw "Add file by name?"; // should not happen
13740 if(!file || !this.urlAPI){
13750 var url = _this.urlAPI.createObjectURL( file);
13753 id : Roo.bootstrap.CardUploader.ID--,
13754 is_uploaded : false,
13758 mimetype : file.type,
13766 * addCard - add an Attachment to the uploader
13767 * @param data - the data about the image to upload
13771 title : "Title of file",
13772 is_uploaded : false,
13773 src : "http://.....",
13774 srcfile : { the File upload object },
13775 mimetype : file.type,
13778 .. any other data...
13784 addCard : function (data)
13786 // hidden input element?
13787 // if the file is not an image...
13788 //then we need to use something other that and header_image
13793 xns : Roo.bootstrap,
13794 xtype : 'CardFooter',
13797 xns : Roo.bootstrap,
13803 xns : Roo.bootstrap,
13805 html : String.format("<small>{0}</small>", data.title),
13806 cls : 'col-10 text-left',
13811 click : function() {
13813 t.fireEvent( "download", t, data );
13819 xns : Roo.bootstrap,
13821 style: 'max-height: 28px; ',
13827 click : function() {
13828 t.removeCard(data.id)
13840 var cn = this.addxtype(
13843 xns : Roo.bootstrap,
13846 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13847 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13848 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13853 initEvents : function() {
13854 Roo.bootstrap.Card.prototype.initEvents.call(this);
13856 this.imgEl = this.el.select('.card-img-top').first();
13858 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13859 this.imgEl.set({ 'pointer' : 'cursor' });
13862 this.getCardFooter().addClass('p-1');
13869 // dont' really need ot update items.
13870 // this.items.push(cn);
13871 this.fileCollection.add(cn);
13873 if (!data.srcfile) {
13874 this.updateInput();
13879 var reader = new FileReader();
13880 reader.addEventListener("load", function() {
13881 data.srcdata = reader.result;
13884 reader.readAsDataURL(data.srcfile);
13889 removeCard : function(id)
13892 var card = this.fileCollection.get(id);
13893 card.data.is_deleted = 1;
13894 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13895 //this.fileCollection.remove(card);
13896 //this.items = this.items.filter(function(e) { return e != card });
13897 // dont' really need ot update items.
13898 card.el.dom.parentNode.removeChild(card.el.dom);
13899 this.updateInput();
13905 this.fileCollection.each(function(card) {
13906 if (card.el.dom && card.el.dom.parentNode) {
13907 card.el.dom.parentNode.removeChild(card.el.dom);
13910 this.fileCollection.clear();
13911 this.updateInput();
13914 updateInput : function()
13917 this.fileCollection.each(function(e) {
13921 this.inputEl().dom.value = JSON.stringify(data);
13931 Roo.bootstrap.CardUploader.ID = -1;/*
13933 * Ext JS Library 1.1.1
13934 * Copyright(c) 2006-2007, Ext JS, LLC.
13936 * Originally Released Under LGPL - original licence link has changed is not relivant.
13939 * <script type="text/javascript">
13944 * @class Roo.data.SortTypes
13946 * Defines the default sorting (casting?) comparison functions used when sorting data.
13948 Roo.data.SortTypes = {
13950 * Default sort that does nothing
13951 * @param {Mixed} s The value being converted
13952 * @return {Mixed} The comparison value
13954 none : function(s){
13959 * The regular expression used to strip tags
13963 stripTagsRE : /<\/?[^>]+>/gi,
13966 * Strips all HTML tags to sort on text only
13967 * @param {Mixed} s The value being converted
13968 * @return {String} The comparison value
13970 asText : function(s){
13971 return String(s).replace(this.stripTagsRE, "");
13975 * Strips all HTML tags to sort on text only - Case insensitive
13976 * @param {Mixed} s The value being converted
13977 * @return {String} The comparison value
13979 asUCText : function(s){
13980 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13984 * Case insensitive string
13985 * @param {Mixed} s The value being converted
13986 * @return {String} The comparison value
13988 asUCString : function(s) {
13989 return String(s).toUpperCase();
13994 * @param {Mixed} s The value being converted
13995 * @return {Number} The comparison value
13997 asDate : function(s) {
14001 if(s instanceof Date){
14002 return s.getTime();
14004 return Date.parse(String(s));
14009 * @param {Mixed} s The value being converted
14010 * @return {Float} The comparison value
14012 asFloat : function(s) {
14013 var val = parseFloat(String(s).replace(/,/g, ""));
14022 * @param {Mixed} s The value being converted
14023 * @return {Number} The comparison value
14025 asInt : function(s) {
14026 var val = parseInt(String(s).replace(/,/g, ""));
14034 * Ext JS Library 1.1.1
14035 * Copyright(c) 2006-2007, Ext JS, LLC.
14037 * Originally Released Under LGPL - original licence link has changed is not relivant.
14040 * <script type="text/javascript">
14044 * @class Roo.data.Record
14045 * Instances of this class encapsulate both record <em>definition</em> information, and record
14046 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14047 * to access Records cached in an {@link Roo.data.Store} object.<br>
14049 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14050 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14053 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14055 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14056 * {@link #create}. The parameters are the same.
14057 * @param {Array} data An associative Array of data values keyed by the field name.
14058 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14059 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14060 * not specified an integer id is generated.
14062 Roo.data.Record = function(data, id){
14063 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14068 * Generate a constructor for a specific record layout.
14069 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14070 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14071 * Each field definition object may contain the following properties: <ul>
14072 * <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,
14073 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14074 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14075 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14076 * is being used, then this is a string containing the javascript expression to reference the data relative to
14077 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14078 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14079 * this may be omitted.</p></li>
14080 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14081 * <ul><li>auto (Default, implies no conversion)</li>
14086 * <li>date</li></ul></p></li>
14087 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14088 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14089 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14090 * by the Reader into an object that will be stored in the Record. It is passed the
14091 * following parameters:<ul>
14092 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14094 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14096 * <br>usage:<br><pre><code>
14097 var TopicRecord = Roo.data.Record.create(
14098 {name: 'title', mapping: 'topic_title'},
14099 {name: 'author', mapping: 'username'},
14100 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14101 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14102 {name: 'lastPoster', mapping: 'user2'},
14103 {name: 'excerpt', mapping: 'post_text'}
14106 var myNewRecord = new TopicRecord({
14107 title: 'Do my job please',
14110 lastPost: new Date(),
14111 lastPoster: 'Animal',
14112 excerpt: 'No way dude!'
14114 myStore.add(myNewRecord);
14119 Roo.data.Record.create = function(o){
14120 var f = function(){
14121 f.superclass.constructor.apply(this, arguments);
14123 Roo.extend(f, Roo.data.Record);
14124 var p = f.prototype;
14125 p.fields = new Roo.util.MixedCollection(false, function(field){
14128 for(var i = 0, len = o.length; i < len; i++){
14129 p.fields.add(new Roo.data.Field(o[i]));
14131 f.getField = function(name){
14132 return p.fields.get(name);
14137 Roo.data.Record.AUTO_ID = 1000;
14138 Roo.data.Record.EDIT = 'edit';
14139 Roo.data.Record.REJECT = 'reject';
14140 Roo.data.Record.COMMIT = 'commit';
14142 Roo.data.Record.prototype = {
14144 * Readonly flag - true if this record has been modified.
14153 join : function(store){
14154 this.store = store;
14158 * Set the named field to the specified value.
14159 * @param {String} name The name of the field to set.
14160 * @param {Object} value The value to set the field to.
14162 set : function(name, value){
14163 if(this.data[name] == value){
14167 if(!this.modified){
14168 this.modified = {};
14170 if(typeof this.modified[name] == 'undefined'){
14171 this.modified[name] = this.data[name];
14173 this.data[name] = value;
14174 if(!this.editing && this.store){
14175 this.store.afterEdit(this);
14180 * Get the value of the named field.
14181 * @param {String} name The name of the field to get the value of.
14182 * @return {Object} The value of the field.
14184 get : function(name){
14185 return this.data[name];
14189 beginEdit : function(){
14190 this.editing = true;
14191 this.modified = {};
14195 cancelEdit : function(){
14196 this.editing = false;
14197 delete this.modified;
14201 endEdit : function(){
14202 this.editing = false;
14203 if(this.dirty && this.store){
14204 this.store.afterEdit(this);
14209 * Usually called by the {@link Roo.data.Store} which owns the Record.
14210 * Rejects all changes made to the Record since either creation, or the last commit operation.
14211 * Modified fields are reverted to their original values.
14213 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14214 * of reject operations.
14216 reject : function(){
14217 var m = this.modified;
14219 if(typeof m[n] != "function"){
14220 this.data[n] = m[n];
14223 this.dirty = false;
14224 delete this.modified;
14225 this.editing = false;
14227 this.store.afterReject(this);
14232 * Usually called by the {@link Roo.data.Store} which owns the Record.
14233 * Commits all changes made to the Record since either creation, or the last commit operation.
14235 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14236 * of commit operations.
14238 commit : function(){
14239 this.dirty = false;
14240 delete this.modified;
14241 this.editing = false;
14243 this.store.afterCommit(this);
14248 hasError : function(){
14249 return this.error != null;
14253 clearError : function(){
14258 * Creates a copy of this record.
14259 * @param {String} id (optional) A new record id if you don't want to use this record's id
14262 copy : function(newId) {
14263 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14267 * Ext JS Library 1.1.1
14268 * Copyright(c) 2006-2007, Ext JS, LLC.
14270 * Originally Released Under LGPL - original licence link has changed is not relivant.
14273 * <script type="text/javascript">
14279 * @class Roo.data.Store
14280 * @extends Roo.util.Observable
14281 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14282 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14284 * 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
14285 * has no knowledge of the format of the data returned by the Proxy.<br>
14287 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14288 * instances from the data object. These records are cached and made available through accessor functions.
14290 * Creates a new Store.
14291 * @param {Object} config A config object containing the objects needed for the Store to access data,
14292 * and read the data into Records.
14294 Roo.data.Store = function(config){
14295 this.data = new Roo.util.MixedCollection(false);
14296 this.data.getKey = function(o){
14299 this.baseParams = {};
14301 this.paramNames = {
14306 "multisort" : "_multisort"
14309 if(config && config.data){
14310 this.inlineData = config.data;
14311 delete config.data;
14314 Roo.apply(this, config);
14316 if(this.reader){ // reader passed
14317 this.reader = Roo.factory(this.reader, Roo.data);
14318 this.reader.xmodule = this.xmodule || false;
14319 if(!this.recordType){
14320 this.recordType = this.reader.recordType;
14322 if(this.reader.onMetaChange){
14323 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14327 if(this.recordType){
14328 this.fields = this.recordType.prototype.fields;
14330 this.modified = [];
14334 * @event datachanged
14335 * Fires when the data cache has changed, and a widget which is using this Store
14336 * as a Record cache should refresh its view.
14337 * @param {Store} this
14339 datachanged : true,
14341 * @event metachange
14342 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14343 * @param {Store} this
14344 * @param {Object} meta The JSON metadata
14349 * Fires when Records have been added to the Store
14350 * @param {Store} this
14351 * @param {Roo.data.Record[]} records The array of Records added
14352 * @param {Number} index The index at which the record(s) were added
14357 * Fires when a Record has been removed from the Store
14358 * @param {Store} this
14359 * @param {Roo.data.Record} record The Record that was removed
14360 * @param {Number} index The index at which the record was removed
14365 * Fires when a Record has been updated
14366 * @param {Store} this
14367 * @param {Roo.data.Record} record The Record that was updated
14368 * @param {String} operation The update operation being performed. Value may be one of:
14370 Roo.data.Record.EDIT
14371 Roo.data.Record.REJECT
14372 Roo.data.Record.COMMIT
14378 * Fires when the data cache has been cleared.
14379 * @param {Store} this
14383 * @event beforeload
14384 * Fires before a request is made for a new data object. If the beforeload handler returns false
14385 * the load action will be canceled.
14386 * @param {Store} this
14387 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14391 * @event beforeloadadd
14392 * Fires after a new set of Records has been loaded.
14393 * @param {Store} this
14394 * @param {Roo.data.Record[]} records The Records that were loaded
14395 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14397 beforeloadadd : true,
14400 * Fires after a new set of Records has been loaded, before they are added to the store.
14401 * @param {Store} this
14402 * @param {Roo.data.Record[]} records The Records that were loaded
14403 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14404 * @params {Object} return from reader
14408 * @event loadexception
14409 * Fires if an exception occurs in the Proxy during loading.
14410 * Called with the signature of the Proxy's "loadexception" event.
14411 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14414 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14415 * @param {Object} load options
14416 * @param {Object} jsonData from your request (normally this contains the Exception)
14418 loadexception : true
14422 this.proxy = Roo.factory(this.proxy, Roo.data);
14423 this.proxy.xmodule = this.xmodule || false;
14424 this.relayEvents(this.proxy, ["loadexception"]);
14426 this.sortToggle = {};
14427 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14429 Roo.data.Store.superclass.constructor.call(this);
14431 if(this.inlineData){
14432 this.loadData(this.inlineData);
14433 delete this.inlineData;
14437 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14439 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14440 * without a remote query - used by combo/forms at present.
14444 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14447 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14450 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14451 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14454 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14455 * on any HTTP request
14458 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14461 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14465 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14466 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14468 remoteSort : false,
14471 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14472 * loaded or when a record is removed. (defaults to false).
14474 pruneModifiedRecords : false,
14477 lastOptions : null,
14480 * Add Records to the Store and fires the add event.
14481 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14483 add : function(records){
14484 records = [].concat(records);
14485 for(var i = 0, len = records.length; i < len; i++){
14486 records[i].join(this);
14488 var index = this.data.length;
14489 this.data.addAll(records);
14490 this.fireEvent("add", this, records, index);
14494 * Remove a Record from the Store and fires the remove event.
14495 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14497 remove : function(record){
14498 var index = this.data.indexOf(record);
14499 this.data.removeAt(index);
14501 if(this.pruneModifiedRecords){
14502 this.modified.remove(record);
14504 this.fireEvent("remove", this, record, index);
14508 * Remove all Records from the Store and fires the clear event.
14510 removeAll : function(){
14512 if(this.pruneModifiedRecords){
14513 this.modified = [];
14515 this.fireEvent("clear", this);
14519 * Inserts Records to the Store at the given index and fires the add event.
14520 * @param {Number} index The start index at which to insert the passed Records.
14521 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14523 insert : function(index, records){
14524 records = [].concat(records);
14525 for(var i = 0, len = records.length; i < len; i++){
14526 this.data.insert(index, records[i]);
14527 records[i].join(this);
14529 this.fireEvent("add", this, records, index);
14533 * Get the index within the cache of the passed Record.
14534 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14535 * @return {Number} The index of the passed Record. Returns -1 if not found.
14537 indexOf : function(record){
14538 return this.data.indexOf(record);
14542 * Get the index within the cache of the Record with the passed id.
14543 * @param {String} id The id of the Record to find.
14544 * @return {Number} The index of the Record. Returns -1 if not found.
14546 indexOfId : function(id){
14547 return this.data.indexOfKey(id);
14551 * Get the Record with the specified id.
14552 * @param {String} id The id of the Record to find.
14553 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14555 getById : function(id){
14556 return this.data.key(id);
14560 * Get the Record at the specified index.
14561 * @param {Number} index The index of the Record to find.
14562 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14564 getAt : function(index){
14565 return this.data.itemAt(index);
14569 * Returns a range of Records between specified indices.
14570 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14571 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14572 * @return {Roo.data.Record[]} An array of Records
14574 getRange : function(start, end){
14575 return this.data.getRange(start, end);
14579 storeOptions : function(o){
14580 o = Roo.apply({}, o);
14583 this.lastOptions = o;
14587 * Loads the Record cache from the configured Proxy using the configured Reader.
14589 * If using remote paging, then the first load call must specify the <em>start</em>
14590 * and <em>limit</em> properties in the options.params property to establish the initial
14591 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14593 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14594 * and this call will return before the new data has been loaded. Perform any post-processing
14595 * in a callback function, or in a "load" event handler.</strong>
14597 * @param {Object} options An object containing properties which control loading options:<ul>
14598 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14599 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14600 * passed the following arguments:<ul>
14601 * <li>r : Roo.data.Record[]</li>
14602 * <li>options: Options object from the load call</li>
14603 * <li>success: Boolean success indicator</li></ul></li>
14604 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14605 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14608 load : function(options){
14609 options = options || {};
14610 if(this.fireEvent("beforeload", this, options) !== false){
14611 this.storeOptions(options);
14612 var p = Roo.apply(options.params || {}, this.baseParams);
14613 // if meta was not loaded from remote source.. try requesting it.
14614 if (!this.reader.metaFromRemote) {
14615 p._requestMeta = 1;
14617 if(this.sortInfo && this.remoteSort){
14618 var pn = this.paramNames;
14619 p[pn["sort"]] = this.sortInfo.field;
14620 p[pn["dir"]] = this.sortInfo.direction;
14622 if (this.multiSort) {
14623 var pn = this.paramNames;
14624 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14627 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14632 * Reloads the Record cache from the configured Proxy using the configured Reader and
14633 * the options from the last load operation performed.
14634 * @param {Object} options (optional) An object containing properties which may override the options
14635 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14636 * the most recently used options are reused).
14638 reload : function(options){
14639 this.load(Roo.applyIf(options||{}, this.lastOptions));
14643 // Called as a callback by the Reader during a load operation.
14644 loadRecords : function(o, options, success){
14645 if(!o || success === false){
14646 if(success !== false){
14647 this.fireEvent("load", this, [], options, o);
14649 if(options.callback){
14650 options.callback.call(options.scope || this, [], options, false);
14654 // if data returned failure - throw an exception.
14655 if (o.success === false) {
14656 // show a message if no listener is registered.
14657 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14658 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14660 // loadmask wil be hooked into this..
14661 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14664 var r = o.records, t = o.totalRecords || r.length;
14666 this.fireEvent("beforeloadadd", this, r, options, o);
14668 if(!options || options.add !== true){
14669 if(this.pruneModifiedRecords){
14670 this.modified = [];
14672 for(var i = 0, len = r.length; i < len; i++){
14676 this.data = this.snapshot;
14677 delete this.snapshot;
14680 this.data.addAll(r);
14681 this.totalLength = t;
14683 this.fireEvent("datachanged", this);
14685 this.totalLength = Math.max(t, this.data.length+r.length);
14689 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14691 var e = new Roo.data.Record({});
14693 e.set(this.parent.displayField, this.parent.emptyTitle);
14694 e.set(this.parent.valueField, '');
14699 this.fireEvent("load", this, r, options, o);
14700 if(options.callback){
14701 options.callback.call(options.scope || this, r, options, true);
14707 * Loads data from a passed data block. A Reader which understands the format of the data
14708 * must have been configured in the constructor.
14709 * @param {Object} data The data block from which to read the Records. The format of the data expected
14710 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14711 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14713 loadData : function(o, append){
14714 var r = this.reader.readRecords(o);
14715 this.loadRecords(r, {add: append}, true);
14719 * using 'cn' the nested child reader read the child array into it's child stores.
14720 * @param {Object} rec The record with a 'children array
14722 loadDataFromChildren : function(rec)
14724 this.loadData(this.reader.toLoadData(rec));
14729 * Gets the number of cached records.
14731 * <em>If using paging, this may not be the total size of the dataset. If the data object
14732 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14733 * the data set size</em>
14735 getCount : function(){
14736 return this.data.length || 0;
14740 * Gets the total number of records in the dataset as returned by the server.
14742 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14743 * the dataset size</em>
14745 getTotalCount : function(){
14746 return this.totalLength || 0;
14750 * Returns the sort state of the Store as an object with two properties:
14752 field {String} The name of the field by which the Records are sorted
14753 direction {String} The sort order, "ASC" or "DESC"
14756 getSortState : function(){
14757 return this.sortInfo;
14761 applySort : function(){
14762 if(this.sortInfo && !this.remoteSort){
14763 var s = this.sortInfo, f = s.field;
14764 var st = this.fields.get(f).sortType;
14765 var fn = function(r1, r2){
14766 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14767 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14769 this.data.sort(s.direction, fn);
14770 if(this.snapshot && this.snapshot != this.data){
14771 this.snapshot.sort(s.direction, fn);
14777 * Sets the default sort column and order to be used by the next load operation.
14778 * @param {String} fieldName The name of the field to sort by.
14779 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14781 setDefaultSort : function(field, dir){
14782 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14786 * Sort the Records.
14787 * If remote sorting is used, the sort is performed on the server, and the cache is
14788 * reloaded. If local sorting is used, the cache is sorted internally.
14789 * @param {String} fieldName The name of the field to sort by.
14790 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14792 sort : function(fieldName, dir){
14793 var f = this.fields.get(fieldName);
14795 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14797 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14798 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14803 this.sortToggle[f.name] = dir;
14804 this.sortInfo = {field: f.name, direction: dir};
14805 if(!this.remoteSort){
14807 this.fireEvent("datachanged", this);
14809 this.load(this.lastOptions);
14814 * Calls the specified function for each of the Records in the cache.
14815 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14816 * Returning <em>false</em> aborts and exits the iteration.
14817 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14819 each : function(fn, scope){
14820 this.data.each(fn, scope);
14824 * Gets all records modified since the last commit. Modified records are persisted across load operations
14825 * (e.g., during paging).
14826 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14828 getModifiedRecords : function(){
14829 return this.modified;
14833 createFilterFn : function(property, value, anyMatch){
14834 if(!value.exec){ // not a regex
14835 value = String(value);
14836 if(value.length == 0){
14839 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14841 return function(r){
14842 return value.test(r.data[property]);
14847 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14848 * @param {String} property A field on your records
14849 * @param {Number} start The record index to start at (defaults to 0)
14850 * @param {Number} end The last record index to include (defaults to length - 1)
14851 * @return {Number} The sum
14853 sum : function(property, start, end){
14854 var rs = this.data.items, v = 0;
14855 start = start || 0;
14856 end = (end || end === 0) ? end : rs.length-1;
14858 for(var i = start; i <= end; i++){
14859 v += (rs[i].data[property] || 0);
14865 * Filter the records by a specified property.
14866 * @param {String} field A field on your records
14867 * @param {String/RegExp} value Either a string that the field
14868 * should start with or a RegExp to test against the field
14869 * @param {Boolean} anyMatch True to match any part not just the beginning
14871 filter : function(property, value, anyMatch){
14872 var fn = this.createFilterFn(property, value, anyMatch);
14873 return fn ? this.filterBy(fn) : this.clearFilter();
14877 * Filter by a function. The specified function will be called with each
14878 * record in this data source. If the function returns true the record is included,
14879 * otherwise it is filtered.
14880 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14881 * @param {Object} scope (optional) The scope of the function (defaults to this)
14883 filterBy : function(fn, scope){
14884 this.snapshot = this.snapshot || this.data;
14885 this.data = this.queryBy(fn, scope||this);
14886 this.fireEvent("datachanged", this);
14890 * Query the records by a specified property.
14891 * @param {String} field A field on your records
14892 * @param {String/RegExp} value Either a string that the field
14893 * should start with or a RegExp to test against the field
14894 * @param {Boolean} anyMatch True to match any part not just the beginning
14895 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14897 query : function(property, value, anyMatch){
14898 var fn = this.createFilterFn(property, value, anyMatch);
14899 return fn ? this.queryBy(fn) : this.data.clone();
14903 * Query by a function. The specified function will be called with each
14904 * record in this data source. If the function returns true the record is included
14906 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14907 * @param {Object} scope (optional) The scope of the function (defaults to this)
14908 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14910 queryBy : function(fn, scope){
14911 var data = this.snapshot || this.data;
14912 return data.filterBy(fn, scope||this);
14916 * Collects unique values for a particular dataIndex from this store.
14917 * @param {String} dataIndex The property to collect
14918 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14919 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14920 * @return {Array} An array of the unique values
14922 collect : function(dataIndex, allowNull, bypassFilter){
14923 var d = (bypassFilter === true && this.snapshot) ?
14924 this.snapshot.items : this.data.items;
14925 var v, sv, r = [], l = {};
14926 for(var i = 0, len = d.length; i < len; i++){
14927 v = d[i].data[dataIndex];
14929 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14938 * Revert to a view of the Record cache with no filtering applied.
14939 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14941 clearFilter : function(suppressEvent){
14942 if(this.snapshot && this.snapshot != this.data){
14943 this.data = this.snapshot;
14944 delete this.snapshot;
14945 if(suppressEvent !== true){
14946 this.fireEvent("datachanged", this);
14952 afterEdit : function(record){
14953 if(this.modified.indexOf(record) == -1){
14954 this.modified.push(record);
14956 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14960 afterReject : function(record){
14961 this.modified.remove(record);
14962 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14966 afterCommit : function(record){
14967 this.modified.remove(record);
14968 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14972 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14973 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14975 commitChanges : function(){
14976 var m = this.modified.slice(0);
14977 this.modified = [];
14978 for(var i = 0, len = m.length; i < len; i++){
14984 * Cancel outstanding changes on all changed records.
14986 rejectChanges : function(){
14987 var m = this.modified.slice(0);
14988 this.modified = [];
14989 for(var i = 0, len = m.length; i < len; i++){
14994 onMetaChange : function(meta, rtype, o){
14995 this.recordType = rtype;
14996 this.fields = rtype.prototype.fields;
14997 delete this.snapshot;
14998 this.sortInfo = meta.sortInfo || this.sortInfo;
14999 this.modified = [];
15000 this.fireEvent('metachange', this, this.reader.meta);
15003 moveIndex : function(data, type)
15005 var index = this.indexOf(data);
15007 var newIndex = index + type;
15011 this.insert(newIndex, data);
15016 * Ext JS Library 1.1.1
15017 * Copyright(c) 2006-2007, Ext JS, LLC.
15019 * Originally Released Under LGPL - original licence link has changed is not relivant.
15022 * <script type="text/javascript">
15026 * @class Roo.data.SimpleStore
15027 * @extends Roo.data.Store
15028 * Small helper class to make creating Stores from Array data easier.
15029 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15030 * @cfg {Array} fields An array of field definition objects, or field name strings.
15031 * @cfg {Object} an existing reader (eg. copied from another store)
15032 * @cfg {Array} data The multi-dimensional array of data
15034 * @param {Object} config
15036 Roo.data.SimpleStore = function(config)
15038 Roo.data.SimpleStore.superclass.constructor.call(this, {
15040 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15043 Roo.data.Record.create(config.fields)
15045 proxy : new Roo.data.MemoryProxy(config.data)
15049 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15051 * Ext JS Library 1.1.1
15052 * Copyright(c) 2006-2007, Ext JS, LLC.
15054 * Originally Released Under LGPL - original licence link has changed is not relivant.
15057 * <script type="text/javascript">
15062 * @extends Roo.data.Store
15063 * @class Roo.data.JsonStore
15064 * Small helper class to make creating Stores for JSON data easier. <br/>
15066 var store = new Roo.data.JsonStore({
15067 url: 'get-images.php',
15069 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15072 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15073 * JsonReader and HttpProxy (unless inline data is provided).</b>
15074 * @cfg {Array} fields An array of field definition objects, or field name strings.
15076 * @param {Object} config
15078 Roo.data.JsonStore = function(c){
15079 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15080 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15081 reader: new Roo.data.JsonReader(c, c.fields)
15084 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15086 * Ext JS Library 1.1.1
15087 * Copyright(c) 2006-2007, Ext JS, LLC.
15089 * Originally Released Under LGPL - original licence link has changed is not relivant.
15092 * <script type="text/javascript">
15096 Roo.data.Field = function(config){
15097 if(typeof config == "string"){
15098 config = {name: config};
15100 Roo.apply(this, config);
15103 this.type = "auto";
15106 var st = Roo.data.SortTypes;
15107 // named sortTypes are supported, here we look them up
15108 if(typeof this.sortType == "string"){
15109 this.sortType = st[this.sortType];
15112 // set default sortType for strings and dates
15113 if(!this.sortType){
15116 this.sortType = st.asUCString;
15119 this.sortType = st.asDate;
15122 this.sortType = st.none;
15127 var stripRe = /[\$,%]/g;
15129 // prebuilt conversion function for this field, instead of
15130 // switching every time we're reading a value
15132 var cv, dateFormat = this.dateFormat;
15137 cv = function(v){ return v; };
15140 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15144 return v !== undefined && v !== null && v !== '' ?
15145 parseInt(String(v).replace(stripRe, ""), 10) : '';
15150 return v !== undefined && v !== null && v !== '' ?
15151 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15156 cv = function(v){ return v === true || v === "true" || v == 1; };
15163 if(v instanceof Date){
15167 if(dateFormat == "timestamp"){
15168 return new Date(v*1000);
15170 return Date.parseDate(v, dateFormat);
15172 var parsed = Date.parse(v);
15173 return parsed ? new Date(parsed) : null;
15182 Roo.data.Field.prototype = {
15190 * Ext JS Library 1.1.1
15191 * Copyright(c) 2006-2007, Ext JS, LLC.
15193 * Originally Released Under LGPL - original licence link has changed is not relivant.
15196 * <script type="text/javascript">
15199 // Base class for reading structured data from a data source. This class is intended to be
15200 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15203 * @class Roo.data.DataReader
15204 * Base class for reading structured data from a data source. This class is intended to be
15205 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15208 Roo.data.DataReader = function(meta, recordType){
15212 this.recordType = recordType instanceof Array ?
15213 Roo.data.Record.create(recordType) : recordType;
15216 Roo.data.DataReader.prototype = {
15219 readerType : 'Data',
15221 * Create an empty record
15222 * @param {Object} data (optional) - overlay some values
15223 * @return {Roo.data.Record} record created.
15225 newRow : function(d) {
15227 this.recordType.prototype.fields.each(function(c) {
15229 case 'int' : da[c.name] = 0; break;
15230 case 'date' : da[c.name] = new Date(); break;
15231 case 'float' : da[c.name] = 0.0; break;
15232 case 'boolean' : da[c.name] = false; break;
15233 default : da[c.name] = ""; break;
15237 return new this.recordType(Roo.apply(da, d));
15243 * Ext JS Library 1.1.1
15244 * Copyright(c) 2006-2007, Ext JS, LLC.
15246 * Originally Released Under LGPL - original licence link has changed is not relivant.
15249 * <script type="text/javascript">
15253 * @class Roo.data.DataProxy
15254 * @extends Roo.data.Observable
15255 * This class is an abstract base class for implementations which provide retrieval of
15256 * unformatted data objects.<br>
15258 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15259 * (of the appropriate type which knows how to parse the data object) to provide a block of
15260 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15262 * Custom implementations must implement the load method as described in
15263 * {@link Roo.data.HttpProxy#load}.
15265 Roo.data.DataProxy = function(){
15268 * @event beforeload
15269 * Fires before a network request is made to retrieve a data object.
15270 * @param {Object} This DataProxy object.
15271 * @param {Object} params The params parameter to the load function.
15276 * Fires before the load method's callback is called.
15277 * @param {Object} This DataProxy object.
15278 * @param {Object} o The data object.
15279 * @param {Object} arg The callback argument object passed to the load function.
15283 * @event loadexception
15284 * Fires if an Exception occurs during data retrieval.
15285 * @param {Object} This DataProxy object.
15286 * @param {Object} o The data object.
15287 * @param {Object} arg The callback argument object passed to the load function.
15288 * @param {Object} e The Exception.
15290 loadexception : true
15292 Roo.data.DataProxy.superclass.constructor.call(this);
15295 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15298 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15302 * Ext JS Library 1.1.1
15303 * Copyright(c) 2006-2007, Ext JS, LLC.
15305 * Originally Released Under LGPL - original licence link has changed is not relivant.
15308 * <script type="text/javascript">
15311 * @class Roo.data.MemoryProxy
15312 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15313 * to the Reader when its load method is called.
15315 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15317 Roo.data.MemoryProxy = function(data){
15321 Roo.data.MemoryProxy.superclass.constructor.call(this);
15325 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15328 * Load data from the requested source (in this case an in-memory
15329 * data object passed to the constructor), read the data object into
15330 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15331 * process that block using the passed callback.
15332 * @param {Object} params This parameter is not used by the MemoryProxy class.
15333 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15334 * object into a block of Roo.data.Records.
15335 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15336 * The function must be passed <ul>
15337 * <li>The Record block object</li>
15338 * <li>The "arg" argument from the load function</li>
15339 * <li>A boolean success indicator</li>
15341 * @param {Object} scope The scope in which to call the callback
15342 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15344 load : function(params, reader, callback, scope, arg){
15345 params = params || {};
15348 result = reader.readRecords(params.data ? params.data :this.data);
15350 this.fireEvent("loadexception", this, arg, null, e);
15351 callback.call(scope, null, arg, false);
15354 callback.call(scope, result, arg, true);
15358 update : function(params, records){
15363 * Ext JS Library 1.1.1
15364 * Copyright(c) 2006-2007, Ext JS, LLC.
15366 * Originally Released Under LGPL - original licence link has changed is not relivant.
15369 * <script type="text/javascript">
15372 * @class Roo.data.HttpProxy
15373 * @extends Roo.data.DataProxy
15374 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15375 * configured to reference a certain URL.<br><br>
15377 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15378 * from which the running page was served.<br><br>
15380 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15382 * Be aware that to enable the browser to parse an XML document, the server must set
15383 * the Content-Type header in the HTTP response to "text/xml".
15385 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15386 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15387 * will be used to make the request.
15389 Roo.data.HttpProxy = function(conn){
15390 Roo.data.HttpProxy.superclass.constructor.call(this);
15391 // is conn a conn config or a real conn?
15393 this.useAjax = !conn || !conn.events;
15397 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15398 // thse are take from connection...
15401 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15404 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15405 * extra parameters to each request made by this object. (defaults to undefined)
15408 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15409 * to each request made by this object. (defaults to undefined)
15412 * @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)
15415 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15418 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15424 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15428 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15429 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15430 * a finer-grained basis than the DataProxy events.
15432 getConnection : function(){
15433 return this.useAjax ? Roo.Ajax : this.conn;
15437 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15438 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15439 * process that block using the passed callback.
15440 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15441 * for the request to the remote server.
15442 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15443 * object into a block of Roo.data.Records.
15444 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15445 * The function must be passed <ul>
15446 * <li>The Record block object</li>
15447 * <li>The "arg" argument from the load function</li>
15448 * <li>A boolean success indicator</li>
15450 * @param {Object} scope The scope in which to call the callback
15451 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15453 load : function(params, reader, callback, scope, arg){
15454 if(this.fireEvent("beforeload", this, params) !== false){
15456 params : params || {},
15458 callback : callback,
15463 callback : this.loadResponse,
15467 Roo.applyIf(o, this.conn);
15468 if(this.activeRequest){
15469 Roo.Ajax.abort(this.activeRequest);
15471 this.activeRequest = Roo.Ajax.request(o);
15473 this.conn.request(o);
15476 callback.call(scope||this, null, arg, false);
15481 loadResponse : function(o, success, response){
15482 delete this.activeRequest;
15484 this.fireEvent("loadexception", this, o, response);
15485 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15490 result = o.reader.read(response);
15492 this.fireEvent("loadexception", this, o, response, e);
15493 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15497 this.fireEvent("load", this, o, o.request.arg);
15498 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15502 update : function(dataSet){
15507 updateResponse : function(dataSet){
15512 * Ext JS Library 1.1.1
15513 * Copyright(c) 2006-2007, Ext JS, LLC.
15515 * Originally Released Under LGPL - original licence link has changed is not relivant.
15518 * <script type="text/javascript">
15522 * @class Roo.data.ScriptTagProxy
15523 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15524 * other than the originating domain of the running page.<br><br>
15526 * <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
15527 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15529 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15530 * source code that is used as the source inside a <script> tag.<br><br>
15532 * In order for the browser to process the returned data, the server must wrap the data object
15533 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15534 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15535 * depending on whether the callback name was passed:
15538 boolean scriptTag = false;
15539 String cb = request.getParameter("callback");
15542 response.setContentType("text/javascript");
15544 response.setContentType("application/x-json");
15546 Writer out = response.getWriter();
15548 out.write(cb + "(");
15550 out.print(dataBlock.toJsonString());
15557 * @param {Object} config A configuration object.
15559 Roo.data.ScriptTagProxy = function(config){
15560 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15561 Roo.apply(this, config);
15562 this.head = document.getElementsByTagName("head")[0];
15565 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15567 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15569 * @cfg {String} url The URL from which to request the data object.
15572 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15576 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15577 * the server the name of the callback function set up by the load call to process the returned data object.
15578 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15579 * javascript output which calls this named function passing the data object as its only parameter.
15581 callbackParam : "callback",
15583 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15584 * name to the request.
15589 * Load data from the configured URL, read the data object into
15590 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15591 * process that block using the passed callback.
15592 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15593 * for the request to the remote server.
15594 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15595 * object into a block of Roo.data.Records.
15596 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15597 * The function must be passed <ul>
15598 * <li>The Record block object</li>
15599 * <li>The "arg" argument from the load function</li>
15600 * <li>A boolean success indicator</li>
15602 * @param {Object} scope The scope in which to call the callback
15603 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15605 load : function(params, reader, callback, scope, arg){
15606 if(this.fireEvent("beforeload", this, params) !== false){
15608 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15610 var url = this.url;
15611 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15613 url += "&_dc=" + (new Date().getTime());
15615 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15618 cb : "stcCallback"+transId,
15619 scriptId : "stcScript"+transId,
15623 callback : callback,
15629 window[trans.cb] = function(o){
15630 conn.handleResponse(o, trans);
15633 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15635 if(this.autoAbort !== false){
15639 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15641 var script = document.createElement("script");
15642 script.setAttribute("src", url);
15643 script.setAttribute("type", "text/javascript");
15644 script.setAttribute("id", trans.scriptId);
15645 this.head.appendChild(script);
15647 this.trans = trans;
15649 callback.call(scope||this, null, arg, false);
15654 isLoading : function(){
15655 return this.trans ? true : false;
15659 * Abort the current server request.
15661 abort : function(){
15662 if(this.isLoading()){
15663 this.destroyTrans(this.trans);
15668 destroyTrans : function(trans, isLoaded){
15669 this.head.removeChild(document.getElementById(trans.scriptId));
15670 clearTimeout(trans.timeoutId);
15672 window[trans.cb] = undefined;
15674 delete window[trans.cb];
15677 // if hasn't been loaded, wait for load to remove it to prevent script error
15678 window[trans.cb] = function(){
15679 window[trans.cb] = undefined;
15681 delete window[trans.cb];
15688 handleResponse : function(o, trans){
15689 this.trans = false;
15690 this.destroyTrans(trans, true);
15693 result = trans.reader.readRecords(o);
15695 this.fireEvent("loadexception", this, o, trans.arg, e);
15696 trans.callback.call(trans.scope||window, null, trans.arg, false);
15699 this.fireEvent("load", this, o, trans.arg);
15700 trans.callback.call(trans.scope||window, result, trans.arg, true);
15704 handleFailure : function(trans){
15705 this.trans = false;
15706 this.destroyTrans(trans, false);
15707 this.fireEvent("loadexception", this, null, trans.arg);
15708 trans.callback.call(trans.scope||window, null, trans.arg, false);
15712 * Ext JS Library 1.1.1
15713 * Copyright(c) 2006-2007, Ext JS, LLC.
15715 * Originally Released Under LGPL - original licence link has changed is not relivant.
15718 * <script type="text/javascript">
15722 * @class Roo.data.JsonReader
15723 * @extends Roo.data.DataReader
15724 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15725 * based on mappings in a provided Roo.data.Record constructor.
15727 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15728 * in the reply previously.
15733 var RecordDef = Roo.data.Record.create([
15734 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15735 {name: 'occupation'} // This field will use "occupation" as the mapping.
15737 var myReader = new Roo.data.JsonReader({
15738 totalProperty: "results", // The property which contains the total dataset size (optional)
15739 root: "rows", // The property which contains an Array of row objects
15740 id: "id" // The property within each row object that provides an ID for the record (optional)
15744 * This would consume a JSON file like this:
15746 { 'results': 2, 'rows': [
15747 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15748 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15751 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15752 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15753 * paged from the remote server.
15754 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15755 * @cfg {String} root name of the property which contains the Array of row objects.
15756 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15757 * @cfg {Array} fields Array of field definition objects
15759 * Create a new JsonReader
15760 * @param {Object} meta Metadata configuration options
15761 * @param {Object} recordType Either an Array of field definition objects,
15762 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15764 Roo.data.JsonReader = function(meta, recordType){
15767 // set some defaults:
15768 Roo.applyIf(meta, {
15769 totalProperty: 'total',
15770 successProperty : 'success',
15775 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15777 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15779 readerType : 'Json',
15782 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15783 * Used by Store query builder to append _requestMeta to params.
15786 metaFromRemote : false,
15788 * This method is only used by a DataProxy which has retrieved data from a remote server.
15789 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15790 * @return {Object} data A data block which is used by an Roo.data.Store object as
15791 * a cache of Roo.data.Records.
15793 read : function(response){
15794 var json = response.responseText;
15796 var o = /* eval:var:o */ eval("("+json+")");
15798 throw {message: "JsonReader.read: Json object not found"};
15804 this.metaFromRemote = true;
15805 this.meta = o.metaData;
15806 this.recordType = Roo.data.Record.create(o.metaData.fields);
15807 this.onMetaChange(this.meta, this.recordType, o);
15809 return this.readRecords(o);
15812 // private function a store will implement
15813 onMetaChange : function(meta, recordType, o){
15820 simpleAccess: function(obj, subsc) {
15827 getJsonAccessor: function(){
15829 return function(expr) {
15831 return(re.test(expr))
15832 ? new Function("obj", "return obj." + expr)
15837 return Roo.emptyFn;
15842 * Create a data block containing Roo.data.Records from an XML document.
15843 * @param {Object} o An object which contains an Array of row objects in the property specified
15844 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15845 * which contains the total size of the dataset.
15846 * @return {Object} data A data block which is used by an Roo.data.Store object as
15847 * a cache of Roo.data.Records.
15849 readRecords : function(o){
15851 * After any data loads, the raw JSON data is available for further custom processing.
15855 var s = this.meta, Record = this.recordType,
15856 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15858 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15860 if(s.totalProperty) {
15861 this.getTotal = this.getJsonAccessor(s.totalProperty);
15863 if(s.successProperty) {
15864 this.getSuccess = this.getJsonAccessor(s.successProperty);
15866 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15868 var g = this.getJsonAccessor(s.id);
15869 this.getId = function(rec) {
15871 return (r === undefined || r === "") ? null : r;
15874 this.getId = function(){return null;};
15877 for(var jj = 0; jj < fl; jj++){
15879 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15880 this.ef[jj] = this.getJsonAccessor(map);
15884 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15885 if(s.totalProperty){
15886 var vt = parseInt(this.getTotal(o), 10);
15891 if(s.successProperty){
15892 var vs = this.getSuccess(o);
15893 if(vs === false || vs === 'false'){
15898 for(var i = 0; i < c; i++){
15901 var id = this.getId(n);
15902 for(var j = 0; j < fl; j++){
15904 var v = this.ef[j](n);
15906 Roo.log('missing convert for ' + f.name);
15910 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15912 var record = new Record(values, id);
15914 records[i] = record;
15920 totalRecords : totalRecords
15923 // used when loading children.. @see loadDataFromChildren
15924 toLoadData: function(rec)
15926 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15927 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15928 return { data : data, total : data.length };
15933 * Ext JS Library 1.1.1
15934 * Copyright(c) 2006-2007, Ext JS, LLC.
15936 * Originally Released Under LGPL - original licence link has changed is not relivant.
15939 * <script type="text/javascript">
15943 * @class Roo.data.ArrayReader
15944 * @extends Roo.data.DataReader
15945 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15946 * Each element of that Array represents a row of data fields. The
15947 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15948 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15952 var RecordDef = Roo.data.Record.create([
15953 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15954 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15956 var myReader = new Roo.data.ArrayReader({
15957 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15961 * This would consume an Array like this:
15963 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15967 * Create a new JsonReader
15968 * @param {Object} meta Metadata configuration options.
15969 * @param {Object|Array} recordType Either an Array of field definition objects
15971 * @cfg {Array} fields Array of field definition objects
15972 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15973 * as specified to {@link Roo.data.Record#create},
15974 * or an {@link Roo.data.Record} object
15977 * created using {@link Roo.data.Record#create}.
15979 Roo.data.ArrayReader = function(meta, recordType)
15981 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15984 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15987 * Create a data block containing Roo.data.Records from an XML document.
15988 * @param {Object} o An Array of row objects which represents the dataset.
15989 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15990 * a cache of Roo.data.Records.
15992 readRecords : function(o)
15994 var sid = this.meta ? this.meta.id : null;
15995 var recordType = this.recordType, fields = recordType.prototype.fields;
15998 for(var i = 0; i < root.length; i++){
16001 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16002 for(var j = 0, jlen = fields.length; j < jlen; j++){
16003 var f = fields.items[j];
16004 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16005 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16007 values[f.name] = v;
16009 var record = new recordType(values, id);
16011 records[records.length] = record;
16015 totalRecords : records.length
16018 // used when loading children.. @see loadDataFromChildren
16019 toLoadData: function(rec)
16021 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16022 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16033 * @class Roo.bootstrap.ComboBox
16034 * @extends Roo.bootstrap.TriggerField
16035 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16036 * @cfg {Boolean} append (true|false) default false
16037 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16038 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16039 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16040 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16041 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16042 * @cfg {Boolean} animate default true
16043 * @cfg {Boolean} emptyResultText only for touch device
16044 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16045 * @cfg {String} emptyTitle default ''
16046 * @cfg {Number} width fixed with? experimental
16048 * Create a new ComboBox.
16049 * @param {Object} config Configuration options
16051 Roo.bootstrap.ComboBox = function(config){
16052 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16056 * Fires when the dropdown list is expanded
16057 * @param {Roo.bootstrap.ComboBox} combo This combo box
16062 * Fires when the dropdown list is collapsed
16063 * @param {Roo.bootstrap.ComboBox} combo This combo box
16067 * @event beforeselect
16068 * Fires before a list item is selected. Return false to cancel the selection.
16069 * @param {Roo.bootstrap.ComboBox} combo This combo box
16070 * @param {Roo.data.Record} record The data record returned from the underlying store
16071 * @param {Number} index The index of the selected item in the dropdown list
16073 'beforeselect' : true,
16076 * Fires when a list item is selected
16077 * @param {Roo.bootstrap.ComboBox} combo This combo box
16078 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16079 * @param {Number} index The index of the selected item in the dropdown list
16083 * @event beforequery
16084 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16085 * The event object passed has these properties:
16086 * @param {Roo.bootstrap.ComboBox} combo This combo box
16087 * @param {String} query The query
16088 * @param {Boolean} forceAll true to force "all" query
16089 * @param {Boolean} cancel true to cancel the query
16090 * @param {Object} e The query event object
16092 'beforequery': true,
16095 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16096 * @param {Roo.bootstrap.ComboBox} combo This combo box
16101 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16102 * @param {Roo.bootstrap.ComboBox} combo This combo box
16103 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16108 * Fires when the remove value from the combobox array
16109 * @param {Roo.bootstrap.ComboBox} combo This combo box
16113 * @event afterremove
16114 * Fires when the remove value from the combobox array
16115 * @param {Roo.bootstrap.ComboBox} combo This combo box
16117 'afterremove' : true,
16119 * @event specialfilter
16120 * Fires when specialfilter
16121 * @param {Roo.bootstrap.ComboBox} combo This combo box
16123 'specialfilter' : true,
16126 * Fires when tick the element
16127 * @param {Roo.bootstrap.ComboBox} combo This combo box
16131 * @event touchviewdisplay
16132 * Fires when touch view require special display (default is using displayField)
16133 * @param {Roo.bootstrap.ComboBox} combo This combo box
16134 * @param {Object} cfg set html .
16136 'touchviewdisplay' : true
16141 this.tickItems = [];
16143 this.selectedIndex = -1;
16144 if(this.mode == 'local'){
16145 if(config.queryDelay === undefined){
16146 this.queryDelay = 10;
16148 if(config.minChars === undefined){
16154 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16157 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16158 * rendering into an Roo.Editor, defaults to false)
16161 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16162 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16165 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16168 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16169 * the dropdown list (defaults to undefined, with no header element)
16173 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16177 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16179 listWidth: undefined,
16181 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16182 * mode = 'remote' or 'text' if mode = 'local')
16184 displayField: undefined,
16187 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16188 * mode = 'remote' or 'value' if mode = 'local').
16189 * Note: use of a valueField requires the user make a selection
16190 * in order for a value to be mapped.
16192 valueField: undefined,
16194 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16199 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16200 * field's data value (defaults to the underlying DOM element's name)
16202 hiddenName: undefined,
16204 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16208 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16210 selectedClass: 'active',
16213 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16217 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16218 * anchor positions (defaults to 'tl-bl')
16220 listAlign: 'tl-bl?',
16222 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16226 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16227 * query specified by the allQuery config option (defaults to 'query')
16229 triggerAction: 'query',
16231 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16232 * (defaults to 4, does not apply if editable = false)
16236 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16237 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16241 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16242 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16246 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16247 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16251 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16252 * when editable = true (defaults to false)
16254 selectOnFocus:false,
16256 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16258 queryParam: 'query',
16260 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16261 * when mode = 'remote' (defaults to 'Loading...')
16263 loadingText: 'Loading...',
16265 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16269 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16273 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16274 * traditional select (defaults to true)
16278 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16282 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16286 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16287 * listWidth has a higher value)
16291 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16292 * allow the user to set arbitrary text into the field (defaults to false)
16294 forceSelection:false,
16296 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16297 * if typeAhead = true (defaults to 250)
16299 typeAheadDelay : 250,
16301 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16302 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16304 valueNotFoundText : undefined,
16306 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16308 blockFocus : false,
16311 * @cfg {Boolean} disableClear Disable showing of clear button.
16313 disableClear : false,
16315 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16317 alwaysQuery : false,
16320 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16325 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16327 invalidClass : "has-warning",
16330 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16332 validClass : "has-success",
16335 * @cfg {Boolean} specialFilter (true|false) special filter default false
16337 specialFilter : false,
16340 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16342 mobileTouchView : true,
16345 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16347 useNativeIOS : false,
16350 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16352 mobile_restrict_height : false,
16354 ios_options : false,
16366 btnPosition : 'right',
16367 triggerList : true,
16368 showToggleBtn : true,
16370 emptyResultText: 'Empty',
16371 triggerText : 'Select',
16375 // element that contains real text value.. (when hidden is used..)
16377 getAutoCreate : function()
16382 * Render classic select for iso
16385 if(Roo.isIOS && this.useNativeIOS){
16386 cfg = this.getAutoCreateNativeIOS();
16394 if(Roo.isTouch && this.mobileTouchView){
16395 cfg = this.getAutoCreateTouchView();
16402 if(!this.tickable){
16403 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16408 * ComboBox with tickable selections
16411 var align = this.labelAlign || this.parentLabelAlign();
16414 cls : 'form-group roo-combobox-tickable' //input-group
16417 var btn_text_select = '';
16418 var btn_text_done = '';
16419 var btn_text_cancel = '';
16421 if (this.btn_text_show) {
16422 btn_text_select = 'Select';
16423 btn_text_done = 'Done';
16424 btn_text_cancel = 'Cancel';
16429 cls : 'tickable-buttons',
16434 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16435 //html : this.triggerText
16436 html: btn_text_select
16442 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16444 html: btn_text_done
16450 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16452 html: btn_text_cancel
16458 buttons.cn.unshift({
16460 cls: 'roo-select2-search-field-input'
16466 Roo.each(buttons.cn, function(c){
16468 c.cls += ' btn-' + _this.size;
16471 if (_this.disabled) {
16478 style : 'display: contents',
16483 cls: 'form-hidden-field'
16487 cls: 'roo-select2-choices',
16491 cls: 'roo-select2-search-field',
16502 cls: 'roo-select2-container input-group roo-select2-container-multi',
16508 // cls: 'typeahead typeahead-long dropdown-menu',
16509 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16514 if(this.hasFeedback && !this.allowBlank){
16518 cls: 'glyphicon form-control-feedback'
16521 combobox.cn.push(feedback);
16528 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16529 tooltip : 'This field is required'
16531 if (Roo.bootstrap.version == 4) {
16534 style : 'display:none'
16537 if (align ==='left' && this.fieldLabel.length) {
16539 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16546 cls : 'control-label col-form-label',
16547 html : this.fieldLabel
16559 var labelCfg = cfg.cn[1];
16560 var contentCfg = cfg.cn[2];
16563 if(this.indicatorpos == 'right'){
16569 cls : 'control-label col-form-label',
16573 html : this.fieldLabel
16589 labelCfg = cfg.cn[0];
16590 contentCfg = cfg.cn[1];
16594 if(this.labelWidth > 12){
16595 labelCfg.style = "width: " + this.labelWidth + 'px';
16597 if(this.width * 1 > 0){
16598 contentCfg.style = "width: " + this.width + 'px';
16600 if(this.labelWidth < 13 && this.labelmd == 0){
16601 this.labelmd = this.labelWidth;
16604 if(this.labellg > 0){
16605 labelCfg.cls += ' col-lg-' + this.labellg;
16606 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16609 if(this.labelmd > 0){
16610 labelCfg.cls += ' col-md-' + this.labelmd;
16611 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16614 if(this.labelsm > 0){
16615 labelCfg.cls += ' col-sm-' + this.labelsm;
16616 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16619 if(this.labelxs > 0){
16620 labelCfg.cls += ' col-xs-' + this.labelxs;
16621 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16625 } else if ( this.fieldLabel.length) {
16626 // Roo.log(" label");
16631 //cls : 'input-group-addon',
16632 html : this.fieldLabel
16637 if(this.indicatorpos == 'right'){
16641 //cls : 'input-group-addon',
16642 html : this.fieldLabel
16652 // Roo.log(" no label && no align");
16659 ['xs','sm','md','lg'].map(function(size){
16660 if (settings[size]) {
16661 cfg.cls += ' col-' + size + '-' + settings[size];
16669 _initEventsCalled : false,
16672 initEvents: function()
16674 if (this._initEventsCalled) { // as we call render... prevent looping...
16677 this._initEventsCalled = true;
16680 throw "can not find store for combo";
16683 this.indicator = this.indicatorEl();
16685 this.store = Roo.factory(this.store, Roo.data);
16686 this.store.parent = this;
16688 // if we are building from html. then this element is so complex, that we can not really
16689 // use the rendered HTML.
16690 // so we have to trash and replace the previous code.
16691 if (Roo.XComponent.build_from_html) {
16692 // remove this element....
16693 var e = this.el.dom, k=0;
16694 while (e ) { e = e.previousSibling; ++k;}
16699 this.rendered = false;
16701 this.render(this.parent().getChildContainer(true), k);
16704 if(Roo.isIOS && this.useNativeIOS){
16705 this.initIOSView();
16713 if(Roo.isTouch && this.mobileTouchView){
16714 this.initTouchView();
16719 this.initTickableEvents();
16723 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16725 if(this.hiddenName){
16727 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16729 this.hiddenField.dom.value =
16730 this.hiddenValue !== undefined ? this.hiddenValue :
16731 this.value !== undefined ? this.value : '';
16733 // prevent input submission
16734 this.el.dom.removeAttribute('name');
16735 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16740 // this.el.dom.setAttribute('autocomplete', 'off');
16743 var cls = 'x-combo-list';
16745 //this.list = new Roo.Layer({
16746 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16752 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16753 _this.list.setWidth(lw);
16756 this.list.on('mouseover', this.onViewOver, this);
16757 this.list.on('mousemove', this.onViewMove, this);
16758 this.list.on('scroll', this.onViewScroll, this);
16761 this.list.swallowEvent('mousewheel');
16762 this.assetHeight = 0;
16765 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16766 this.assetHeight += this.header.getHeight();
16769 this.innerList = this.list.createChild({cls:cls+'-inner'});
16770 this.innerList.on('mouseover', this.onViewOver, this);
16771 this.innerList.on('mousemove', this.onViewMove, this);
16772 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16774 if(this.allowBlank && !this.pageSize && !this.disableClear){
16775 this.footer = this.list.createChild({cls:cls+'-ft'});
16776 this.pageTb = new Roo.Toolbar(this.footer);
16780 this.footer = this.list.createChild({cls:cls+'-ft'});
16781 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16782 {pageSize: this.pageSize});
16786 if (this.pageTb && this.allowBlank && !this.disableClear) {
16788 this.pageTb.add(new Roo.Toolbar.Fill(), {
16789 cls: 'x-btn-icon x-btn-clear',
16791 handler: function()
16794 _this.clearValue();
16795 _this.onSelect(false, -1);
16800 this.assetHeight += this.footer.getHeight();
16805 this.tpl = Roo.bootstrap.version == 4 ?
16806 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16807 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16810 this.view = new Roo.View(this.list, this.tpl, {
16811 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16813 //this.view.wrapEl.setDisplayed(false);
16814 this.view.on('click', this.onViewClick, this);
16817 this.store.on('beforeload', this.onBeforeLoad, this);
16818 this.store.on('load', this.onLoad, this);
16819 this.store.on('loadexception', this.onLoadException, this);
16821 if(this.resizable){
16822 this.resizer = new Roo.Resizable(this.list, {
16823 pinned:true, handles:'se'
16825 this.resizer.on('resize', function(r, w, h){
16826 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16827 this.listWidth = w;
16828 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16829 this.restrictHeight();
16831 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16834 if(!this.editable){
16835 this.editable = true;
16836 this.setEditable(false);
16841 if (typeof(this.events.add.listeners) != 'undefined') {
16843 this.addicon = this.wrap.createChild(
16844 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16846 this.addicon.on('click', function(e) {
16847 this.fireEvent('add', this);
16850 if (typeof(this.events.edit.listeners) != 'undefined') {
16852 this.editicon = this.wrap.createChild(
16853 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16854 if (this.addicon) {
16855 this.editicon.setStyle('margin-left', '40px');
16857 this.editicon.on('click', function(e) {
16859 // we fire even if inothing is selected..
16860 this.fireEvent('edit', this, this.lastData );
16866 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16867 "up" : function(e){
16868 this.inKeyMode = true;
16872 "down" : function(e){
16873 if(!this.isExpanded()){
16874 this.onTriggerClick();
16876 this.inKeyMode = true;
16881 "enter" : function(e){
16882 // this.onViewClick();
16886 if(this.fireEvent("specialkey", this, e)){
16887 this.onViewClick(false);
16893 "esc" : function(e){
16897 "tab" : function(e){
16900 if(this.fireEvent("specialkey", this, e)){
16901 this.onViewClick(false);
16909 doRelay : function(foo, bar, hname){
16910 if(hname == 'down' || this.scope.isExpanded()){
16911 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16920 this.queryDelay = Math.max(this.queryDelay || 10,
16921 this.mode == 'local' ? 10 : 250);
16924 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16926 if(this.typeAhead){
16927 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16929 if(this.editable !== false){
16930 this.inputEl().on("keyup", this.onKeyUp, this);
16932 if(this.forceSelection){
16933 this.inputEl().on('blur', this.doForce, this);
16937 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16938 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16942 initTickableEvents: function()
16946 if(this.hiddenName){
16948 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16950 this.hiddenField.dom.value =
16951 this.hiddenValue !== undefined ? this.hiddenValue :
16952 this.value !== undefined ? this.value : '';
16954 // prevent input submission
16955 this.el.dom.removeAttribute('name');
16956 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16961 // this.list = this.el.select('ul.dropdown-menu',true).first();
16963 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16964 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16965 if(this.triggerList){
16966 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16969 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16970 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16972 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16973 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16975 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16976 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16978 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16979 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16980 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16983 this.cancelBtn.hide();
16988 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16989 _this.list.setWidth(lw);
16992 this.list.on('mouseover', this.onViewOver, this);
16993 this.list.on('mousemove', this.onViewMove, this);
16995 this.list.on('scroll', this.onViewScroll, this);
16998 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16999 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17002 this.view = new Roo.View(this.list, this.tpl, {
17007 selectedClass: this.selectedClass
17010 //this.view.wrapEl.setDisplayed(false);
17011 this.view.on('click', this.onViewClick, this);
17015 this.store.on('beforeload', this.onBeforeLoad, this);
17016 this.store.on('load', this.onLoad, this);
17017 this.store.on('loadexception', this.onLoadException, this);
17020 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17021 "up" : function(e){
17022 this.inKeyMode = true;
17026 "down" : function(e){
17027 this.inKeyMode = true;
17031 "enter" : function(e){
17032 if(this.fireEvent("specialkey", this, e)){
17033 this.onViewClick(false);
17039 "esc" : function(e){
17040 this.onTickableFooterButtonClick(e, false, false);
17043 "tab" : function(e){
17044 this.fireEvent("specialkey", this, e);
17046 this.onTickableFooterButtonClick(e, false, false);
17053 doRelay : function(e, fn, key){
17054 if(this.scope.isExpanded()){
17055 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17064 this.queryDelay = Math.max(this.queryDelay || 10,
17065 this.mode == 'local' ? 10 : 250);
17068 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17070 if(this.typeAhead){
17071 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17074 if(this.editable !== false){
17075 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17078 this.indicator = this.indicatorEl();
17080 if(this.indicator){
17081 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17082 this.indicator.hide();
17087 onDestroy : function(){
17089 this.view.setStore(null);
17090 this.view.el.removeAllListeners();
17091 this.view.el.remove();
17092 this.view.purgeListeners();
17095 this.list.dom.innerHTML = '';
17099 this.store.un('beforeload', this.onBeforeLoad, this);
17100 this.store.un('load', this.onLoad, this);
17101 this.store.un('loadexception', this.onLoadException, this);
17103 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17107 fireKey : function(e){
17108 if(e.isNavKeyPress() && !this.list.isVisible()){
17109 this.fireEvent("specialkey", this, e);
17114 onResize: function(w, h)
17118 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17120 // if(typeof w != 'number'){
17121 // // we do not handle it!?!?
17124 // var tw = this.trigger.getWidth();
17125 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17126 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17128 // this.inputEl().setWidth( this.adjustWidth('input', x));
17130 // //this.trigger.setStyle('left', x+'px');
17132 // if(this.list && this.listWidth === undefined){
17133 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17134 // this.list.setWidth(lw);
17135 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17143 * Allow or prevent the user from directly editing the field text. If false is passed,
17144 * the user will only be able to select from the items defined in the dropdown list. This method
17145 * is the runtime equivalent of setting the 'editable' config option at config time.
17146 * @param {Boolean} value True to allow the user to directly edit the field text
17148 setEditable : function(value){
17149 if(value == this.editable){
17152 this.editable = value;
17154 this.inputEl().dom.setAttribute('readOnly', true);
17155 this.inputEl().on('mousedown', this.onTriggerClick, this);
17156 this.inputEl().addClass('x-combo-noedit');
17158 this.inputEl().dom.removeAttribute('readOnly');
17159 this.inputEl().un('mousedown', this.onTriggerClick, this);
17160 this.inputEl().removeClass('x-combo-noedit');
17166 onBeforeLoad : function(combo,opts){
17167 if(!this.hasFocus){
17171 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17173 this.restrictHeight();
17174 this.selectedIndex = -1;
17178 onLoad : function(){
17180 this.hasQuery = false;
17182 if(!this.hasFocus){
17186 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17187 this.loading.hide();
17190 if(this.store.getCount() > 0){
17193 this.restrictHeight();
17194 if(this.lastQuery == this.allQuery){
17195 if(this.editable && !this.tickable){
17196 this.inputEl().dom.select();
17200 !this.selectByValue(this.value, true) &&
17203 !this.store.lastOptions ||
17204 typeof(this.store.lastOptions.add) == 'undefined' ||
17205 this.store.lastOptions.add != true
17208 this.select(0, true);
17211 if(this.autoFocus){
17214 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17215 this.taTask.delay(this.typeAheadDelay);
17219 this.onEmptyResults();
17225 onLoadException : function()
17227 this.hasQuery = false;
17229 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17230 this.loading.hide();
17233 if(this.tickable && this.editable){
17238 // only causes errors at present
17239 //Roo.log(this.store.reader.jsonData);
17240 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17242 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17248 onTypeAhead : function(){
17249 if(this.store.getCount() > 0){
17250 var r = this.store.getAt(0);
17251 var newValue = r.data[this.displayField];
17252 var len = newValue.length;
17253 var selStart = this.getRawValue().length;
17255 if(selStart != len){
17256 this.setRawValue(newValue);
17257 this.selectText(selStart, newValue.length);
17263 onSelect : function(record, index){
17265 if(this.fireEvent('beforeselect', this, record, index) !== false){
17267 this.setFromData(index > -1 ? record.data : false);
17270 this.fireEvent('select', this, record, index);
17275 * Returns the currently selected field value or empty string if no value is set.
17276 * @return {String} value The selected value
17278 getValue : function()
17280 if(Roo.isIOS && this.useNativeIOS){
17281 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17285 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17288 if(this.valueField){
17289 return typeof this.value != 'undefined' ? this.value : '';
17291 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17295 getRawValue : function()
17297 if(Roo.isIOS && this.useNativeIOS){
17298 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17301 var v = this.inputEl().getValue();
17307 * Clears any text/value currently set in the field
17309 clearValue : function(){
17311 if(this.hiddenField){
17312 this.hiddenField.dom.value = '';
17315 this.setRawValue('');
17316 this.lastSelectionText = '';
17317 this.lastData = false;
17319 var close = this.closeTriggerEl();
17330 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17331 * will be displayed in the field. If the value does not match the data value of an existing item,
17332 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17333 * Otherwise the field will be blank (although the value will still be set).
17334 * @param {String} value The value to match
17336 setValue : function(v)
17338 if(Roo.isIOS && this.useNativeIOS){
17339 this.setIOSValue(v);
17349 if(this.valueField){
17350 var r = this.findRecord(this.valueField, v);
17352 text = r.data[this.displayField];
17353 }else if(this.valueNotFoundText !== undefined){
17354 text = this.valueNotFoundText;
17357 this.lastSelectionText = text;
17358 if(this.hiddenField){
17359 this.hiddenField.dom.value = v;
17361 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17364 var close = this.closeTriggerEl();
17367 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17373 * @property {Object} the last set data for the element
17378 * Sets the value of the field based on a object which is related to the record format for the store.
17379 * @param {Object} value the value to set as. or false on reset?
17381 setFromData : function(o){
17388 var dv = ''; // display value
17389 var vv = ''; // value value..
17391 if (this.displayField) {
17392 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17394 // this is an error condition!!!
17395 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17398 if(this.valueField){
17399 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17402 var close = this.closeTriggerEl();
17405 if(dv.length || vv * 1 > 0){
17407 this.blockFocus=true;
17413 if(this.hiddenField){
17414 this.hiddenField.dom.value = vv;
17416 this.lastSelectionText = dv;
17417 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17421 // no hidden field.. - we store the value in 'value', but still display
17422 // display field!!!!
17423 this.lastSelectionText = dv;
17424 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17431 reset : function(){
17432 // overridden so that last data is reset..
17439 this.setValue(this.originalValue);
17440 //this.clearInvalid();
17441 this.lastData = false;
17443 this.view.clearSelections();
17449 findRecord : function(prop, value){
17451 if(this.store.getCount() > 0){
17452 this.store.each(function(r){
17453 if(r.data[prop] == value){
17463 getName: function()
17465 // returns hidden if it's set..
17466 if (!this.rendered) {return ''};
17467 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17471 onViewMove : function(e, t){
17472 this.inKeyMode = false;
17476 onViewOver : function(e, t){
17477 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17480 var item = this.view.findItemFromChild(t);
17483 var index = this.view.indexOf(item);
17484 this.select(index, false);
17489 onViewClick : function(view, doFocus, el, e)
17491 var index = this.view.getSelectedIndexes()[0];
17493 var r = this.store.getAt(index);
17497 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17504 Roo.each(this.tickItems, function(v,k){
17506 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17508 _this.tickItems.splice(k, 1);
17510 if(typeof(e) == 'undefined' && view == false){
17511 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17523 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17524 this.tickItems.push(r.data);
17527 if(typeof(e) == 'undefined' && view == false){
17528 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17535 this.onSelect(r, index);
17537 if(doFocus !== false && !this.blockFocus){
17538 this.inputEl().focus();
17543 restrictHeight : function(){
17544 //this.innerList.dom.style.height = '';
17545 //var inner = this.innerList.dom;
17546 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17547 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17548 //this.list.beginUpdate();
17549 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17550 this.list.alignTo(this.inputEl(), this.listAlign);
17551 this.list.alignTo(this.inputEl(), this.listAlign);
17552 //this.list.endUpdate();
17556 onEmptyResults : function(){
17558 if(this.tickable && this.editable){
17559 this.hasFocus = false;
17560 this.restrictHeight();
17568 * Returns true if the dropdown list is expanded, else false.
17570 isExpanded : function(){
17571 return this.list.isVisible();
17575 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17576 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17577 * @param {String} value The data value of the item to select
17578 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17579 * selected item if it is not currently in view (defaults to true)
17580 * @return {Boolean} True if the value matched an item in the list, else false
17582 selectByValue : function(v, scrollIntoView){
17583 if(v !== undefined && v !== null){
17584 var r = this.findRecord(this.valueField || this.displayField, v);
17586 this.select(this.store.indexOf(r), scrollIntoView);
17594 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17595 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17596 * @param {Number} index The zero-based index of the list item to select
17597 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17598 * selected item if it is not currently in view (defaults to true)
17600 select : function(index, scrollIntoView){
17601 this.selectedIndex = index;
17602 this.view.select(index);
17603 if(scrollIntoView !== false){
17604 var el = this.view.getNode(index);
17606 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17609 this.list.scrollChildIntoView(el, false);
17615 selectNext : function(){
17616 var ct = this.store.getCount();
17618 if(this.selectedIndex == -1){
17620 }else if(this.selectedIndex < ct-1){
17621 this.select(this.selectedIndex+1);
17627 selectPrev : function(){
17628 var ct = this.store.getCount();
17630 if(this.selectedIndex == -1){
17632 }else if(this.selectedIndex != 0){
17633 this.select(this.selectedIndex-1);
17639 onKeyUp : function(e){
17640 if(this.editable !== false && !e.isSpecialKey()){
17641 this.lastKey = e.getKey();
17642 this.dqTask.delay(this.queryDelay);
17647 validateBlur : function(){
17648 return !this.list || !this.list.isVisible();
17652 initQuery : function(){
17654 var v = this.getRawValue();
17656 if(this.tickable && this.editable){
17657 v = this.tickableInputEl().getValue();
17664 doForce : function(){
17665 if(this.inputEl().dom.value.length > 0){
17666 this.inputEl().dom.value =
17667 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17673 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17674 * query allowing the query action to be canceled if needed.
17675 * @param {String} query The SQL query to execute
17676 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17677 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17678 * saved in the current store (defaults to false)
17680 doQuery : function(q, forceAll){
17682 if(q === undefined || q === null){
17687 forceAll: forceAll,
17691 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17696 forceAll = qe.forceAll;
17697 if(forceAll === true || (q.length >= this.minChars)){
17699 this.hasQuery = true;
17701 if(this.lastQuery != q || this.alwaysQuery){
17702 this.lastQuery = q;
17703 if(this.mode == 'local'){
17704 this.selectedIndex = -1;
17706 this.store.clearFilter();
17709 if(this.specialFilter){
17710 this.fireEvent('specialfilter', this);
17715 this.store.filter(this.displayField, q);
17718 this.store.fireEvent("datachanged", this.store);
17725 this.store.baseParams[this.queryParam] = q;
17727 var options = {params : this.getParams(q)};
17730 options.add = true;
17731 options.params.start = this.page * this.pageSize;
17734 this.store.load(options);
17737 * this code will make the page width larger, at the beginning, the list not align correctly,
17738 * we should expand the list on onLoad
17739 * so command out it
17744 this.selectedIndex = -1;
17749 this.loadNext = false;
17753 getParams : function(q){
17755 //p[this.queryParam] = q;
17759 p.limit = this.pageSize;
17765 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17767 collapse : function(){
17768 if(!this.isExpanded()){
17774 this.hasFocus = false;
17778 this.cancelBtn.hide();
17779 this.trigger.show();
17782 this.tickableInputEl().dom.value = '';
17783 this.tickableInputEl().blur();
17788 Roo.get(document).un('mousedown', this.collapseIf, this);
17789 Roo.get(document).un('mousewheel', this.collapseIf, this);
17790 if (!this.editable) {
17791 Roo.get(document).un('keydown', this.listKeyPress, this);
17793 this.fireEvent('collapse', this);
17799 collapseIf : function(e){
17800 var in_combo = e.within(this.el);
17801 var in_list = e.within(this.list);
17802 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17804 if (in_combo || in_list || is_list) {
17805 //e.stopPropagation();
17810 this.onTickableFooterButtonClick(e, false, false);
17818 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17820 expand : function(){
17822 if(this.isExpanded() || !this.hasFocus){
17826 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17827 this.list.setWidth(lw);
17833 this.restrictHeight();
17837 this.tickItems = Roo.apply([], this.item);
17840 this.cancelBtn.show();
17841 this.trigger.hide();
17844 this.tickableInputEl().focus();
17849 Roo.get(document).on('mousedown', this.collapseIf, this);
17850 Roo.get(document).on('mousewheel', this.collapseIf, this);
17851 if (!this.editable) {
17852 Roo.get(document).on('keydown', this.listKeyPress, this);
17855 this.fireEvent('expand', this);
17859 // Implements the default empty TriggerField.onTriggerClick function
17860 onTriggerClick : function(e)
17862 Roo.log('trigger click');
17864 if(this.disabled || !this.triggerList){
17869 this.loadNext = false;
17871 if(this.isExpanded()){
17873 if (!this.blockFocus) {
17874 this.inputEl().focus();
17878 this.hasFocus = true;
17879 if(this.triggerAction == 'all') {
17880 this.doQuery(this.allQuery, true);
17882 this.doQuery(this.getRawValue());
17884 if (!this.blockFocus) {
17885 this.inputEl().focus();
17890 onTickableTriggerClick : function(e)
17897 this.loadNext = false;
17898 this.hasFocus = true;
17900 if(this.triggerAction == 'all') {
17901 this.doQuery(this.allQuery, true);
17903 this.doQuery(this.getRawValue());
17907 onSearchFieldClick : function(e)
17909 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17910 this.onTickableFooterButtonClick(e, false, false);
17914 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17919 this.loadNext = false;
17920 this.hasFocus = true;
17922 if(this.triggerAction == 'all') {
17923 this.doQuery(this.allQuery, true);
17925 this.doQuery(this.getRawValue());
17929 listKeyPress : function(e)
17931 //Roo.log('listkeypress');
17932 // scroll to first matching element based on key pres..
17933 if (e.isSpecialKey()) {
17936 var k = String.fromCharCode(e.getKey()).toUpperCase();
17939 var csel = this.view.getSelectedNodes();
17940 var cselitem = false;
17942 var ix = this.view.indexOf(csel[0]);
17943 cselitem = this.store.getAt(ix);
17944 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17950 this.store.each(function(v) {
17952 // start at existing selection.
17953 if (cselitem.id == v.id) {
17959 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17960 match = this.store.indexOf(v);
17966 if (match === false) {
17967 return true; // no more action?
17970 this.view.select(match);
17971 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17972 sn.scrollIntoView(sn.dom.parentNode, false);
17975 onViewScroll : function(e, t){
17977 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){
17981 this.hasQuery = true;
17983 this.loading = this.list.select('.loading', true).first();
17985 if(this.loading === null){
17986 this.list.createChild({
17988 cls: 'loading roo-select2-more-results roo-select2-active',
17989 html: 'Loading more results...'
17992 this.loading = this.list.select('.loading', true).first();
17994 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17996 this.loading.hide();
17999 this.loading.show();
18004 this.loadNext = true;
18006 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18011 addItem : function(o)
18013 var dv = ''; // display value
18015 if (this.displayField) {
18016 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18018 // this is an error condition!!!
18019 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18026 var choice = this.choices.createChild({
18028 cls: 'roo-select2-search-choice',
18037 cls: 'roo-select2-search-choice-close fa fa-times',
18042 }, this.searchField);
18044 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18046 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18054 this.inputEl().dom.value = '';
18059 onRemoveItem : function(e, _self, o)
18061 e.preventDefault();
18063 this.lastItem = Roo.apply([], this.item);
18065 var index = this.item.indexOf(o.data) * 1;
18068 Roo.log('not this item?!');
18072 this.item.splice(index, 1);
18077 this.fireEvent('remove', this, e);
18083 syncValue : function()
18085 if(!this.item.length){
18092 Roo.each(this.item, function(i){
18093 if(_this.valueField){
18094 value.push(i[_this.valueField]);
18101 this.value = value.join(',');
18103 if(this.hiddenField){
18104 this.hiddenField.dom.value = this.value;
18107 this.store.fireEvent("datachanged", this.store);
18112 clearItem : function()
18114 if(!this.multiple){
18120 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18128 if(this.tickable && !Roo.isTouch){
18129 this.view.refresh();
18133 inputEl: function ()
18135 if(Roo.isIOS && this.useNativeIOS){
18136 return this.el.select('select.roo-ios-select', true).first();
18139 if(Roo.isTouch && this.mobileTouchView){
18140 return this.el.select('input.form-control',true).first();
18144 return this.searchField;
18147 return this.el.select('input.form-control',true).first();
18150 onTickableFooterButtonClick : function(e, btn, el)
18152 e.preventDefault();
18154 this.lastItem = Roo.apply([], this.item);
18156 if(btn && btn.name == 'cancel'){
18157 this.tickItems = Roo.apply([], this.item);
18166 Roo.each(this.tickItems, function(o){
18174 validate : function()
18176 if(this.getVisibilityEl().hasClass('hidden')){
18180 var v = this.getRawValue();
18183 v = this.getValue();
18186 if(this.disabled || this.allowBlank || v.length){
18191 this.markInvalid();
18195 tickableInputEl : function()
18197 if(!this.tickable || !this.editable){
18198 return this.inputEl();
18201 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18205 getAutoCreateTouchView : function()
18210 cls: 'form-group' //input-group
18216 type : this.inputType,
18217 cls : 'form-control x-combo-noedit',
18218 autocomplete: 'new-password',
18219 placeholder : this.placeholder || '',
18224 input.name = this.name;
18228 input.cls += ' input-' + this.size;
18231 if (this.disabled) {
18232 input.disabled = true;
18236 cls : 'roo-combobox-wrap',
18243 inputblock.cls += ' input-group';
18245 inputblock.cn.unshift({
18247 cls : 'input-group-addon input-group-prepend input-group-text',
18252 if(this.removable && !this.multiple){
18253 inputblock.cls += ' roo-removable';
18255 inputblock.cn.push({
18258 cls : 'roo-combo-removable-btn close'
18262 if(this.hasFeedback && !this.allowBlank){
18264 inputblock.cls += ' has-feedback';
18266 inputblock.cn.push({
18268 cls: 'glyphicon form-control-feedback'
18275 inputblock.cls += (this.before) ? '' : ' input-group';
18277 inputblock.cn.push({
18279 cls : 'input-group-addon input-group-append input-group-text',
18285 var ibwrap = inputblock;
18290 cls: 'roo-select2-choices',
18294 cls: 'roo-select2-search-field',
18307 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18312 cls: 'form-hidden-field'
18318 if(!this.multiple && this.showToggleBtn){
18324 if (this.caret != false) {
18327 cls: 'fa fa-' + this.caret
18334 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18336 Roo.bootstrap.version == 3 ? caret : '',
18339 cls: 'combobox-clear',
18353 combobox.cls += ' roo-select2-container-multi';
18356 var required = this.allowBlank ? {
18358 style: 'display: none'
18361 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18362 tooltip : 'This field is required'
18365 var align = this.labelAlign || this.parentLabelAlign();
18367 if (align ==='left' && this.fieldLabel.length) {
18373 cls : 'control-label col-form-label',
18374 html : this.fieldLabel
18378 cls : 'roo-combobox-wrap ',
18385 var labelCfg = cfg.cn[1];
18386 var contentCfg = cfg.cn[2];
18389 if(this.indicatorpos == 'right'){
18394 cls : 'control-label col-form-label',
18398 html : this.fieldLabel
18404 cls : "roo-combobox-wrap ",
18412 labelCfg = cfg.cn[0];
18413 contentCfg = cfg.cn[1];
18418 if(this.labelWidth > 12){
18419 labelCfg.style = "width: " + this.labelWidth + 'px';
18422 if(this.labelWidth < 13 && this.labelmd == 0){
18423 this.labelmd = this.labelWidth;
18426 if(this.labellg > 0){
18427 labelCfg.cls += ' col-lg-' + this.labellg;
18428 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18431 if(this.labelmd > 0){
18432 labelCfg.cls += ' col-md-' + this.labelmd;
18433 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18436 if(this.labelsm > 0){
18437 labelCfg.cls += ' col-sm-' + this.labelsm;
18438 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18441 if(this.labelxs > 0){
18442 labelCfg.cls += ' col-xs-' + this.labelxs;
18443 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18447 } else if ( this.fieldLabel.length) {
18452 cls : 'control-label',
18453 html : this.fieldLabel
18464 if(this.indicatorpos == 'right'){
18468 cls : 'control-label',
18469 html : this.fieldLabel,
18487 var settings = this;
18489 ['xs','sm','md','lg'].map(function(size){
18490 if (settings[size]) {
18491 cfg.cls += ' col-' + size + '-' + settings[size];
18498 initTouchView : function()
18500 this.renderTouchView();
18502 this.touchViewEl.on('scroll', function(){
18503 this.el.dom.scrollTop = 0;
18506 this.originalValue = this.getValue();
18508 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18510 this.inputEl().on("click", this.showTouchView, this);
18511 if (this.triggerEl) {
18512 this.triggerEl.on("click", this.showTouchView, this);
18516 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18517 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18519 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18521 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18522 this.store.on('load', this.onTouchViewLoad, this);
18523 this.store.on('loadexception', this.onTouchViewLoadException, this);
18525 if(this.hiddenName){
18527 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18529 this.hiddenField.dom.value =
18530 this.hiddenValue !== undefined ? this.hiddenValue :
18531 this.value !== undefined ? this.value : '';
18533 this.el.dom.removeAttribute('name');
18534 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18538 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18539 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18542 if(this.removable && !this.multiple){
18543 var close = this.closeTriggerEl();
18545 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18546 close.on('click', this.removeBtnClick, this, close);
18550 * fix the bug in Safari iOS8
18552 this.inputEl().on("focus", function(e){
18553 document.activeElement.blur();
18556 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18563 renderTouchView : function()
18565 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18566 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18568 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18569 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18571 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18572 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18573 this.touchViewBodyEl.setStyle('overflow', 'auto');
18575 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18576 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18578 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18579 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18583 showTouchView : function()
18589 this.touchViewHeaderEl.hide();
18591 if(this.modalTitle.length){
18592 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18593 this.touchViewHeaderEl.show();
18596 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18597 this.touchViewEl.show();
18599 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18601 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18602 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18604 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18606 if(this.modalTitle.length){
18607 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18610 this.touchViewBodyEl.setHeight(bodyHeight);
18614 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18616 this.touchViewEl.addClass(['in','show']);
18619 if(this._touchViewMask){
18620 Roo.get(document.body).addClass("x-body-masked");
18621 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18622 this._touchViewMask.setStyle('z-index', 10000);
18623 this._touchViewMask.addClass('show');
18626 this.doTouchViewQuery();
18630 hideTouchView : function()
18632 this.touchViewEl.removeClass(['in','show']);
18636 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18638 this.touchViewEl.setStyle('display', 'none');
18641 if(this._touchViewMask){
18642 this._touchViewMask.removeClass('show');
18643 Roo.get(document.body).removeClass("x-body-masked");
18647 setTouchViewValue : function()
18654 Roo.each(this.tickItems, function(o){
18659 this.hideTouchView();
18662 doTouchViewQuery : function()
18671 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18675 if(!this.alwaysQuery || this.mode == 'local'){
18676 this.onTouchViewLoad();
18683 onTouchViewBeforeLoad : function(combo,opts)
18689 onTouchViewLoad : function()
18691 if(this.store.getCount() < 1){
18692 this.onTouchViewEmptyResults();
18696 this.clearTouchView();
18698 var rawValue = this.getRawValue();
18700 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18702 this.tickItems = [];
18704 this.store.data.each(function(d, rowIndex){
18705 var row = this.touchViewListGroup.createChild(template);
18707 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18708 row.addClass(d.data.cls);
18711 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18714 html : d.data[this.displayField]
18717 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18718 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18721 row.removeClass('selected');
18722 if(!this.multiple && this.valueField &&
18723 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18726 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18727 row.addClass('selected');
18730 if(this.multiple && this.valueField &&
18731 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18735 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18736 this.tickItems.push(d.data);
18739 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18743 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18745 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18747 if(this.modalTitle.length){
18748 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18751 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18753 if(this.mobile_restrict_height && listHeight < bodyHeight){
18754 this.touchViewBodyEl.setHeight(listHeight);
18759 if(firstChecked && listHeight > bodyHeight){
18760 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18765 onTouchViewLoadException : function()
18767 this.hideTouchView();
18770 onTouchViewEmptyResults : function()
18772 this.clearTouchView();
18774 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18776 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18780 clearTouchView : function()
18782 this.touchViewListGroup.dom.innerHTML = '';
18785 onTouchViewClick : function(e, el, o)
18787 e.preventDefault();
18790 var rowIndex = o.rowIndex;
18792 var r = this.store.getAt(rowIndex);
18794 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18796 if(!this.multiple){
18797 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18798 c.dom.removeAttribute('checked');
18801 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18803 this.setFromData(r.data);
18805 var close = this.closeTriggerEl();
18811 this.hideTouchView();
18813 this.fireEvent('select', this, r, rowIndex);
18818 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18819 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18820 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18824 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18825 this.addItem(r.data);
18826 this.tickItems.push(r.data);
18830 getAutoCreateNativeIOS : function()
18833 cls: 'form-group' //input-group,
18838 cls : 'roo-ios-select'
18842 combobox.name = this.name;
18845 if (this.disabled) {
18846 combobox.disabled = true;
18849 var settings = this;
18851 ['xs','sm','md','lg'].map(function(size){
18852 if (settings[size]) {
18853 cfg.cls += ' col-' + size + '-' + settings[size];
18863 initIOSView : function()
18865 this.store.on('load', this.onIOSViewLoad, this);
18870 onIOSViewLoad : function()
18872 if(this.store.getCount() < 1){
18876 this.clearIOSView();
18878 if(this.allowBlank) {
18880 var default_text = '-- SELECT --';
18882 if(this.placeholder.length){
18883 default_text = this.placeholder;
18886 if(this.emptyTitle.length){
18887 default_text += ' - ' + this.emptyTitle + ' -';
18890 var opt = this.inputEl().createChild({
18893 html : default_text
18897 o[this.valueField] = 0;
18898 o[this.displayField] = default_text;
18900 this.ios_options.push({
18907 this.store.data.each(function(d, rowIndex){
18911 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18912 html = d.data[this.displayField];
18917 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18918 value = d.data[this.valueField];
18927 if(this.value == d.data[this.valueField]){
18928 option['selected'] = true;
18931 var opt = this.inputEl().createChild(option);
18933 this.ios_options.push({
18940 this.inputEl().on('change', function(){
18941 this.fireEvent('select', this);
18946 clearIOSView: function()
18948 this.inputEl().dom.innerHTML = '';
18950 this.ios_options = [];
18953 setIOSValue: function(v)
18957 if(!this.ios_options){
18961 Roo.each(this.ios_options, function(opts){
18963 opts.el.dom.removeAttribute('selected');
18965 if(opts.data[this.valueField] != v){
18969 opts.el.dom.setAttribute('selected', true);
18975 * @cfg {Boolean} grow
18979 * @cfg {Number} growMin
18983 * @cfg {Number} growMax
18992 Roo.apply(Roo.bootstrap.ComboBox, {
18996 cls: 'modal-header',
19018 cls: 'list-group-item',
19022 cls: 'roo-combobox-list-group-item-value'
19026 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19040 listItemCheckbox : {
19042 cls: 'list-group-item',
19046 cls: 'roo-combobox-list-group-item-value'
19050 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19066 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19071 cls: 'modal-footer',
19079 cls: 'col-xs-6 text-left',
19082 cls: 'btn btn-danger roo-touch-view-cancel',
19088 cls: 'col-xs-6 text-right',
19091 cls: 'btn btn-success roo-touch-view-ok',
19102 Roo.apply(Roo.bootstrap.ComboBox, {
19104 touchViewTemplate : {
19106 cls: 'modal fade roo-combobox-touch-view',
19110 cls: 'modal-dialog',
19111 style : 'position:fixed', // we have to fix position....
19115 cls: 'modal-content',
19117 Roo.bootstrap.ComboBox.header,
19118 Roo.bootstrap.ComboBox.body,
19119 Roo.bootstrap.ComboBox.footer
19128 * Ext JS Library 1.1.1
19129 * Copyright(c) 2006-2007, Ext JS, LLC.
19131 * Originally Released Under LGPL - original licence link has changed is not relivant.
19134 * <script type="text/javascript">
19139 * @extends Roo.util.Observable
19140 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19141 * This class also supports single and multi selection modes. <br>
19142 * Create a data model bound view:
19144 var store = new Roo.data.Store(...);
19146 var view = new Roo.View({
19148 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19150 singleSelect: true,
19151 selectedClass: "ydataview-selected",
19155 // listen for node click?
19156 view.on("click", function(vw, index, node, e){
19157 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19161 dataModel.load("foobar.xml");
19163 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19165 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19166 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19168 * Note: old style constructor is still suported (container, template, config)
19171 * Create a new View
19172 * @param {Object} config The config object
19175 Roo.View = function(config, depreciated_tpl, depreciated_config){
19177 this.parent = false;
19179 if (typeof(depreciated_tpl) == 'undefined') {
19180 // new way.. - universal constructor.
19181 Roo.apply(this, config);
19182 this.el = Roo.get(this.el);
19185 this.el = Roo.get(config);
19186 this.tpl = depreciated_tpl;
19187 Roo.apply(this, depreciated_config);
19189 this.wrapEl = this.el.wrap().wrap();
19190 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19193 if(typeof(this.tpl) == "string"){
19194 this.tpl = new Roo.Template(this.tpl);
19196 // support xtype ctors..
19197 this.tpl = new Roo.factory(this.tpl, Roo);
19201 this.tpl.compile();
19206 * @event beforeclick
19207 * Fires before a click is processed. Returns false to cancel the default action.
19208 * @param {Roo.View} this
19209 * @param {Number} index The index of the target node
19210 * @param {HTMLElement} node The target node
19211 * @param {Roo.EventObject} e The raw event object
19213 "beforeclick" : true,
19216 * Fires when a template node is clicked.
19217 * @param {Roo.View} this
19218 * @param {Number} index The index of the target node
19219 * @param {HTMLElement} node The target node
19220 * @param {Roo.EventObject} e The raw event object
19225 * Fires when a template node is double clicked.
19226 * @param {Roo.View} this
19227 * @param {Number} index The index of the target node
19228 * @param {HTMLElement} node The target node
19229 * @param {Roo.EventObject} e The raw event object
19233 * @event contextmenu
19234 * Fires when a template node is right clicked.
19235 * @param {Roo.View} this
19236 * @param {Number} index The index of the target node
19237 * @param {HTMLElement} node The target node
19238 * @param {Roo.EventObject} e The raw event object
19240 "contextmenu" : true,
19242 * @event selectionchange
19243 * Fires when the selected nodes change.
19244 * @param {Roo.View} this
19245 * @param {Array} selections Array of the selected nodes
19247 "selectionchange" : true,
19250 * @event beforeselect
19251 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19252 * @param {Roo.View} this
19253 * @param {HTMLElement} node The node to be selected
19254 * @param {Array} selections Array of currently selected nodes
19256 "beforeselect" : true,
19258 * @event preparedata
19259 * Fires on every row to render, to allow you to change the data.
19260 * @param {Roo.View} this
19261 * @param {Object} data to be rendered (change this)
19263 "preparedata" : true
19271 "click": this.onClick,
19272 "dblclick": this.onDblClick,
19273 "contextmenu": this.onContextMenu,
19277 this.selections = [];
19279 this.cmp = new Roo.CompositeElementLite([]);
19281 this.store = Roo.factory(this.store, Roo.data);
19282 this.setStore(this.store, true);
19285 if ( this.footer && this.footer.xtype) {
19287 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19289 this.footer.dataSource = this.store;
19290 this.footer.container = fctr;
19291 this.footer = Roo.factory(this.footer, Roo);
19292 fctr.insertFirst(this.el);
19294 // this is a bit insane - as the paging toolbar seems to detach the el..
19295 // dom.parentNode.parentNode.parentNode
19296 // they get detached?
19300 Roo.View.superclass.constructor.call(this);
19305 Roo.extend(Roo.View, Roo.util.Observable, {
19308 * @cfg {Roo.data.Store} store Data store to load data from.
19313 * @cfg {String|Roo.Element} el The container element.
19318 * @cfg {String|Roo.Template} tpl The template used by this View
19322 * @cfg {String} dataName the named area of the template to use as the data area
19323 * Works with domtemplates roo-name="name"
19327 * @cfg {String} selectedClass The css class to add to selected nodes
19329 selectedClass : "x-view-selected",
19331 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19336 * @cfg {String} text to display on mask (default Loading)
19340 * @cfg {Boolean} multiSelect Allow multiple selection
19342 multiSelect : false,
19344 * @cfg {Boolean} singleSelect Allow single selection
19346 singleSelect: false,
19349 * @cfg {Boolean} toggleSelect - selecting
19351 toggleSelect : false,
19354 * @cfg {Boolean} tickable - selecting
19359 * Returns the element this view is bound to.
19360 * @return {Roo.Element}
19362 getEl : function(){
19363 return this.wrapEl;
19369 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19371 refresh : function(){
19372 //Roo.log('refresh');
19375 // if we are using something like 'domtemplate', then
19376 // the what gets used is:
19377 // t.applySubtemplate(NAME, data, wrapping data..)
19378 // the outer template then get' applied with
19379 // the store 'extra data'
19380 // and the body get's added to the
19381 // roo-name="data" node?
19382 // <span class='roo-tpl-{name}'></span> ?????
19386 this.clearSelections();
19387 this.el.update("");
19389 var records = this.store.getRange();
19390 if(records.length < 1) {
19392 // is this valid?? = should it render a template??
19394 this.el.update(this.emptyText);
19398 if (this.dataName) {
19399 this.el.update(t.apply(this.store.meta)); //????
19400 el = this.el.child('.roo-tpl-' + this.dataName);
19403 for(var i = 0, len = records.length; i < len; i++){
19404 var data = this.prepareData(records[i].data, i, records[i]);
19405 this.fireEvent("preparedata", this, data, i, records[i]);
19407 var d = Roo.apply({}, data);
19410 Roo.apply(d, {'roo-id' : Roo.id()});
19414 Roo.each(this.parent.item, function(item){
19415 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19418 Roo.apply(d, {'roo-data-checked' : 'checked'});
19422 html[html.length] = Roo.util.Format.trim(
19424 t.applySubtemplate(this.dataName, d, this.store.meta) :
19431 el.update(html.join(""));
19432 this.nodes = el.dom.childNodes;
19433 this.updateIndexes(0);
19438 * Function to override to reformat the data that is sent to
19439 * the template for each node.
19440 * DEPRICATED - use the preparedata event handler.
19441 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19442 * a JSON object for an UpdateManager bound view).
19444 prepareData : function(data, index, record)
19446 this.fireEvent("preparedata", this, data, index, record);
19450 onUpdate : function(ds, record){
19451 // Roo.log('on update');
19452 this.clearSelections();
19453 var index = this.store.indexOf(record);
19454 var n = this.nodes[index];
19455 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19456 n.parentNode.removeChild(n);
19457 this.updateIndexes(index, index);
19463 onAdd : function(ds, records, index)
19465 //Roo.log(['on Add', ds, records, index] );
19466 this.clearSelections();
19467 if(this.nodes.length == 0){
19471 var n = this.nodes[index];
19472 for(var i = 0, len = records.length; i < len; i++){
19473 var d = this.prepareData(records[i].data, i, records[i]);
19475 this.tpl.insertBefore(n, d);
19478 this.tpl.append(this.el, d);
19481 this.updateIndexes(index);
19484 onRemove : function(ds, record, index){
19485 // Roo.log('onRemove');
19486 this.clearSelections();
19487 var el = this.dataName ?
19488 this.el.child('.roo-tpl-' + this.dataName) :
19491 el.dom.removeChild(this.nodes[index]);
19492 this.updateIndexes(index);
19496 * Refresh an individual node.
19497 * @param {Number} index
19499 refreshNode : function(index){
19500 this.onUpdate(this.store, this.store.getAt(index));
19503 updateIndexes : function(startIndex, endIndex){
19504 var ns = this.nodes;
19505 startIndex = startIndex || 0;
19506 endIndex = endIndex || ns.length - 1;
19507 for(var i = startIndex; i <= endIndex; i++){
19508 ns[i].nodeIndex = i;
19513 * Changes the data store this view uses and refresh the view.
19514 * @param {Store} store
19516 setStore : function(store, initial){
19517 if(!initial && this.store){
19518 this.store.un("datachanged", this.refresh);
19519 this.store.un("add", this.onAdd);
19520 this.store.un("remove", this.onRemove);
19521 this.store.un("update", this.onUpdate);
19522 this.store.un("clear", this.refresh);
19523 this.store.un("beforeload", this.onBeforeLoad);
19524 this.store.un("load", this.onLoad);
19525 this.store.un("loadexception", this.onLoad);
19529 store.on("datachanged", this.refresh, this);
19530 store.on("add", this.onAdd, this);
19531 store.on("remove", this.onRemove, this);
19532 store.on("update", this.onUpdate, this);
19533 store.on("clear", this.refresh, this);
19534 store.on("beforeload", this.onBeforeLoad, this);
19535 store.on("load", this.onLoad, this);
19536 store.on("loadexception", this.onLoad, this);
19544 * onbeforeLoad - masks the loading area.
19547 onBeforeLoad : function(store,opts)
19549 //Roo.log('onBeforeLoad');
19551 this.el.update("");
19553 this.el.mask(this.mask ? this.mask : "Loading" );
19555 onLoad : function ()
19562 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19563 * @param {HTMLElement} node
19564 * @return {HTMLElement} The template node
19566 findItemFromChild : function(node){
19567 var el = this.dataName ?
19568 this.el.child('.roo-tpl-' + this.dataName,true) :
19571 if(!node || node.parentNode == el){
19574 var p = node.parentNode;
19575 while(p && p != el){
19576 if(p.parentNode == el){
19585 onClick : function(e){
19586 var item = this.findItemFromChild(e.getTarget());
19588 var index = this.indexOf(item);
19589 if(this.onItemClick(item, index, e) !== false){
19590 this.fireEvent("click", this, index, item, e);
19593 this.clearSelections();
19598 onContextMenu : function(e){
19599 var item = this.findItemFromChild(e.getTarget());
19601 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19606 onDblClick : function(e){
19607 var item = this.findItemFromChild(e.getTarget());
19609 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19613 onItemClick : function(item, index, e)
19615 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19618 if (this.toggleSelect) {
19619 var m = this.isSelected(item) ? 'unselect' : 'select';
19622 _t[m](item, true, false);
19625 if(this.multiSelect || this.singleSelect){
19626 if(this.multiSelect && e.shiftKey && this.lastSelection){
19627 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19629 this.select(item, this.multiSelect && e.ctrlKey);
19630 this.lastSelection = item;
19633 if(!this.tickable){
19634 e.preventDefault();
19642 * Get the number of selected nodes.
19645 getSelectionCount : function(){
19646 return this.selections.length;
19650 * Get the currently selected nodes.
19651 * @return {Array} An array of HTMLElements
19653 getSelectedNodes : function(){
19654 return this.selections;
19658 * Get the indexes of the selected nodes.
19661 getSelectedIndexes : function(){
19662 var indexes = [], s = this.selections;
19663 for(var i = 0, len = s.length; i < len; i++){
19664 indexes.push(s[i].nodeIndex);
19670 * Clear all selections
19671 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19673 clearSelections : function(suppressEvent){
19674 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19675 this.cmp.elements = this.selections;
19676 this.cmp.removeClass(this.selectedClass);
19677 this.selections = [];
19678 if(!suppressEvent){
19679 this.fireEvent("selectionchange", this, this.selections);
19685 * Returns true if the passed node is selected
19686 * @param {HTMLElement/Number} node The node or node index
19687 * @return {Boolean}
19689 isSelected : function(node){
19690 var s = this.selections;
19694 node = this.getNode(node);
19695 return s.indexOf(node) !== -1;
19700 * @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
19701 * @param {Boolean} keepExisting (optional) true to keep existing selections
19702 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19704 select : function(nodeInfo, keepExisting, suppressEvent){
19705 if(nodeInfo instanceof Array){
19707 this.clearSelections(true);
19709 for(var i = 0, len = nodeInfo.length; i < len; i++){
19710 this.select(nodeInfo[i], true, true);
19714 var node = this.getNode(nodeInfo);
19715 if(!node || this.isSelected(node)){
19716 return; // already selected.
19719 this.clearSelections(true);
19722 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19723 Roo.fly(node).addClass(this.selectedClass);
19724 this.selections.push(node);
19725 if(!suppressEvent){
19726 this.fireEvent("selectionchange", this, this.selections);
19734 * @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
19735 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19736 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19738 unselect : function(nodeInfo, keepExisting, suppressEvent)
19740 if(nodeInfo instanceof Array){
19741 Roo.each(this.selections, function(s) {
19742 this.unselect(s, nodeInfo);
19746 var node = this.getNode(nodeInfo);
19747 if(!node || !this.isSelected(node)){
19748 //Roo.log("not selected");
19749 return; // not selected.
19753 Roo.each(this.selections, function(s) {
19755 Roo.fly(node).removeClass(this.selectedClass);
19762 this.selections= ns;
19763 this.fireEvent("selectionchange", this, this.selections);
19767 * Gets a template node.
19768 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19769 * @return {HTMLElement} The node or null if it wasn't found
19771 getNode : function(nodeInfo){
19772 if(typeof nodeInfo == "string"){
19773 return document.getElementById(nodeInfo);
19774 }else if(typeof nodeInfo == "number"){
19775 return this.nodes[nodeInfo];
19781 * Gets a range template nodes.
19782 * @param {Number} startIndex
19783 * @param {Number} endIndex
19784 * @return {Array} An array of nodes
19786 getNodes : function(start, end){
19787 var ns = this.nodes;
19788 start = start || 0;
19789 end = typeof end == "undefined" ? ns.length - 1 : end;
19792 for(var i = start; i <= end; i++){
19796 for(var i = start; i >= end; i--){
19804 * Finds the index of the passed node
19805 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19806 * @return {Number} The index of the node or -1
19808 indexOf : function(node){
19809 node = this.getNode(node);
19810 if(typeof node.nodeIndex == "number"){
19811 return node.nodeIndex;
19813 var ns = this.nodes;
19814 for(var i = 0, len = ns.length; i < len; i++){
19825 * based on jquery fullcalendar
19829 Roo.bootstrap = Roo.bootstrap || {};
19831 * @class Roo.bootstrap.Calendar
19832 * @extends Roo.bootstrap.Component
19833 * Bootstrap Calendar class
19834 * @cfg {Boolean} loadMask (true|false) default false
19835 * @cfg {Object} header generate the user specific header of the calendar, default false
19838 * Create a new Container
19839 * @param {Object} config The config object
19844 Roo.bootstrap.Calendar = function(config){
19845 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19849 * Fires when a date is selected
19850 * @param {DatePicker} this
19851 * @param {Date} date The selected date
19855 * @event monthchange
19856 * Fires when the displayed month changes
19857 * @param {DatePicker} this
19858 * @param {Date} date The selected month
19860 'monthchange': true,
19862 * @event evententer
19863 * Fires when mouse over an event
19864 * @param {Calendar} this
19865 * @param {event} Event
19867 'evententer': true,
19869 * @event eventleave
19870 * Fires when the mouse leaves an
19871 * @param {Calendar} this
19874 'eventleave': true,
19876 * @event eventclick
19877 * Fires when the mouse click an
19878 * @param {Calendar} this
19887 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19890 * @cfg {Number} startDay
19891 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19899 getAutoCreate : function(){
19902 var fc_button = function(name, corner, style, content ) {
19903 return Roo.apply({},{
19905 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19907 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19910 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19921 style : 'width:100%',
19928 cls : 'fc-header-left',
19930 fc_button('prev', 'left', 'arrow', '‹' ),
19931 fc_button('next', 'right', 'arrow', '›' ),
19932 { tag: 'span', cls: 'fc-header-space' },
19933 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19941 cls : 'fc-header-center',
19945 cls: 'fc-header-title',
19948 html : 'month / year'
19956 cls : 'fc-header-right',
19958 /* fc_button('month', 'left', '', 'month' ),
19959 fc_button('week', '', '', 'week' ),
19960 fc_button('day', 'right', '', 'day' )
19972 header = this.header;
19975 var cal_heads = function() {
19977 // fixme - handle this.
19979 for (var i =0; i < Date.dayNames.length; i++) {
19980 var d = Date.dayNames[i];
19983 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19984 html : d.substring(0,3)
19988 ret[0].cls += ' fc-first';
19989 ret[6].cls += ' fc-last';
19992 var cal_cell = function(n) {
19995 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20000 cls: 'fc-day-number',
20004 cls: 'fc-day-content',
20008 style: 'position: relative;' // height: 17px;
20020 var cal_rows = function() {
20023 for (var r = 0; r < 6; r++) {
20030 for (var i =0; i < Date.dayNames.length; i++) {
20031 var d = Date.dayNames[i];
20032 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20035 row.cn[0].cls+=' fc-first';
20036 row.cn[0].cn[0].style = 'min-height:90px';
20037 row.cn[6].cls+=' fc-last';
20041 ret[0].cls += ' fc-first';
20042 ret[4].cls += ' fc-prev-last';
20043 ret[5].cls += ' fc-last';
20050 cls: 'fc-border-separate',
20051 style : 'width:100%',
20059 cls : 'fc-first fc-last',
20077 cls : 'fc-content',
20078 style : "position: relative;",
20081 cls : 'fc-view fc-view-month fc-grid',
20082 style : 'position: relative',
20083 unselectable : 'on',
20086 cls : 'fc-event-container',
20087 style : 'position:absolute;z-index:8;top:0;left:0;'
20105 initEvents : function()
20108 throw "can not find store for calendar";
20114 style: "text-align:center",
20118 style: "background-color:white;width:50%;margin:250 auto",
20122 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20133 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20135 var size = this.el.select('.fc-content', true).first().getSize();
20136 this.maskEl.setSize(size.width, size.height);
20137 this.maskEl.enableDisplayMode("block");
20138 if(!this.loadMask){
20139 this.maskEl.hide();
20142 this.store = Roo.factory(this.store, Roo.data);
20143 this.store.on('load', this.onLoad, this);
20144 this.store.on('beforeload', this.onBeforeLoad, this);
20148 this.cells = this.el.select('.fc-day',true);
20149 //Roo.log(this.cells);
20150 this.textNodes = this.el.query('.fc-day-number');
20151 this.cells.addClassOnOver('fc-state-hover');
20153 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20154 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20155 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20156 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20158 this.on('monthchange', this.onMonthChange, this);
20160 this.update(new Date().clearTime());
20163 resize : function() {
20164 var sz = this.el.getSize();
20166 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20167 this.el.select('.fc-day-content div',true).setHeight(34);
20172 showPrevMonth : function(e){
20173 this.update(this.activeDate.add("mo", -1));
20175 showToday : function(e){
20176 this.update(new Date().clearTime());
20179 showNextMonth : function(e){
20180 this.update(this.activeDate.add("mo", 1));
20184 showPrevYear : function(){
20185 this.update(this.activeDate.add("y", -1));
20189 showNextYear : function(){
20190 this.update(this.activeDate.add("y", 1));
20195 update : function(date)
20197 var vd = this.activeDate;
20198 this.activeDate = date;
20199 // if(vd && this.el){
20200 // var t = date.getTime();
20201 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20202 // Roo.log('using add remove');
20204 // this.fireEvent('monthchange', this, date);
20206 // this.cells.removeClass("fc-state-highlight");
20207 // this.cells.each(function(c){
20208 // if(c.dateValue == t){
20209 // c.addClass("fc-state-highlight");
20210 // setTimeout(function(){
20211 // try{c.dom.firstChild.focus();}catch(e){}
20221 var days = date.getDaysInMonth();
20223 var firstOfMonth = date.getFirstDateOfMonth();
20224 var startingPos = firstOfMonth.getDay()-this.startDay;
20226 if(startingPos < this.startDay){
20230 var pm = date.add(Date.MONTH, -1);
20231 var prevStart = pm.getDaysInMonth()-startingPos;
20233 this.cells = this.el.select('.fc-day',true);
20234 this.textNodes = this.el.query('.fc-day-number');
20235 this.cells.addClassOnOver('fc-state-hover');
20237 var cells = this.cells.elements;
20238 var textEls = this.textNodes;
20240 Roo.each(cells, function(cell){
20241 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20244 days += startingPos;
20246 // convert everything to numbers so it's fast
20247 var day = 86400000;
20248 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20251 //Roo.log(prevStart);
20253 var today = new Date().clearTime().getTime();
20254 var sel = date.clearTime().getTime();
20255 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20256 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20257 var ddMatch = this.disabledDatesRE;
20258 var ddText = this.disabledDatesText;
20259 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20260 var ddaysText = this.disabledDaysText;
20261 var format = this.format;
20263 var setCellClass = function(cal, cell){
20267 //Roo.log('set Cell Class');
20269 var t = d.getTime();
20273 cell.dateValue = t;
20275 cell.className += " fc-today";
20276 cell.className += " fc-state-highlight";
20277 cell.title = cal.todayText;
20280 // disable highlight in other month..
20281 //cell.className += " fc-state-highlight";
20286 cell.className = " fc-state-disabled";
20287 cell.title = cal.minText;
20291 cell.className = " fc-state-disabled";
20292 cell.title = cal.maxText;
20296 if(ddays.indexOf(d.getDay()) != -1){
20297 cell.title = ddaysText;
20298 cell.className = " fc-state-disabled";
20301 if(ddMatch && format){
20302 var fvalue = d.dateFormat(format);
20303 if(ddMatch.test(fvalue)){
20304 cell.title = ddText.replace("%0", fvalue);
20305 cell.className = " fc-state-disabled";
20309 if (!cell.initialClassName) {
20310 cell.initialClassName = cell.dom.className;
20313 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20318 for(; i < startingPos; i++) {
20319 textEls[i].innerHTML = (++prevStart);
20320 d.setDate(d.getDate()+1);
20322 cells[i].className = "fc-past fc-other-month";
20323 setCellClass(this, cells[i]);
20328 for(; i < days; i++){
20329 intDay = i - startingPos + 1;
20330 textEls[i].innerHTML = (intDay);
20331 d.setDate(d.getDate()+1);
20333 cells[i].className = ''; // "x-date-active";
20334 setCellClass(this, cells[i]);
20338 for(; i < 42; i++) {
20339 textEls[i].innerHTML = (++extraDays);
20340 d.setDate(d.getDate()+1);
20342 cells[i].className = "fc-future fc-other-month";
20343 setCellClass(this, cells[i]);
20346 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20348 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20350 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20351 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20353 if(totalRows != 6){
20354 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20355 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20358 this.fireEvent('monthchange', this, date);
20362 if(!this.internalRender){
20363 var main = this.el.dom.firstChild;
20364 var w = main.offsetWidth;
20365 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20366 Roo.fly(main).setWidth(w);
20367 this.internalRender = true;
20368 // opera does not respect the auto grow header center column
20369 // then, after it gets a width opera refuses to recalculate
20370 // without a second pass
20371 if(Roo.isOpera && !this.secondPass){
20372 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20373 this.secondPass = true;
20374 this.update.defer(10, this, [date]);
20381 findCell : function(dt) {
20382 dt = dt.clearTime().getTime();
20384 this.cells.each(function(c){
20385 //Roo.log("check " +c.dateValue + '?=' + dt);
20386 if(c.dateValue == dt){
20396 findCells : function(ev) {
20397 var s = ev.start.clone().clearTime().getTime();
20399 var e= ev.end.clone().clearTime().getTime();
20402 this.cells.each(function(c){
20403 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20405 if(c.dateValue > e){
20408 if(c.dateValue < s){
20417 // findBestRow: function(cells)
20421 // for (var i =0 ; i < cells.length;i++) {
20422 // ret = Math.max(cells[i].rows || 0,ret);
20429 addItem : function(ev)
20431 // look for vertical location slot in
20432 var cells = this.findCells(ev);
20434 // ev.row = this.findBestRow(cells);
20436 // work out the location.
20440 for(var i =0; i < cells.length; i++) {
20442 cells[i].row = cells[0].row;
20445 cells[i].row = cells[i].row + 1;
20455 if (crow.start.getY() == cells[i].getY()) {
20457 crow.end = cells[i];
20474 cells[0].events.push(ev);
20476 this.calevents.push(ev);
20479 clearEvents: function() {
20481 if(!this.calevents){
20485 Roo.each(this.cells.elements, function(c){
20491 Roo.each(this.calevents, function(e) {
20492 Roo.each(e.els, function(el) {
20493 el.un('mouseenter' ,this.onEventEnter, this);
20494 el.un('mouseleave' ,this.onEventLeave, this);
20499 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20505 renderEvents: function()
20509 this.cells.each(function(c) {
20518 if(c.row != c.events.length){
20519 r = 4 - (4 - (c.row - c.events.length));
20522 c.events = ev.slice(0, r);
20523 c.more = ev.slice(r);
20525 if(c.more.length && c.more.length == 1){
20526 c.events.push(c.more.pop());
20529 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20533 this.cells.each(function(c) {
20535 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20538 for (var e = 0; e < c.events.length; e++){
20539 var ev = c.events[e];
20540 var rows = ev.rows;
20542 for(var i = 0; i < rows.length; i++) {
20544 // how many rows should it span..
20547 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20548 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20550 unselectable : "on",
20553 cls: 'fc-event-inner',
20557 // cls: 'fc-event-time',
20558 // html : cells.length > 1 ? '' : ev.time
20562 cls: 'fc-event-title',
20563 html : String.format('{0}', ev.title)
20570 cls: 'ui-resizable-handle ui-resizable-e',
20571 html : '  '
20578 cfg.cls += ' fc-event-start';
20580 if ((i+1) == rows.length) {
20581 cfg.cls += ' fc-event-end';
20584 var ctr = _this.el.select('.fc-event-container',true).first();
20585 var cg = ctr.createChild(cfg);
20587 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20588 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20590 var r = (c.more.length) ? 1 : 0;
20591 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20592 cg.setWidth(ebox.right - sbox.x -2);
20594 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20595 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20596 cg.on('click', _this.onEventClick, _this, ev);
20607 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20608 style : 'position: absolute',
20609 unselectable : "on",
20612 cls: 'fc-event-inner',
20616 cls: 'fc-event-title',
20624 cls: 'ui-resizable-handle ui-resizable-e',
20625 html : '  '
20631 var ctr = _this.el.select('.fc-event-container',true).first();
20632 var cg = ctr.createChild(cfg);
20634 var sbox = c.select('.fc-day-content',true).first().getBox();
20635 var ebox = c.select('.fc-day-content',true).first().getBox();
20637 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20638 cg.setWidth(ebox.right - sbox.x -2);
20640 cg.on('click', _this.onMoreEventClick, _this, c.more);
20650 onEventEnter: function (e, el,event,d) {
20651 this.fireEvent('evententer', this, el, event);
20654 onEventLeave: function (e, el,event,d) {
20655 this.fireEvent('eventleave', this, el, event);
20658 onEventClick: function (e, el,event,d) {
20659 this.fireEvent('eventclick', this, el, event);
20662 onMonthChange: function () {
20666 onMoreEventClick: function(e, el, more)
20670 this.calpopover.placement = 'right';
20671 this.calpopover.setTitle('More');
20673 this.calpopover.setContent('');
20675 var ctr = this.calpopover.el.select('.popover-content', true).first();
20677 Roo.each(more, function(m){
20679 cls : 'fc-event-hori fc-event-draggable',
20682 var cg = ctr.createChild(cfg);
20684 cg.on('click', _this.onEventClick, _this, m);
20687 this.calpopover.show(el);
20692 onLoad: function ()
20694 this.calevents = [];
20697 if(this.store.getCount() > 0){
20698 this.store.data.each(function(d){
20701 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20702 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20703 time : d.data.start_time,
20704 title : d.data.title,
20705 description : d.data.description,
20706 venue : d.data.venue
20711 this.renderEvents();
20713 if(this.calevents.length && this.loadMask){
20714 this.maskEl.hide();
20718 onBeforeLoad: function()
20720 this.clearEvents();
20722 this.maskEl.show();
20736 * @class Roo.bootstrap.Popover
20737 * @extends Roo.bootstrap.Component
20738 * Bootstrap Popover class
20739 * @cfg {String} html contents of the popover (or false to use children..)
20740 * @cfg {String} title of popover (or false to hide)
20741 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20742 * @cfg {String} trigger click || hover (or false to trigger manually)
20743 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20744 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20745 * - if false and it has a 'parent' then it will be automatically added to that element
20746 * - if string - Roo.get will be called
20747 * @cfg {Number} delay - delay before showing
20750 * Create a new Popover
20751 * @param {Object} config The config object
20754 Roo.bootstrap.Popover = function(config){
20755 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20761 * After the popover show
20763 * @param {Roo.bootstrap.Popover} this
20768 * After the popover hide
20770 * @param {Roo.bootstrap.Popover} this
20776 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20781 placement : 'right',
20782 trigger : 'hover', // hover
20788 can_build_overlaid : false,
20790 maskEl : false, // the mask element
20793 alignEl : false, // when show is called with an element - this get's stored.
20795 getChildContainer : function()
20797 return this.contentEl;
20800 getPopoverHeader : function()
20802 this.title = true; // flag not to hide it..
20803 this.headerEl.addClass('p-0');
20804 return this.headerEl
20808 getAutoCreate : function(){
20811 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20812 style: 'display:block',
20818 cls : 'popover-inner ',
20822 cls: 'popover-title popover-header',
20823 html : this.title === false ? '' : this.title
20826 cls : 'popover-content popover-body ' + (this.cls || ''),
20827 html : this.html || ''
20838 * @param {string} the title
20840 setTitle: function(str)
20844 this.headerEl.dom.innerHTML = str;
20849 * @param {string} the body content
20851 setContent: function(str)
20854 if (this.contentEl) {
20855 this.contentEl.dom.innerHTML = str;
20859 // as it get's added to the bottom of the page.
20860 onRender : function(ct, position)
20862 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20867 var cfg = Roo.apply({}, this.getAutoCreate());
20871 cfg.cls += ' ' + this.cls;
20874 cfg.style = this.style;
20876 //Roo.log("adding to ");
20877 this.el = Roo.get(document.body).createChild(cfg, position);
20878 // Roo.log(this.el);
20881 this.contentEl = this.el.select('.popover-content',true).first();
20882 this.headerEl = this.el.select('.popover-title',true).first();
20885 if(typeof(this.items) != 'undefined'){
20886 var items = this.items;
20889 for(var i =0;i < items.length;i++) {
20890 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20894 this.items = nitems;
20896 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20897 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20904 resizeMask : function()
20906 this.maskEl.setSize(
20907 Roo.lib.Dom.getViewWidth(true),
20908 Roo.lib.Dom.getViewHeight(true)
20912 initEvents : function()
20916 Roo.bootstrap.Popover.register(this);
20919 this.arrowEl = this.el.select('.arrow',true).first();
20920 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20921 this.el.enableDisplayMode('block');
20925 if (this.over === false && !this.parent()) {
20928 if (this.triggers === false) {
20933 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20934 var triggers = this.trigger ? this.trigger.split(' ') : [];
20935 Roo.each(triggers, function(trigger) {
20937 if (trigger == 'click') {
20938 on_el.on('click', this.toggle, this);
20939 } else if (trigger != 'manual') {
20940 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20941 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20943 on_el.on(eventIn ,this.enter, this);
20944 on_el.on(eventOut, this.leave, this);
20954 toggle : function () {
20955 this.hoverState == 'in' ? this.leave() : this.enter();
20958 enter : function () {
20960 clearTimeout(this.timeout);
20962 this.hoverState = 'in';
20964 if (!this.delay || !this.delay.show) {
20969 this.timeout = setTimeout(function () {
20970 if (_t.hoverState == 'in') {
20973 }, this.delay.show)
20976 leave : function() {
20977 clearTimeout(this.timeout);
20979 this.hoverState = 'out';
20981 if (!this.delay || !this.delay.hide) {
20986 this.timeout = setTimeout(function () {
20987 if (_t.hoverState == 'out') {
20990 }, this.delay.hide)
20994 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20995 * @param {string} (left|right|top|bottom) position
20997 show : function (on_el, placement)
20999 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21000 on_el = on_el || false; // default to false
21003 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21004 on_el = this.parent().el;
21005 } else if (this.over) {
21006 on_el = Roo.get(this.over);
21011 this.alignEl = Roo.get( on_el );
21014 this.render(document.body);
21020 if (this.title === false) {
21021 this.headerEl.hide();
21026 this.el.dom.style.display = 'block';
21029 if (this.alignEl) {
21030 this.updatePosition(this.placement, true);
21033 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21034 var es = this.el.getSize();
21035 var x = Roo.lib.Dom.getViewWidth()/2;
21036 var y = Roo.lib.Dom.getViewHeight()/2;
21037 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21042 //var arrow = this.el.select('.arrow',true).first();
21043 //arrow.set(align[2],
21045 this.el.addClass('in');
21049 this.hoverState = 'in';
21052 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21053 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21054 this.maskEl.dom.style.display = 'block';
21055 this.maskEl.addClass('show');
21057 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21059 this.fireEvent('show', this);
21063 * fire this manually after loading a grid in the table for example
21064 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21065 * @param {Boolean} try and move it if we cant get right position.
21067 updatePosition : function(placement, try_move)
21069 // allow for calling with no parameters
21070 placement = placement ? placement : this.placement;
21071 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21073 this.el.removeClass([
21074 'fade','top','bottom', 'left', 'right','in',
21075 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21077 this.el.addClass(placement + ' bs-popover-' + placement);
21079 if (!this.alignEl ) {
21083 switch (placement) {
21085 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21086 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21087 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21088 //normal display... or moved up/down.
21089 this.el.setXY(offset);
21090 var xy = this.alignEl.getAnchorXY('tr', false);
21092 this.arrowEl.setXY(xy);
21095 // continue through...
21096 return this.updatePosition('left', false);
21100 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21101 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21102 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21103 //normal display... or moved up/down.
21104 this.el.setXY(offset);
21105 var xy = this.alignEl.getAnchorXY('tl', false);
21106 xy[0]-=10;xy[1]+=5; // << fix me
21107 this.arrowEl.setXY(xy);
21111 return this.updatePosition('right', false);
21114 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21115 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21116 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21117 //normal display... or moved up/down.
21118 this.el.setXY(offset);
21119 var xy = this.alignEl.getAnchorXY('t', false);
21120 xy[1]-=10; // << fix me
21121 this.arrowEl.setXY(xy);
21125 return this.updatePosition('bottom', false);
21128 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21129 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21130 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21131 //normal display... or moved up/down.
21132 this.el.setXY(offset);
21133 var xy = this.alignEl.getAnchorXY('b', false);
21134 xy[1]+=2; // << fix me
21135 this.arrowEl.setXY(xy);
21139 return this.updatePosition('top', false);
21150 this.el.setXY([0,0]);
21151 this.el.removeClass('in');
21153 this.hoverState = null;
21154 this.maskEl.hide(); // always..
21155 this.fireEvent('hide', this);
21161 Roo.apply(Roo.bootstrap.Popover, {
21164 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21165 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21166 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21167 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21172 clickHander : false,
21176 onMouseDown : function(e)
21178 if (this.popups.length && !e.getTarget(".roo-popover")) {
21179 /// what is nothing is showing..
21188 register : function(popup)
21190 if (!Roo.bootstrap.Popover.clickHandler) {
21191 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21193 // hide other popups.
21194 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21195 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21196 this.hideAll(); //<< why?
21197 //this.popups.push(popup);
21199 hideAll : function()
21201 this.popups.forEach(function(p) {
21205 onShow : function() {
21206 Roo.bootstrap.Popover.popups.push(this);
21208 onHide : function() {
21209 Roo.bootstrap.Popover.popups.remove(this);
21215 * Card header - holder for the card header elements.
21220 * @class Roo.bootstrap.PopoverNav
21221 * @extends Roo.bootstrap.NavGroup
21222 * Bootstrap Popover header navigation class
21224 * Create a new Popover Header Navigation
21225 * @param {Object} config The config object
21228 Roo.bootstrap.PopoverNav = function(config){
21229 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21232 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21235 container_method : 'getPopoverHeader'
21253 * @class Roo.bootstrap.Progress
21254 * @extends Roo.bootstrap.Component
21255 * Bootstrap Progress class
21256 * @cfg {Boolean} striped striped of the progress bar
21257 * @cfg {Boolean} active animated of the progress bar
21261 * Create a new Progress
21262 * @param {Object} config The config object
21265 Roo.bootstrap.Progress = function(config){
21266 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21269 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21274 getAutoCreate : function(){
21282 cfg.cls += ' progress-striped';
21286 cfg.cls += ' active';
21305 * @class Roo.bootstrap.ProgressBar
21306 * @extends Roo.bootstrap.Component
21307 * Bootstrap ProgressBar class
21308 * @cfg {Number} aria_valuenow aria-value now
21309 * @cfg {Number} aria_valuemin aria-value min
21310 * @cfg {Number} aria_valuemax aria-value max
21311 * @cfg {String} label label for the progress bar
21312 * @cfg {String} panel (success | info | warning | danger )
21313 * @cfg {String} role role of the progress bar
21314 * @cfg {String} sr_only text
21318 * Create a new ProgressBar
21319 * @param {Object} config The config object
21322 Roo.bootstrap.ProgressBar = function(config){
21323 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21326 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21330 aria_valuemax : 100,
21336 getAutoCreate : function()
21341 cls: 'progress-bar',
21342 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21354 cfg.role = this.role;
21357 if(this.aria_valuenow){
21358 cfg['aria-valuenow'] = this.aria_valuenow;
21361 if(this.aria_valuemin){
21362 cfg['aria-valuemin'] = this.aria_valuemin;
21365 if(this.aria_valuemax){
21366 cfg['aria-valuemax'] = this.aria_valuemax;
21369 if(this.label && !this.sr_only){
21370 cfg.html = this.label;
21374 cfg.cls += ' progress-bar-' + this.panel;
21380 update : function(aria_valuenow)
21382 this.aria_valuenow = aria_valuenow;
21384 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21399 * @class Roo.bootstrap.TabGroup
21400 * @extends Roo.bootstrap.Column
21401 * Bootstrap Column class
21402 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21403 * @cfg {Boolean} carousel true to make the group behave like a carousel
21404 * @cfg {Boolean} bullets show bullets for the panels
21405 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21406 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21407 * @cfg {Boolean} showarrow (true|false) show arrow default true
21410 * Create a new TabGroup
21411 * @param {Object} config The config object
21414 Roo.bootstrap.TabGroup = function(config){
21415 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21417 this.navId = Roo.id();
21420 Roo.bootstrap.TabGroup.register(this);
21424 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21427 transition : false,
21432 slideOnTouch : false,
21435 getAutoCreate : function()
21437 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21439 cfg.cls += ' tab-content';
21441 if (this.carousel) {
21442 cfg.cls += ' carousel slide';
21445 cls : 'carousel-inner',
21449 if(this.bullets && !Roo.isTouch){
21452 cls : 'carousel-bullets',
21456 if(this.bullets_cls){
21457 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21464 cfg.cn[0].cn.push(bullets);
21467 if(this.showarrow){
21468 cfg.cn[0].cn.push({
21470 class : 'carousel-arrow',
21474 class : 'carousel-prev',
21478 class : 'fa fa-chevron-left'
21484 class : 'carousel-next',
21488 class : 'fa fa-chevron-right'
21501 initEvents: function()
21503 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21504 // this.el.on("touchstart", this.onTouchStart, this);
21507 if(this.autoslide){
21510 this.slideFn = window.setInterval(function() {
21511 _this.showPanelNext();
21515 if(this.showarrow){
21516 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21517 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21523 // onTouchStart : function(e, el, o)
21525 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21529 // this.showPanelNext();
21533 getChildContainer : function()
21535 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21539 * register a Navigation item
21540 * @param {Roo.bootstrap.NavItem} the navitem to add
21542 register : function(item)
21544 this.tabs.push( item);
21545 item.navId = this.navId; // not really needed..
21550 getActivePanel : function()
21553 Roo.each(this.tabs, function(t) {
21563 getPanelByName : function(n)
21566 Roo.each(this.tabs, function(t) {
21567 if (t.tabId == n) {
21575 indexOfPanel : function(p)
21578 Roo.each(this.tabs, function(t,i) {
21579 if (t.tabId == p.tabId) {
21588 * show a specific panel
21589 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21590 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21592 showPanel : function (pan)
21594 if(this.transition || typeof(pan) == 'undefined'){
21595 Roo.log("waiting for the transitionend");
21599 if (typeof(pan) == 'number') {
21600 pan = this.tabs[pan];
21603 if (typeof(pan) == 'string') {
21604 pan = this.getPanelByName(pan);
21607 var cur = this.getActivePanel();
21610 Roo.log('pan or acitve pan is undefined');
21614 if (pan.tabId == this.getActivePanel().tabId) {
21618 if (false === cur.fireEvent('beforedeactivate')) {
21622 if(this.bullets > 0 && !Roo.isTouch){
21623 this.setActiveBullet(this.indexOfPanel(pan));
21626 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21628 //class="carousel-item carousel-item-next carousel-item-left"
21630 this.transition = true;
21631 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21632 var lr = dir == 'next' ? 'left' : 'right';
21633 pan.el.addClass(dir); // or prev
21634 pan.el.addClass('carousel-item-' + dir); // or prev
21635 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21636 cur.el.addClass(lr); // or right
21637 pan.el.addClass(lr);
21638 cur.el.addClass('carousel-item-' +lr); // or right
21639 pan.el.addClass('carousel-item-' +lr);
21643 cur.el.on('transitionend', function() {
21644 Roo.log("trans end?");
21646 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21647 pan.setActive(true);
21649 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21650 cur.setActive(false);
21652 _this.transition = false;
21654 }, this, { single: true } );
21659 cur.setActive(false);
21660 pan.setActive(true);
21665 showPanelNext : function()
21667 var i = this.indexOfPanel(this.getActivePanel());
21669 if (i >= this.tabs.length - 1 && !this.autoslide) {
21673 if (i >= this.tabs.length - 1 && this.autoslide) {
21677 this.showPanel(this.tabs[i+1]);
21680 showPanelPrev : function()
21682 var i = this.indexOfPanel(this.getActivePanel());
21684 if (i < 1 && !this.autoslide) {
21688 if (i < 1 && this.autoslide) {
21689 i = this.tabs.length;
21692 this.showPanel(this.tabs[i-1]);
21696 addBullet: function()
21698 if(!this.bullets || Roo.isTouch){
21701 var ctr = this.el.select('.carousel-bullets',true).first();
21702 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21703 var bullet = ctr.createChild({
21704 cls : 'bullet bullet-' + i
21705 },ctr.dom.lastChild);
21710 bullet.on('click', (function(e, el, o, ii, t){
21712 e.preventDefault();
21714 this.showPanel(ii);
21716 if(this.autoslide && this.slideFn){
21717 clearInterval(this.slideFn);
21718 this.slideFn = window.setInterval(function() {
21719 _this.showPanelNext();
21723 }).createDelegate(this, [i, bullet], true));
21728 setActiveBullet : function(i)
21734 Roo.each(this.el.select('.bullet', true).elements, function(el){
21735 el.removeClass('selected');
21738 var bullet = this.el.select('.bullet-' + i, true).first();
21744 bullet.addClass('selected');
21755 Roo.apply(Roo.bootstrap.TabGroup, {
21759 * register a Navigation Group
21760 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21762 register : function(navgrp)
21764 this.groups[navgrp.navId] = navgrp;
21768 * fetch a Navigation Group based on the navigation ID
21769 * if one does not exist , it will get created.
21770 * @param {string} the navgroup to add
21771 * @returns {Roo.bootstrap.NavGroup} the navgroup
21773 get: function(navId) {
21774 if (typeof(this.groups[navId]) == 'undefined') {
21775 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21777 return this.groups[navId] ;
21792 * @class Roo.bootstrap.TabPanel
21793 * @extends Roo.bootstrap.Component
21794 * Bootstrap TabPanel class
21795 * @cfg {Boolean} active panel active
21796 * @cfg {String} html panel content
21797 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21798 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21799 * @cfg {String} href click to link..
21800 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21804 * Create a new TabPanel
21805 * @param {Object} config The config object
21808 Roo.bootstrap.TabPanel = function(config){
21809 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21813 * Fires when the active status changes
21814 * @param {Roo.bootstrap.TabPanel} this
21815 * @param {Boolean} state the new state
21820 * @event beforedeactivate
21821 * Fires before a tab is de-activated - can be used to do validation on a form.
21822 * @param {Roo.bootstrap.TabPanel} this
21823 * @return {Boolean} false if there is an error
21826 'beforedeactivate': true
21829 this.tabId = this.tabId || Roo.id();
21833 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21840 touchSlide : false,
21841 getAutoCreate : function(){
21846 // item is needed for carousel - not sure if it has any effect otherwise
21847 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21848 html: this.html || ''
21852 cfg.cls += ' active';
21856 cfg.tabId = this.tabId;
21864 initEvents: function()
21866 var p = this.parent();
21868 this.navId = this.navId || p.navId;
21870 if (typeof(this.navId) != 'undefined') {
21871 // not really needed.. but just in case.. parent should be a NavGroup.
21872 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21876 var i = tg.tabs.length - 1;
21878 if(this.active && tg.bullets > 0 && i < tg.bullets){
21879 tg.setActiveBullet(i);
21883 this.el.on('click', this.onClick, this);
21885 if(Roo.isTouch && this.touchSlide){
21886 this.el.on("touchstart", this.onTouchStart, this);
21887 this.el.on("touchmove", this.onTouchMove, this);
21888 this.el.on("touchend", this.onTouchEnd, this);
21893 onRender : function(ct, position)
21895 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21898 setActive : function(state)
21900 Roo.log("panel - set active " + this.tabId + "=" + state);
21902 this.active = state;
21904 this.el.removeClass('active');
21906 } else if (!this.el.hasClass('active')) {
21907 this.el.addClass('active');
21910 this.fireEvent('changed', this, state);
21913 onClick : function(e)
21915 e.preventDefault();
21917 if(!this.href.length){
21921 window.location.href = this.href;
21930 onTouchStart : function(e)
21932 this.swiping = false;
21934 this.startX = e.browserEvent.touches[0].clientX;
21935 this.startY = e.browserEvent.touches[0].clientY;
21938 onTouchMove : function(e)
21940 this.swiping = true;
21942 this.endX = e.browserEvent.touches[0].clientX;
21943 this.endY = e.browserEvent.touches[0].clientY;
21946 onTouchEnd : function(e)
21953 var tabGroup = this.parent();
21955 if(this.endX > this.startX){ // swiping right
21956 tabGroup.showPanelPrev();
21960 if(this.startX > this.endX){ // swiping left
21961 tabGroup.showPanelNext();
21980 * @class Roo.bootstrap.DateField
21981 * @extends Roo.bootstrap.Input
21982 * Bootstrap DateField class
21983 * @cfg {Number} weekStart default 0
21984 * @cfg {String} viewMode default empty, (months|years)
21985 * @cfg {String} minViewMode default empty, (months|years)
21986 * @cfg {Number} startDate default -Infinity
21987 * @cfg {Number} endDate default Infinity
21988 * @cfg {Boolean} todayHighlight default false
21989 * @cfg {Boolean} todayBtn default false
21990 * @cfg {Boolean} calendarWeeks default false
21991 * @cfg {Object} daysOfWeekDisabled default empty
21992 * @cfg {Boolean} singleMode default false (true | false)
21994 * @cfg {Boolean} keyboardNavigation default true
21995 * @cfg {String} language default en
21998 * Create a new DateField
21999 * @param {Object} config The config object
22002 Roo.bootstrap.DateField = function(config){
22003 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22007 * Fires when this field show.
22008 * @param {Roo.bootstrap.DateField} this
22009 * @param {Mixed} date The date value
22014 * Fires when this field hide.
22015 * @param {Roo.bootstrap.DateField} this
22016 * @param {Mixed} date The date value
22021 * Fires when select a date.
22022 * @param {Roo.bootstrap.DateField} this
22023 * @param {Mixed} date The date value
22027 * @event beforeselect
22028 * Fires when before select a date.
22029 * @param {Roo.bootstrap.DateField} this
22030 * @param {Mixed} date The date value
22032 beforeselect : true
22036 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22039 * @cfg {String} format
22040 * The default date format string which can be overriden for localization support. The format must be
22041 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22045 * @cfg {String} altFormats
22046 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22047 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22049 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22057 todayHighlight : false,
22063 keyboardNavigation: true,
22065 calendarWeeks: false,
22067 startDate: -Infinity,
22071 daysOfWeekDisabled: [],
22075 singleMode : false,
22077 UTCDate: function()
22079 return new Date(Date.UTC.apply(Date, arguments));
22082 UTCToday: function()
22084 var today = new Date();
22085 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22088 getDate: function() {
22089 var d = this.getUTCDate();
22090 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22093 getUTCDate: function() {
22097 setDate: function(d) {
22098 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22101 setUTCDate: function(d) {
22103 this.setValue(this.formatDate(this.date));
22106 onRender: function(ct, position)
22109 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22111 this.language = this.language || 'en';
22112 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22113 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22115 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22116 this.format = this.format || 'm/d/y';
22117 this.isInline = false;
22118 this.isInput = true;
22119 this.component = this.el.select('.add-on', true).first() || false;
22120 this.component = (this.component && this.component.length === 0) ? false : this.component;
22121 this.hasInput = this.component && this.inputEl().length;
22123 if (typeof(this.minViewMode === 'string')) {
22124 switch (this.minViewMode) {
22126 this.minViewMode = 1;
22129 this.minViewMode = 2;
22132 this.minViewMode = 0;
22137 if (typeof(this.viewMode === 'string')) {
22138 switch (this.viewMode) {
22151 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22153 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22155 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22157 this.picker().on('mousedown', this.onMousedown, this);
22158 this.picker().on('click', this.onClick, this);
22160 this.picker().addClass('datepicker-dropdown');
22162 this.startViewMode = this.viewMode;
22164 if(this.singleMode){
22165 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22166 v.setVisibilityMode(Roo.Element.DISPLAY);
22170 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22171 v.setStyle('width', '189px');
22175 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22176 if(!this.calendarWeeks){
22181 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22182 v.attr('colspan', function(i, val){
22183 return parseInt(val) + 1;
22188 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22190 this.setStartDate(this.startDate);
22191 this.setEndDate(this.endDate);
22193 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22200 if(this.isInline) {
22205 picker : function()
22207 return this.pickerEl;
22208 // return this.el.select('.datepicker', true).first();
22211 fillDow: function()
22213 var dowCnt = this.weekStart;
22222 if(this.calendarWeeks){
22230 while (dowCnt < this.weekStart + 7) {
22234 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22238 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22241 fillMonths: function()
22244 var months = this.picker().select('>.datepicker-months td', true).first();
22246 months.dom.innerHTML = '';
22252 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22255 months.createChild(month);
22262 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;
22264 if (this.date < this.startDate) {
22265 this.viewDate = new Date(this.startDate);
22266 } else if (this.date > this.endDate) {
22267 this.viewDate = new Date(this.endDate);
22269 this.viewDate = new Date(this.date);
22277 var d = new Date(this.viewDate),
22278 year = d.getUTCFullYear(),
22279 month = d.getUTCMonth(),
22280 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22281 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22282 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22283 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22284 currentDate = this.date && this.date.valueOf(),
22285 today = this.UTCToday();
22287 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22289 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22291 // this.picker.select('>tfoot th.today').
22292 // .text(dates[this.language].today)
22293 // .toggle(this.todayBtn !== false);
22295 this.updateNavArrows();
22298 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22300 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22302 prevMonth.setUTCDate(day);
22304 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22306 var nextMonth = new Date(prevMonth);
22308 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22310 nextMonth = nextMonth.valueOf();
22312 var fillMonths = false;
22314 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22316 while(prevMonth.valueOf() <= nextMonth) {
22319 if (prevMonth.getUTCDay() === this.weekStart) {
22321 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22329 if(this.calendarWeeks){
22330 // ISO 8601: First week contains first thursday.
22331 // ISO also states week starts on Monday, but we can be more abstract here.
22333 // Start of current week: based on weekstart/current date
22334 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22335 // Thursday of this week
22336 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22337 // First Thursday of year, year from thursday
22338 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22339 // Calendar week: ms between thursdays, div ms per day, div 7 days
22340 calWeek = (th - yth) / 864e5 / 7 + 1;
22342 fillMonths.cn.push({
22350 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22352 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22355 if (this.todayHighlight &&
22356 prevMonth.getUTCFullYear() == today.getFullYear() &&
22357 prevMonth.getUTCMonth() == today.getMonth() &&
22358 prevMonth.getUTCDate() == today.getDate()) {
22359 clsName += ' today';
22362 if (currentDate && prevMonth.valueOf() === currentDate) {
22363 clsName += ' active';
22366 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22367 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22368 clsName += ' disabled';
22371 fillMonths.cn.push({
22373 cls: 'day ' + clsName,
22374 html: prevMonth.getDate()
22377 prevMonth.setDate(prevMonth.getDate()+1);
22380 var currentYear = this.date && this.date.getUTCFullYear();
22381 var currentMonth = this.date && this.date.getUTCMonth();
22383 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22385 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22386 v.removeClass('active');
22388 if(currentYear === year && k === currentMonth){
22389 v.addClass('active');
22392 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22393 v.addClass('disabled');
22399 year = parseInt(year/10, 10) * 10;
22401 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22403 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22406 for (var i = -1; i < 11; i++) {
22407 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22409 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22417 showMode: function(dir)
22420 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22423 Roo.each(this.picker().select('>div',true).elements, function(v){
22424 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22427 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22432 if(this.isInline) {
22436 this.picker().removeClass(['bottom', 'top']);
22438 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22440 * place to the top of element!
22444 this.picker().addClass('top');
22445 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22450 this.picker().addClass('bottom');
22452 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22455 parseDate : function(value)
22457 if(!value || value instanceof Date){
22460 var v = Date.parseDate(value, this.format);
22461 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22462 v = Date.parseDate(value, 'Y-m-d');
22464 if(!v && this.altFormats){
22465 if(!this.altFormatsArray){
22466 this.altFormatsArray = this.altFormats.split("|");
22468 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22469 v = Date.parseDate(value, this.altFormatsArray[i]);
22475 formatDate : function(date, fmt)
22477 return (!date || !(date instanceof Date)) ?
22478 date : date.dateFormat(fmt || this.format);
22481 onFocus : function()
22483 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22487 onBlur : function()
22489 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22491 var d = this.inputEl().getValue();
22498 showPopup : function()
22500 this.picker().show();
22504 this.fireEvent('showpopup', this, this.date);
22507 hidePopup : function()
22509 if(this.isInline) {
22512 this.picker().hide();
22513 this.viewMode = this.startViewMode;
22516 this.fireEvent('hidepopup', this, this.date);
22520 onMousedown: function(e)
22522 e.stopPropagation();
22523 e.preventDefault();
22528 Roo.bootstrap.DateField.superclass.keyup.call(this);
22532 setValue: function(v)
22534 if(this.fireEvent('beforeselect', this, v) !== false){
22535 var d = new Date(this.parseDate(v) ).clearTime();
22537 if(isNaN(d.getTime())){
22538 this.date = this.viewDate = '';
22539 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22543 v = this.formatDate(d);
22545 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22547 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22551 this.fireEvent('select', this, this.date);
22555 getValue: function()
22557 return this.formatDate(this.date);
22560 fireKey: function(e)
22562 if (!this.picker().isVisible()){
22563 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22569 var dateChanged = false,
22571 newDate, newViewDate;
22576 e.preventDefault();
22580 if (!this.keyboardNavigation) {
22583 dir = e.keyCode == 37 ? -1 : 1;
22586 newDate = this.moveYear(this.date, dir);
22587 newViewDate = this.moveYear(this.viewDate, dir);
22588 } else if (e.shiftKey){
22589 newDate = this.moveMonth(this.date, dir);
22590 newViewDate = this.moveMonth(this.viewDate, dir);
22592 newDate = new Date(this.date);
22593 newDate.setUTCDate(this.date.getUTCDate() + dir);
22594 newViewDate = new Date(this.viewDate);
22595 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22597 if (this.dateWithinRange(newDate)){
22598 this.date = newDate;
22599 this.viewDate = newViewDate;
22600 this.setValue(this.formatDate(this.date));
22602 e.preventDefault();
22603 dateChanged = true;
22608 if (!this.keyboardNavigation) {
22611 dir = e.keyCode == 38 ? -1 : 1;
22613 newDate = this.moveYear(this.date, dir);
22614 newViewDate = this.moveYear(this.viewDate, dir);
22615 } else if (e.shiftKey){
22616 newDate = this.moveMonth(this.date, dir);
22617 newViewDate = this.moveMonth(this.viewDate, dir);
22619 newDate = new Date(this.date);
22620 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22621 newViewDate = new Date(this.viewDate);
22622 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22624 if (this.dateWithinRange(newDate)){
22625 this.date = newDate;
22626 this.viewDate = newViewDate;
22627 this.setValue(this.formatDate(this.date));
22629 e.preventDefault();
22630 dateChanged = true;
22634 this.setValue(this.formatDate(this.date));
22636 e.preventDefault();
22639 this.setValue(this.formatDate(this.date));
22653 onClick: function(e)
22655 e.stopPropagation();
22656 e.preventDefault();
22658 var target = e.getTarget();
22660 if(target.nodeName.toLowerCase() === 'i'){
22661 target = Roo.get(target).dom.parentNode;
22664 var nodeName = target.nodeName;
22665 var className = target.className;
22666 var html = target.innerHTML;
22667 //Roo.log(nodeName);
22669 switch(nodeName.toLowerCase()) {
22671 switch(className) {
22677 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22678 switch(this.viewMode){
22680 this.viewDate = this.moveMonth(this.viewDate, dir);
22684 this.viewDate = this.moveYear(this.viewDate, dir);
22690 var date = new Date();
22691 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22693 this.setValue(this.formatDate(this.date));
22700 if (className.indexOf('disabled') < 0) {
22701 if (!this.viewDate) {
22702 this.viewDate = new Date();
22704 this.viewDate.setUTCDate(1);
22705 if (className.indexOf('month') > -1) {
22706 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22708 var year = parseInt(html, 10) || 0;
22709 this.viewDate.setUTCFullYear(year);
22713 if(this.singleMode){
22714 this.setValue(this.formatDate(this.viewDate));
22725 //Roo.log(className);
22726 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22727 var day = parseInt(html, 10) || 1;
22728 var year = (this.viewDate || new Date()).getUTCFullYear(),
22729 month = (this.viewDate || new Date()).getUTCMonth();
22731 if (className.indexOf('old') > -1) {
22738 } else if (className.indexOf('new') > -1) {
22746 //Roo.log([year,month,day]);
22747 this.date = this.UTCDate(year, month, day,0,0,0,0);
22748 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22750 //Roo.log(this.formatDate(this.date));
22751 this.setValue(this.formatDate(this.date));
22758 setStartDate: function(startDate)
22760 this.startDate = startDate || -Infinity;
22761 if (this.startDate !== -Infinity) {
22762 this.startDate = this.parseDate(this.startDate);
22765 this.updateNavArrows();
22768 setEndDate: function(endDate)
22770 this.endDate = endDate || Infinity;
22771 if (this.endDate !== Infinity) {
22772 this.endDate = this.parseDate(this.endDate);
22775 this.updateNavArrows();
22778 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22780 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22781 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22782 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22784 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22785 return parseInt(d, 10);
22788 this.updateNavArrows();
22791 updateNavArrows: function()
22793 if(this.singleMode){
22797 var d = new Date(this.viewDate),
22798 year = d.getUTCFullYear(),
22799 month = d.getUTCMonth();
22801 Roo.each(this.picker().select('.prev', true).elements, function(v){
22803 switch (this.viewMode) {
22806 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22812 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22819 Roo.each(this.picker().select('.next', true).elements, function(v){
22821 switch (this.viewMode) {
22824 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22830 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22838 moveMonth: function(date, dir)
22843 var new_date = new Date(date.valueOf()),
22844 day = new_date.getUTCDate(),
22845 month = new_date.getUTCMonth(),
22846 mag = Math.abs(dir),
22848 dir = dir > 0 ? 1 : -1;
22851 // If going back one month, make sure month is not current month
22852 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22854 return new_date.getUTCMonth() == month;
22856 // If going forward one month, make sure month is as expected
22857 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22859 return new_date.getUTCMonth() != new_month;
22861 new_month = month + dir;
22862 new_date.setUTCMonth(new_month);
22863 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22864 if (new_month < 0 || new_month > 11) {
22865 new_month = (new_month + 12) % 12;
22868 // For magnitudes >1, move one month at a time...
22869 for (var i=0; i<mag; i++) {
22870 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22871 new_date = this.moveMonth(new_date, dir);
22873 // ...then reset the day, keeping it in the new month
22874 new_month = new_date.getUTCMonth();
22875 new_date.setUTCDate(day);
22877 return new_month != new_date.getUTCMonth();
22880 // Common date-resetting loop -- if date is beyond end of month, make it
22883 new_date.setUTCDate(--day);
22884 new_date.setUTCMonth(new_month);
22889 moveYear: function(date, dir)
22891 return this.moveMonth(date, dir*12);
22894 dateWithinRange: function(date)
22896 return date >= this.startDate && date <= this.endDate;
22902 this.picker().remove();
22905 validateValue : function(value)
22907 if(this.getVisibilityEl().hasClass('hidden')){
22911 if(value.length < 1) {
22912 if(this.allowBlank){
22918 if(value.length < this.minLength){
22921 if(value.length > this.maxLength){
22925 var vt = Roo.form.VTypes;
22926 if(!vt[this.vtype](value, this)){
22930 if(typeof this.validator == "function"){
22931 var msg = this.validator(value);
22937 if(this.regex && !this.regex.test(value)){
22941 if(typeof(this.parseDate(value)) == 'undefined'){
22945 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22949 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22959 this.date = this.viewDate = '';
22961 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22966 Roo.apply(Roo.bootstrap.DateField, {
22977 html: '<i class="fa fa-arrow-left"/>'
22987 html: '<i class="fa fa-arrow-right"/>'
23029 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23030 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23031 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23032 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23033 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23046 navFnc: 'FullYear',
23051 navFnc: 'FullYear',
23056 Roo.apply(Roo.bootstrap.DateField, {
23060 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23064 cls: 'datepicker-days',
23068 cls: 'table-condensed',
23070 Roo.bootstrap.DateField.head,
23074 Roo.bootstrap.DateField.footer
23081 cls: 'datepicker-months',
23085 cls: 'table-condensed',
23087 Roo.bootstrap.DateField.head,
23088 Roo.bootstrap.DateField.content,
23089 Roo.bootstrap.DateField.footer
23096 cls: 'datepicker-years',
23100 cls: 'table-condensed',
23102 Roo.bootstrap.DateField.head,
23103 Roo.bootstrap.DateField.content,
23104 Roo.bootstrap.DateField.footer
23123 * @class Roo.bootstrap.TimeField
23124 * @extends Roo.bootstrap.Input
23125 * Bootstrap DateField class
23129 * Create a new TimeField
23130 * @param {Object} config The config object
23133 Roo.bootstrap.TimeField = function(config){
23134 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23138 * Fires when this field show.
23139 * @param {Roo.bootstrap.DateField} thisthis
23140 * @param {Mixed} date The date value
23145 * Fires when this field hide.
23146 * @param {Roo.bootstrap.DateField} this
23147 * @param {Mixed} date The date value
23152 * Fires when select a date.
23153 * @param {Roo.bootstrap.DateField} this
23154 * @param {Mixed} date The date value
23160 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23163 * @cfg {String} format
23164 * The default time format string which can be overriden for localization support. The format must be
23165 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23169 getAutoCreate : function()
23171 this.after = '<i class="fa far fa-clock"></i>';
23172 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23176 onRender: function(ct, position)
23179 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23181 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23183 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23185 this.pop = this.picker().select('>.datepicker-time',true).first();
23186 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23188 this.picker().on('mousedown', this.onMousedown, this);
23189 this.picker().on('click', this.onClick, this);
23191 this.picker().addClass('datepicker-dropdown');
23196 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23197 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23198 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23199 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23200 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23201 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23205 fireKey: function(e){
23206 if (!this.picker().isVisible()){
23207 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23213 e.preventDefault();
23221 this.onTogglePeriod();
23224 this.onIncrementMinutes();
23227 this.onDecrementMinutes();
23236 onClick: function(e) {
23237 e.stopPropagation();
23238 e.preventDefault();
23241 picker : function()
23243 return this.pickerEl;
23246 fillTime: function()
23248 var time = this.pop.select('tbody', true).first();
23250 time.dom.innerHTML = '';
23265 cls: 'hours-up fa fas fa-chevron-up'
23285 cls: 'minutes-up fa fas fa-chevron-up'
23306 cls: 'timepicker-hour',
23321 cls: 'timepicker-minute',
23336 cls: 'btn btn-primary period',
23358 cls: 'hours-down fa fas fa-chevron-down'
23378 cls: 'minutes-down fa fas fa-chevron-down'
23396 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23403 var hours = this.time.getHours();
23404 var minutes = this.time.getMinutes();
23417 hours = hours - 12;
23421 hours = '0' + hours;
23425 minutes = '0' + minutes;
23428 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23429 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23430 this.pop.select('button', true).first().dom.innerHTML = period;
23436 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23438 var cls = ['bottom'];
23440 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23447 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23451 //this.picker().setXY(20000,20000);
23452 this.picker().addClass(cls.join('-'));
23456 Roo.each(cls, function(c){
23461 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23462 //_this.picker().setTop(_this.inputEl().getHeight());
23466 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23468 //_this.picker().setTop(0 - _this.picker().getHeight());
23473 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23477 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23485 onFocus : function()
23487 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23491 onBlur : function()
23493 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23499 this.picker().show();
23504 this.fireEvent('show', this, this.date);
23509 this.picker().hide();
23512 this.fireEvent('hide', this, this.date);
23515 setTime : function()
23518 this.setValue(this.time.format(this.format));
23520 this.fireEvent('select', this, this.date);
23525 onMousedown: function(e){
23526 e.stopPropagation();
23527 e.preventDefault();
23530 onIncrementHours: function()
23532 Roo.log('onIncrementHours');
23533 this.time = this.time.add(Date.HOUR, 1);
23538 onDecrementHours: function()
23540 Roo.log('onDecrementHours');
23541 this.time = this.time.add(Date.HOUR, -1);
23545 onIncrementMinutes: function()
23547 Roo.log('onIncrementMinutes');
23548 this.time = this.time.add(Date.MINUTE, 1);
23552 onDecrementMinutes: function()
23554 Roo.log('onDecrementMinutes');
23555 this.time = this.time.add(Date.MINUTE, -1);
23559 onTogglePeriod: function()
23561 Roo.log('onTogglePeriod');
23562 this.time = this.time.add(Date.HOUR, 12);
23570 Roo.apply(Roo.bootstrap.TimeField, {
23574 cls: 'datepicker dropdown-menu',
23578 cls: 'datepicker-time',
23582 cls: 'table-condensed',
23611 cls: 'btn btn-info ok',
23639 * @class Roo.bootstrap.MonthField
23640 * @extends Roo.bootstrap.Input
23641 * Bootstrap MonthField class
23643 * @cfg {String} language default en
23646 * Create a new MonthField
23647 * @param {Object} config The config object
23650 Roo.bootstrap.MonthField = function(config){
23651 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23656 * Fires when this field show.
23657 * @param {Roo.bootstrap.MonthField} this
23658 * @param {Mixed} date The date value
23663 * Fires when this field hide.
23664 * @param {Roo.bootstrap.MonthField} this
23665 * @param {Mixed} date The date value
23670 * Fires when select a date.
23671 * @param {Roo.bootstrap.MonthField} this
23672 * @param {String} oldvalue The old value
23673 * @param {String} newvalue The new value
23679 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23681 onRender: function(ct, position)
23684 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23686 this.language = this.language || 'en';
23687 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23688 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23690 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23691 this.isInline = false;
23692 this.isInput = true;
23693 this.component = this.el.select('.add-on', true).first() || false;
23694 this.component = (this.component && this.component.length === 0) ? false : this.component;
23695 this.hasInput = this.component && this.inputEL().length;
23697 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23699 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23701 this.picker().on('mousedown', this.onMousedown, this);
23702 this.picker().on('click', this.onClick, this);
23704 this.picker().addClass('datepicker-dropdown');
23706 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23707 v.setStyle('width', '189px');
23714 if(this.isInline) {
23720 setValue: function(v, suppressEvent)
23722 var o = this.getValue();
23724 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23728 if(suppressEvent !== true){
23729 this.fireEvent('select', this, o, v);
23734 getValue: function()
23739 onClick: function(e)
23741 e.stopPropagation();
23742 e.preventDefault();
23744 var target = e.getTarget();
23746 if(target.nodeName.toLowerCase() === 'i'){
23747 target = Roo.get(target).dom.parentNode;
23750 var nodeName = target.nodeName;
23751 var className = target.className;
23752 var html = target.innerHTML;
23754 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23758 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23760 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23766 picker : function()
23768 return this.pickerEl;
23771 fillMonths: function()
23774 var months = this.picker().select('>.datepicker-months td', true).first();
23776 months.dom.innerHTML = '';
23782 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23785 months.createChild(month);
23794 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23795 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23798 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23799 e.removeClass('active');
23801 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23802 e.addClass('active');
23809 if(this.isInline) {
23813 this.picker().removeClass(['bottom', 'top']);
23815 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23817 * place to the top of element!
23821 this.picker().addClass('top');
23822 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23827 this.picker().addClass('bottom');
23829 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23832 onFocus : function()
23834 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23838 onBlur : function()
23840 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23842 var d = this.inputEl().getValue();
23851 this.picker().show();
23852 this.picker().select('>.datepicker-months', true).first().show();
23856 this.fireEvent('show', this, this.date);
23861 if(this.isInline) {
23864 this.picker().hide();
23865 this.fireEvent('hide', this, this.date);
23869 onMousedown: function(e)
23871 e.stopPropagation();
23872 e.preventDefault();
23877 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23881 fireKey: function(e)
23883 if (!this.picker().isVisible()){
23884 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23895 e.preventDefault();
23899 dir = e.keyCode == 37 ? -1 : 1;
23901 this.vIndex = this.vIndex + dir;
23903 if(this.vIndex < 0){
23907 if(this.vIndex > 11){
23911 if(isNaN(this.vIndex)){
23915 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23921 dir = e.keyCode == 38 ? -1 : 1;
23923 this.vIndex = this.vIndex + dir * 4;
23925 if(this.vIndex < 0){
23929 if(this.vIndex > 11){
23933 if(isNaN(this.vIndex)){
23937 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23942 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23943 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23947 e.preventDefault();
23950 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23951 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23967 this.picker().remove();
23972 Roo.apply(Roo.bootstrap.MonthField, {
23991 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23992 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23997 Roo.apply(Roo.bootstrap.MonthField, {
24001 cls: 'datepicker dropdown-menu roo-dynamic',
24005 cls: 'datepicker-months',
24009 cls: 'table-condensed',
24011 Roo.bootstrap.DateField.content
24031 * @class Roo.bootstrap.CheckBox
24032 * @extends Roo.bootstrap.Input
24033 * Bootstrap CheckBox class
24035 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24036 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24037 * @cfg {String} boxLabel The text that appears beside the checkbox
24038 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24039 * @cfg {Boolean} checked initnal the element
24040 * @cfg {Boolean} inline inline the element (default false)
24041 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24042 * @cfg {String} tooltip label tooltip
24045 * Create a new CheckBox
24046 * @param {Object} config The config object
24049 Roo.bootstrap.CheckBox = function(config){
24050 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24055 * Fires when the element is checked or unchecked.
24056 * @param {Roo.bootstrap.CheckBox} this This input
24057 * @param {Boolean} checked The new checked value
24062 * Fires when the element is click.
24063 * @param {Roo.bootstrap.CheckBox} this This input
24070 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24072 inputType: 'checkbox',
24081 // checkbox success does not make any sense really..
24086 getAutoCreate : function()
24088 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24094 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24097 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24103 type : this.inputType,
24104 value : this.inputValue,
24105 cls : 'roo-' + this.inputType, //'form-box',
24106 placeholder : this.placeholder || ''
24110 if(this.inputType != 'radio'){
24114 cls : 'roo-hidden-value',
24115 value : this.checked ? this.inputValue : this.valueOff
24120 if (this.weight) { // Validity check?
24121 cfg.cls += " " + this.inputType + "-" + this.weight;
24124 if (this.disabled) {
24125 input.disabled=true;
24129 input.checked = this.checked;
24134 input.name = this.name;
24136 if(this.inputType != 'radio'){
24137 hidden.name = this.name;
24138 input.name = '_hidden_' + this.name;
24143 input.cls += ' input-' + this.size;
24148 ['xs','sm','md','lg'].map(function(size){
24149 if (settings[size]) {
24150 cfg.cls += ' col-' + size + '-' + settings[size];
24154 var inputblock = input;
24156 if (this.before || this.after) {
24159 cls : 'input-group',
24164 inputblock.cn.push({
24166 cls : 'input-group-addon',
24171 inputblock.cn.push(input);
24173 if(this.inputType != 'radio'){
24174 inputblock.cn.push(hidden);
24178 inputblock.cn.push({
24180 cls : 'input-group-addon',
24186 var boxLabelCfg = false;
24192 //'for': id, // box label is handled by onclick - so no for...
24194 html: this.boxLabel
24197 boxLabelCfg.tooltip = this.tooltip;
24203 if (align ==='left' && this.fieldLabel.length) {
24204 // Roo.log("left and has label");
24209 cls : 'control-label',
24210 html : this.fieldLabel
24221 cfg.cn[1].cn.push(boxLabelCfg);
24224 if(this.labelWidth > 12){
24225 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24228 if(this.labelWidth < 13 && this.labelmd == 0){
24229 this.labelmd = this.labelWidth;
24232 if(this.labellg > 0){
24233 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24234 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24237 if(this.labelmd > 0){
24238 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24239 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24242 if(this.labelsm > 0){
24243 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24244 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24247 if(this.labelxs > 0){
24248 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24249 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24252 } else if ( this.fieldLabel.length) {
24253 // Roo.log(" label");
24257 tag: this.boxLabel ? 'span' : 'label',
24259 cls: 'control-label box-input-label',
24260 //cls : 'input-group-addon',
24261 html : this.fieldLabel
24268 cfg.cn.push(boxLabelCfg);
24273 // Roo.log(" no label && no align");
24274 cfg.cn = [ inputblock ] ;
24276 cfg.cn.push(boxLabelCfg);
24284 if(this.inputType != 'radio'){
24285 cfg.cn.push(hidden);
24293 * return the real input element.
24295 inputEl: function ()
24297 return this.el.select('input.roo-' + this.inputType,true).first();
24299 hiddenEl: function ()
24301 return this.el.select('input.roo-hidden-value',true).first();
24304 labelEl: function()
24306 return this.el.select('label.control-label',true).first();
24308 /* depricated... */
24312 return this.labelEl();
24315 boxLabelEl: function()
24317 return this.el.select('label.box-label',true).first();
24320 initEvents : function()
24322 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24324 this.inputEl().on('click', this.onClick, this);
24326 if (this.boxLabel) {
24327 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24330 this.startValue = this.getValue();
24333 Roo.bootstrap.CheckBox.register(this);
24337 onClick : function(e)
24339 if(this.fireEvent('click', this, e) !== false){
24340 this.setChecked(!this.checked);
24345 setChecked : function(state,suppressEvent)
24347 this.startValue = this.getValue();
24349 if(this.inputType == 'radio'){
24351 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24352 e.dom.checked = false;
24355 this.inputEl().dom.checked = true;
24357 this.inputEl().dom.value = this.inputValue;
24359 if(suppressEvent !== true){
24360 this.fireEvent('check', this, true);
24368 this.checked = state;
24370 this.inputEl().dom.checked = state;
24373 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24375 if(suppressEvent !== true){
24376 this.fireEvent('check', this, state);
24382 getValue : function()
24384 if(this.inputType == 'radio'){
24385 return this.getGroupValue();
24388 return this.hiddenEl().dom.value;
24392 getGroupValue : function()
24394 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24398 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24401 setValue : function(v,suppressEvent)
24403 if(this.inputType == 'radio'){
24404 this.setGroupValue(v, suppressEvent);
24408 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24413 setGroupValue : function(v, suppressEvent)
24415 this.startValue = this.getValue();
24417 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24418 e.dom.checked = false;
24420 if(e.dom.value == v){
24421 e.dom.checked = true;
24425 if(suppressEvent !== true){
24426 this.fireEvent('check', this, true);
24434 validate : function()
24436 if(this.getVisibilityEl().hasClass('hidden')){
24442 (this.inputType == 'radio' && this.validateRadio()) ||
24443 (this.inputType == 'checkbox' && this.validateCheckbox())
24449 this.markInvalid();
24453 validateRadio : function()
24455 if(this.getVisibilityEl().hasClass('hidden')){
24459 if(this.allowBlank){
24465 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24466 if(!e.dom.checked){
24478 validateCheckbox : function()
24481 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24482 //return (this.getValue() == this.inputValue) ? true : false;
24485 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24493 for(var i in group){
24494 if(group[i].el.isVisible(true)){
24502 for(var i in group){
24507 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24514 * Mark this field as valid
24516 markValid : function()
24520 this.fireEvent('valid', this);
24522 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24525 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24532 if(this.inputType == 'radio'){
24533 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24534 var fg = e.findParent('.form-group', false, true);
24535 if (Roo.bootstrap.version == 3) {
24536 fg.removeClass([_this.invalidClass, _this.validClass]);
24537 fg.addClass(_this.validClass);
24539 fg.removeClass(['is-valid', 'is-invalid']);
24540 fg.addClass('is-valid');
24548 var fg = this.el.findParent('.form-group', false, true);
24549 if (Roo.bootstrap.version == 3) {
24550 fg.removeClass([this.invalidClass, this.validClass]);
24551 fg.addClass(this.validClass);
24553 fg.removeClass(['is-valid', 'is-invalid']);
24554 fg.addClass('is-valid');
24559 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24565 for(var i in group){
24566 var fg = group[i].el.findParent('.form-group', false, true);
24567 if (Roo.bootstrap.version == 3) {
24568 fg.removeClass([this.invalidClass, this.validClass]);
24569 fg.addClass(this.validClass);
24571 fg.removeClass(['is-valid', 'is-invalid']);
24572 fg.addClass('is-valid');
24578 * Mark this field as invalid
24579 * @param {String} msg The validation message
24581 markInvalid : function(msg)
24583 if(this.allowBlank){
24589 this.fireEvent('invalid', this, msg);
24591 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24594 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24598 label.markInvalid();
24601 if(this.inputType == 'radio'){
24603 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24604 var fg = e.findParent('.form-group', false, true);
24605 if (Roo.bootstrap.version == 3) {
24606 fg.removeClass([_this.invalidClass, _this.validClass]);
24607 fg.addClass(_this.invalidClass);
24609 fg.removeClass(['is-invalid', 'is-valid']);
24610 fg.addClass('is-invalid');
24618 var fg = this.el.findParent('.form-group', false, true);
24619 if (Roo.bootstrap.version == 3) {
24620 fg.removeClass([_this.invalidClass, _this.validClass]);
24621 fg.addClass(_this.invalidClass);
24623 fg.removeClass(['is-invalid', 'is-valid']);
24624 fg.addClass('is-invalid');
24629 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24635 for(var i in group){
24636 var fg = group[i].el.findParent('.form-group', false, true);
24637 if (Roo.bootstrap.version == 3) {
24638 fg.removeClass([_this.invalidClass, _this.validClass]);
24639 fg.addClass(_this.invalidClass);
24641 fg.removeClass(['is-invalid', 'is-valid']);
24642 fg.addClass('is-invalid');
24648 clearInvalid : function()
24650 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24652 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24654 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24656 if (label && label.iconEl) {
24657 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24658 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24662 disable : function()
24664 if(this.inputType != 'radio'){
24665 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24672 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24673 _this.getActionEl().addClass(this.disabledClass);
24674 e.dom.disabled = true;
24678 this.disabled = true;
24679 this.fireEvent("disable", this);
24683 enable : function()
24685 if(this.inputType != 'radio'){
24686 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24693 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24694 _this.getActionEl().removeClass(this.disabledClass);
24695 e.dom.disabled = false;
24699 this.disabled = false;
24700 this.fireEvent("enable", this);
24704 setBoxLabel : function(v)
24709 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24715 Roo.apply(Roo.bootstrap.CheckBox, {
24720 * register a CheckBox Group
24721 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24723 register : function(checkbox)
24725 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24726 this.groups[checkbox.groupId] = {};
24729 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24733 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24737 * fetch a CheckBox Group based on the group ID
24738 * @param {string} the group ID
24739 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24741 get: function(groupId) {
24742 if (typeof(this.groups[groupId]) == 'undefined') {
24746 return this.groups[groupId] ;
24759 * @class Roo.bootstrap.Radio
24760 * @extends Roo.bootstrap.Component
24761 * Bootstrap Radio class
24762 * @cfg {String} boxLabel - the label associated
24763 * @cfg {String} value - the value of radio
24766 * Create a new Radio
24767 * @param {Object} config The config object
24769 Roo.bootstrap.Radio = function(config){
24770 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24774 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24780 getAutoCreate : function()
24784 cls : 'form-group radio',
24789 html : this.boxLabel
24797 initEvents : function()
24799 this.parent().register(this);
24801 this.el.on('click', this.onClick, this);
24805 onClick : function(e)
24807 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24808 this.setChecked(true);
24812 setChecked : function(state, suppressEvent)
24814 this.parent().setValue(this.value, suppressEvent);
24818 setBoxLabel : function(v)
24823 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24838 * @class Roo.bootstrap.SecurePass
24839 * @extends Roo.bootstrap.Input
24840 * Bootstrap SecurePass class
24844 * Create a new SecurePass
24845 * @param {Object} config The config object
24848 Roo.bootstrap.SecurePass = function (config) {
24849 // these go here, so the translation tool can replace them..
24851 PwdEmpty: "Please type a password, and then retype it to confirm.",
24852 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24853 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24854 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24855 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24856 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24857 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24858 TooWeak: "Your password is Too Weak."
24860 this.meterLabel = "Password strength:";
24861 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24862 this.meterClass = [
24863 "roo-password-meter-tooweak",
24864 "roo-password-meter-weak",
24865 "roo-password-meter-medium",
24866 "roo-password-meter-strong",
24867 "roo-password-meter-grey"
24872 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24875 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24877 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24879 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24880 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24881 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24882 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24883 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24884 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24885 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24895 * @cfg {String/Object} Label for the strength meter (defaults to
24896 * 'Password strength:')
24901 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24902 * ['Weak', 'Medium', 'Strong'])
24905 pwdStrengths: false,
24918 initEvents: function ()
24920 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24922 if (this.el.is('input[type=password]') && Roo.isSafari) {
24923 this.el.on('keydown', this.SafariOnKeyDown, this);
24926 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24929 onRender: function (ct, position)
24931 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24932 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24933 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24935 this.trigger.createChild({
24940 cls: 'roo-password-meter-grey col-xs-12',
24943 //width: this.meterWidth + 'px'
24947 cls: 'roo-password-meter-text'
24953 if (this.hideTrigger) {
24954 this.trigger.setDisplayed(false);
24956 this.setSize(this.width || '', this.height || '');
24959 onDestroy: function ()
24961 if (this.trigger) {
24962 this.trigger.removeAllListeners();
24963 this.trigger.remove();
24966 this.wrap.remove();
24968 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24971 checkStrength: function ()
24973 var pwd = this.inputEl().getValue();
24974 if (pwd == this._lastPwd) {
24979 if (this.ClientSideStrongPassword(pwd)) {
24981 } else if (this.ClientSideMediumPassword(pwd)) {
24983 } else if (this.ClientSideWeakPassword(pwd)) {
24989 Roo.log('strength1: ' + strength);
24991 //var pm = this.trigger.child('div/div/div').dom;
24992 var pm = this.trigger.child('div/div');
24993 pm.removeClass(this.meterClass);
24994 pm.addClass(this.meterClass[strength]);
24997 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24999 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25001 this._lastPwd = pwd;
25005 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25007 this._lastPwd = '';
25009 var pm = this.trigger.child('div/div');
25010 pm.removeClass(this.meterClass);
25011 pm.addClass('roo-password-meter-grey');
25014 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25017 this.inputEl().dom.type='password';
25020 validateValue: function (value)
25022 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25025 if (value.length == 0) {
25026 if (this.allowBlank) {
25027 this.clearInvalid();
25031 this.markInvalid(this.errors.PwdEmpty);
25032 this.errorMsg = this.errors.PwdEmpty;
25040 if (!value.match(/[\x21-\x7e]+/)) {
25041 this.markInvalid(this.errors.PwdBadChar);
25042 this.errorMsg = this.errors.PwdBadChar;
25045 if (value.length < 6) {
25046 this.markInvalid(this.errors.PwdShort);
25047 this.errorMsg = this.errors.PwdShort;
25050 if (value.length > 16) {
25051 this.markInvalid(this.errors.PwdLong);
25052 this.errorMsg = this.errors.PwdLong;
25056 if (this.ClientSideStrongPassword(value)) {
25058 } else if (this.ClientSideMediumPassword(value)) {
25060 } else if (this.ClientSideWeakPassword(value)) {
25067 if (strength < 2) {
25068 //this.markInvalid(this.errors.TooWeak);
25069 this.errorMsg = this.errors.TooWeak;
25074 console.log('strength2: ' + strength);
25076 //var pm = this.trigger.child('div/div/div').dom;
25078 var pm = this.trigger.child('div/div');
25079 pm.removeClass(this.meterClass);
25080 pm.addClass(this.meterClass[strength]);
25082 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25084 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25086 this.errorMsg = '';
25090 CharacterSetChecks: function (type)
25093 this.fResult = false;
25096 isctype: function (character, type)
25099 case this.kCapitalLetter:
25100 if (character >= 'A' && character <= 'Z') {
25105 case this.kSmallLetter:
25106 if (character >= 'a' && character <= 'z') {
25112 if (character >= '0' && character <= '9') {
25117 case this.kPunctuation:
25118 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25129 IsLongEnough: function (pwd, size)
25131 return !(pwd == null || isNaN(size) || pwd.length < size);
25134 SpansEnoughCharacterSets: function (word, nb)
25136 if (!this.IsLongEnough(word, nb))
25141 var characterSetChecks = new Array(
25142 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25143 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25146 for (var index = 0; index < word.length; ++index) {
25147 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25148 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25149 characterSetChecks[nCharSet].fResult = true;
25156 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25157 if (characterSetChecks[nCharSet].fResult) {
25162 if (nCharSets < nb) {
25168 ClientSideStrongPassword: function (pwd)
25170 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25173 ClientSideMediumPassword: function (pwd)
25175 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25178 ClientSideWeakPassword: function (pwd)
25180 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25183 })//<script type="text/javascript">
25186 * Based Ext JS Library 1.1.1
25187 * Copyright(c) 2006-2007, Ext JS, LLC.
25193 * @class Roo.HtmlEditorCore
25194 * @extends Roo.Component
25195 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25197 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25200 Roo.HtmlEditorCore = function(config){
25203 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25208 * @event initialize
25209 * Fires when the editor is fully initialized (including the iframe)
25210 * @param {Roo.HtmlEditorCore} this
25215 * Fires when the editor is first receives the focus. Any insertion must wait
25216 * until after this event.
25217 * @param {Roo.HtmlEditorCore} this
25221 * @event beforesync
25222 * Fires before the textarea is updated with content from the editor iframe. Return false
25223 * to cancel the sync.
25224 * @param {Roo.HtmlEditorCore} this
25225 * @param {String} html
25229 * @event beforepush
25230 * Fires before the iframe editor is updated with content from the textarea. Return false
25231 * to cancel the push.
25232 * @param {Roo.HtmlEditorCore} this
25233 * @param {String} html
25238 * Fires when the textarea is updated with content from the editor iframe.
25239 * @param {Roo.HtmlEditorCore} this
25240 * @param {String} html
25245 * Fires when the iframe editor is updated with content from the textarea.
25246 * @param {Roo.HtmlEditorCore} this
25247 * @param {String} html
25252 * @event editorevent
25253 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25254 * @param {Roo.HtmlEditorCore} this
25260 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25262 // defaults : white / black...
25263 this.applyBlacklists();
25270 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25274 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25280 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25285 * @cfg {Number} height (in pixels)
25289 * @cfg {Number} width (in pixels)
25294 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25297 stylesheets: false,
25302 // private properties
25303 validationEvent : false,
25305 initialized : false,
25307 sourceEditMode : false,
25308 onFocus : Roo.emptyFn,
25310 hideMode:'offsets',
25314 // blacklist + whitelisted elements..
25321 * Protected method that will not generally be called directly. It
25322 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25323 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25325 getDocMarkup : function(){
25329 // inherit styels from page...??
25330 if (this.stylesheets === false) {
25332 Roo.get(document.head).select('style').each(function(node) {
25333 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25336 Roo.get(document.head).select('link').each(function(node) {
25337 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25340 } else if (!this.stylesheets.length) {
25342 st = '<style type="text/css">' +
25343 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25346 for (var i in this.stylesheets) {
25347 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25352 st += '<style type="text/css">' +
25353 'IMG { cursor: pointer } ' +
25356 var cls = 'roo-htmleditor-body';
25358 if(this.bodyCls.length){
25359 cls += ' ' + this.bodyCls;
25362 return '<html><head>' + st +
25363 //<style type="text/css">' +
25364 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25366 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25370 onRender : function(ct, position)
25373 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25374 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25377 this.el.dom.style.border = '0 none';
25378 this.el.dom.setAttribute('tabIndex', -1);
25379 this.el.addClass('x-hidden hide');
25383 if(Roo.isIE){ // fix IE 1px bogus margin
25384 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25388 this.frameId = Roo.id();
25392 var iframe = this.owner.wrap.createChild({
25394 cls: 'form-control', // bootstrap..
25396 name: this.frameId,
25397 frameBorder : 'no',
25398 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25403 this.iframe = iframe.dom;
25405 this.assignDocWin();
25407 this.doc.designMode = 'on';
25410 this.doc.write(this.getDocMarkup());
25414 var task = { // must defer to wait for browser to be ready
25416 //console.log("run task?" + this.doc.readyState);
25417 this.assignDocWin();
25418 if(this.doc.body || this.doc.readyState == 'complete'){
25420 this.doc.designMode="on";
25424 Roo.TaskMgr.stop(task);
25425 this.initEditor.defer(10, this);
25432 Roo.TaskMgr.start(task);
25437 onResize : function(w, h)
25439 Roo.log('resize: ' +w + ',' + h );
25440 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25444 if(typeof w == 'number'){
25446 this.iframe.style.width = w + 'px';
25448 if(typeof h == 'number'){
25450 this.iframe.style.height = h + 'px';
25452 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25459 * Toggles the editor between standard and source edit mode.
25460 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25462 toggleSourceEdit : function(sourceEditMode){
25464 this.sourceEditMode = sourceEditMode === true;
25466 if(this.sourceEditMode){
25468 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25471 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25472 //this.iframe.className = '';
25475 //this.setSize(this.owner.wrap.getSize());
25476 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25483 * Protected method that will not generally be called directly. If you need/want
25484 * custom HTML cleanup, this is the method you should override.
25485 * @param {String} html The HTML to be cleaned
25486 * return {String} The cleaned HTML
25488 cleanHtml : function(html){
25489 html = String(html);
25490 if(html.length > 5){
25491 if(Roo.isSafari){ // strip safari nonsense
25492 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25495 if(html == ' '){
25502 * HTML Editor -> Textarea
25503 * Protected method that will not generally be called directly. Syncs the contents
25504 * of the editor iframe with the textarea.
25506 syncValue : function(){
25507 if(this.initialized){
25508 var bd = (this.doc.body || this.doc.documentElement);
25509 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25510 var html = bd.innerHTML;
25512 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25513 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25515 html = '<div style="'+m[0]+'">' + html + '</div>';
25518 html = this.cleanHtml(html);
25519 // fix up the special chars.. normaly like back quotes in word...
25520 // however we do not want to do this with chinese..
25521 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25523 var cc = match.charCodeAt();
25525 // Get the character value, handling surrogate pairs
25526 if (match.length == 2) {
25527 // It's a surrogate pair, calculate the Unicode code point
25528 var high = match.charCodeAt(0) - 0xD800;
25529 var low = match.charCodeAt(1) - 0xDC00;
25530 cc = (high * 0x400) + low + 0x10000;
25532 (cc >= 0x4E00 && cc < 0xA000 ) ||
25533 (cc >= 0x3400 && cc < 0x4E00 ) ||
25534 (cc >= 0xf900 && cc < 0xfb00 )
25539 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25540 return "&#" + cc + ";";
25547 if(this.owner.fireEvent('beforesync', this, html) !== false){
25548 this.el.dom.value = html;
25549 this.owner.fireEvent('sync', this, html);
25555 * Protected method that will not generally be called directly. Pushes the value of the textarea
25556 * into the iframe editor.
25558 pushValue : function(){
25559 if(this.initialized){
25560 var v = this.el.dom.value.trim();
25562 // if(v.length < 1){
25566 if(this.owner.fireEvent('beforepush', this, v) !== false){
25567 var d = (this.doc.body || this.doc.documentElement);
25569 this.cleanUpPaste();
25570 this.el.dom.value = d.innerHTML;
25571 this.owner.fireEvent('push', this, v);
25577 deferFocus : function(){
25578 this.focus.defer(10, this);
25582 focus : function(){
25583 if(this.win && !this.sourceEditMode){
25590 assignDocWin: function()
25592 var iframe = this.iframe;
25595 this.doc = iframe.contentWindow.document;
25596 this.win = iframe.contentWindow;
25598 // if (!Roo.get(this.frameId)) {
25601 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25602 // this.win = Roo.get(this.frameId).dom.contentWindow;
25604 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25608 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25609 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25614 initEditor : function(){
25615 //console.log("INIT EDITOR");
25616 this.assignDocWin();
25620 this.doc.designMode="on";
25622 this.doc.write(this.getDocMarkup());
25625 var dbody = (this.doc.body || this.doc.documentElement);
25626 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25627 // this copies styles from the containing element into thsi one..
25628 // not sure why we need all of this..
25629 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25631 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25632 //ss['background-attachment'] = 'fixed'; // w3c
25633 dbody.bgProperties = 'fixed'; // ie
25634 //Roo.DomHelper.applyStyles(dbody, ss);
25635 Roo.EventManager.on(this.doc, {
25636 //'mousedown': this.onEditorEvent,
25637 'mouseup': this.onEditorEvent,
25638 'dblclick': this.onEditorEvent,
25639 'click': this.onEditorEvent,
25640 'keyup': this.onEditorEvent,
25645 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25647 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25648 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25650 this.initialized = true;
25652 this.owner.fireEvent('initialize', this);
25657 onDestroy : function(){
25663 //for (var i =0; i < this.toolbars.length;i++) {
25664 // // fixme - ask toolbars for heights?
25665 // this.toolbars[i].onDestroy();
25668 //this.wrap.dom.innerHTML = '';
25669 //this.wrap.remove();
25674 onFirstFocus : function(){
25676 this.assignDocWin();
25679 this.activated = true;
25682 if(Roo.isGecko){ // prevent silly gecko errors
25684 var s = this.win.getSelection();
25685 if(!s.focusNode || s.focusNode.nodeType != 3){
25686 var r = s.getRangeAt(0);
25687 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25692 this.execCmd('useCSS', true);
25693 this.execCmd('styleWithCSS', false);
25696 this.owner.fireEvent('activate', this);
25700 adjustFont: function(btn){
25701 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25702 //if(Roo.isSafari){ // safari
25705 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25706 if(Roo.isSafari){ // safari
25707 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25708 v = (v < 10) ? 10 : v;
25709 v = (v > 48) ? 48 : v;
25710 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25715 v = Math.max(1, v+adjust);
25717 this.execCmd('FontSize', v );
25720 onEditorEvent : function(e)
25722 this.owner.fireEvent('editorevent', this, e);
25723 // this.updateToolbar();
25724 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25727 insertTag : function(tg)
25729 // could be a bit smarter... -> wrap the current selected tRoo..
25730 if (tg.toLowerCase() == 'span' ||
25731 tg.toLowerCase() == 'code' ||
25732 tg.toLowerCase() == 'sup' ||
25733 tg.toLowerCase() == 'sub'
25736 range = this.createRange(this.getSelection());
25737 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25738 wrappingNode.appendChild(range.extractContents());
25739 range.insertNode(wrappingNode);
25746 this.execCmd("formatblock", tg);
25750 insertText : function(txt)
25754 var range = this.createRange();
25755 range.deleteContents();
25756 //alert(Sender.getAttribute('label'));
25758 range.insertNode(this.doc.createTextNode(txt));
25764 * Executes a Midas editor command on the editor document and performs necessary focus and
25765 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25766 * @param {String} cmd The Midas command
25767 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25769 relayCmd : function(cmd, value){
25771 this.execCmd(cmd, value);
25772 this.owner.fireEvent('editorevent', this);
25773 //this.updateToolbar();
25774 this.owner.deferFocus();
25778 * Executes a Midas editor command directly on the editor document.
25779 * For visual commands, you should use {@link #relayCmd} instead.
25780 * <b>This should only be called after the editor is initialized.</b>
25781 * @param {String} cmd The Midas command
25782 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25784 execCmd : function(cmd, value){
25785 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25792 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25794 * @param {String} text | dom node..
25796 insertAtCursor : function(text)
25799 if(!this.activated){
25805 var r = this.doc.selection.createRange();
25816 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25820 // from jquery ui (MIT licenced)
25822 var win = this.win;
25824 if (win.getSelection && win.getSelection().getRangeAt) {
25825 range = win.getSelection().getRangeAt(0);
25826 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25827 range.insertNode(node);
25828 } else if (win.document.selection && win.document.selection.createRange) {
25829 // no firefox support
25830 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25831 win.document.selection.createRange().pasteHTML(txt);
25833 // no firefox support
25834 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25835 this.execCmd('InsertHTML', txt);
25844 mozKeyPress : function(e){
25846 var c = e.getCharCode(), cmd;
25849 c = String.fromCharCode(c).toLowerCase();
25863 this.cleanUpPaste.defer(100, this);
25871 e.preventDefault();
25879 fixKeys : function(){ // load time branching for fastest keydown performance
25881 return function(e){
25882 var k = e.getKey(), r;
25885 r = this.doc.selection.createRange();
25888 r.pasteHTML('    ');
25895 r = this.doc.selection.createRange();
25897 var target = r.parentElement();
25898 if(!target || target.tagName.toLowerCase() != 'li'){
25900 r.pasteHTML('<br />');
25906 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25907 this.cleanUpPaste.defer(100, this);
25913 }else if(Roo.isOpera){
25914 return function(e){
25915 var k = e.getKey();
25919 this.execCmd('InsertHTML','    ');
25922 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25923 this.cleanUpPaste.defer(100, this);
25928 }else if(Roo.isSafari){
25929 return function(e){
25930 var k = e.getKey();
25934 this.execCmd('InsertText','\t');
25938 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25939 this.cleanUpPaste.defer(100, this);
25947 getAllAncestors: function()
25949 var p = this.getSelectedNode();
25952 a.push(p); // push blank onto stack..
25953 p = this.getParentElement();
25957 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25961 a.push(this.doc.body);
25965 lastSelNode : false,
25968 getSelection : function()
25970 this.assignDocWin();
25971 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25974 getSelectedNode: function()
25976 // this may only work on Gecko!!!
25978 // should we cache this!!!!
25983 var range = this.createRange(this.getSelection()).cloneRange();
25986 var parent = range.parentElement();
25988 var testRange = range.duplicate();
25989 testRange.moveToElementText(parent);
25990 if (testRange.inRange(range)) {
25993 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25996 parent = parent.parentElement;
26001 // is ancestor a text element.
26002 var ac = range.commonAncestorContainer;
26003 if (ac.nodeType == 3) {
26004 ac = ac.parentNode;
26007 var ar = ac.childNodes;
26010 var other_nodes = [];
26011 var has_other_nodes = false;
26012 for (var i=0;i<ar.length;i++) {
26013 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26016 // fullly contained node.
26018 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26023 // probably selected..
26024 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26025 other_nodes.push(ar[i]);
26029 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26034 has_other_nodes = true;
26036 if (!nodes.length && other_nodes.length) {
26037 nodes= other_nodes;
26039 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26045 createRange: function(sel)
26047 // this has strange effects when using with
26048 // top toolbar - not sure if it's a great idea.
26049 //this.editor.contentWindow.focus();
26050 if (typeof sel != "undefined") {
26052 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26054 return this.doc.createRange();
26057 return this.doc.createRange();
26060 getParentElement: function()
26063 this.assignDocWin();
26064 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26066 var range = this.createRange(sel);
26069 var p = range.commonAncestorContainer;
26070 while (p.nodeType == 3) { // text node
26081 * Range intersection.. the hard stuff...
26085 * [ -- selected range --- ]
26089 * if end is before start or hits it. fail.
26090 * if start is after end or hits it fail.
26092 * if either hits (but other is outside. - then it's not
26098 // @see http://www.thismuchiknow.co.uk/?p=64.
26099 rangeIntersectsNode : function(range, node)
26101 var nodeRange = node.ownerDocument.createRange();
26103 nodeRange.selectNode(node);
26105 nodeRange.selectNodeContents(node);
26108 var rangeStartRange = range.cloneRange();
26109 rangeStartRange.collapse(true);
26111 var rangeEndRange = range.cloneRange();
26112 rangeEndRange.collapse(false);
26114 var nodeStartRange = nodeRange.cloneRange();
26115 nodeStartRange.collapse(true);
26117 var nodeEndRange = nodeRange.cloneRange();
26118 nodeEndRange.collapse(false);
26120 return rangeStartRange.compareBoundaryPoints(
26121 Range.START_TO_START, nodeEndRange) == -1 &&
26122 rangeEndRange.compareBoundaryPoints(
26123 Range.START_TO_START, nodeStartRange) == 1;
26127 rangeCompareNode : function(range, node)
26129 var nodeRange = node.ownerDocument.createRange();
26131 nodeRange.selectNode(node);
26133 nodeRange.selectNodeContents(node);
26137 range.collapse(true);
26139 nodeRange.collapse(true);
26141 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26142 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26144 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26146 var nodeIsBefore = ss == 1;
26147 var nodeIsAfter = ee == -1;
26149 if (nodeIsBefore && nodeIsAfter) {
26152 if (!nodeIsBefore && nodeIsAfter) {
26153 return 1; //right trailed.
26156 if (nodeIsBefore && !nodeIsAfter) {
26157 return 2; // left trailed.
26163 // private? - in a new class?
26164 cleanUpPaste : function()
26166 // cleans up the whole document..
26167 Roo.log('cleanuppaste');
26169 this.cleanUpChildren(this.doc.body);
26170 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26171 if (clean != this.doc.body.innerHTML) {
26172 this.doc.body.innerHTML = clean;
26177 cleanWordChars : function(input) {// change the chars to hex code
26178 var he = Roo.HtmlEditorCore;
26180 var output = input;
26181 Roo.each(he.swapCodes, function(sw) {
26182 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26184 output = output.replace(swapper, sw[1]);
26191 cleanUpChildren : function (n)
26193 if (!n.childNodes.length) {
26196 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26197 this.cleanUpChild(n.childNodes[i]);
26204 cleanUpChild : function (node)
26207 //console.log(node);
26208 if (node.nodeName == "#text") {
26209 // clean up silly Windows -- stuff?
26212 if (node.nodeName == "#comment") {
26213 node.parentNode.removeChild(node);
26214 // clean up silly Windows -- stuff?
26217 var lcname = node.tagName.toLowerCase();
26218 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26219 // whitelist of tags..
26221 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26223 node.parentNode.removeChild(node);
26228 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26230 // spans with no attributes - just remove them..
26231 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26232 remove_keep_children = true;
26235 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26236 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26238 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26239 // remove_keep_children = true;
26242 if (remove_keep_children) {
26243 this.cleanUpChildren(node);
26244 // inserts everything just before this node...
26245 while (node.childNodes.length) {
26246 var cn = node.childNodes[0];
26247 node.removeChild(cn);
26248 node.parentNode.insertBefore(cn, node);
26250 node.parentNode.removeChild(node);
26254 if (!node.attributes || !node.attributes.length) {
26259 this.cleanUpChildren(node);
26263 function cleanAttr(n,v)
26266 if (v.match(/^\./) || v.match(/^\//)) {
26269 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26272 if (v.match(/^#/)) {
26275 if (v.match(/^\{/)) { // allow template editing.
26278 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26279 node.removeAttribute(n);
26283 var cwhite = this.cwhite;
26284 var cblack = this.cblack;
26286 function cleanStyle(n,v)
26288 if (v.match(/expression/)) { //XSS?? should we even bother..
26289 node.removeAttribute(n);
26293 var parts = v.split(/;/);
26296 Roo.each(parts, function(p) {
26297 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26301 var l = p.split(':').shift().replace(/\s+/g,'');
26302 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26304 if ( cwhite.length && cblack.indexOf(l) > -1) {
26305 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26306 //node.removeAttribute(n);
26310 // only allow 'c whitelisted system attributes'
26311 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26312 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26313 //node.removeAttribute(n);
26323 if (clean.length) {
26324 node.setAttribute(n, clean.join(';'));
26326 node.removeAttribute(n);
26332 for (var i = node.attributes.length-1; i > -1 ; i--) {
26333 var a = node.attributes[i];
26336 if (a.name.toLowerCase().substr(0,2)=='on') {
26337 node.removeAttribute(a.name);
26340 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26341 node.removeAttribute(a.name);
26344 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26345 cleanAttr(a.name,a.value); // fixme..
26348 if (a.name == 'style') {
26349 cleanStyle(a.name,a.value);
26352 /// clean up MS crap..
26353 // tecnically this should be a list of valid class'es..
26356 if (a.name == 'class') {
26357 if (a.value.match(/^Mso/)) {
26358 node.removeAttribute('class');
26361 if (a.value.match(/^body$/)) {
26362 node.removeAttribute('class');
26373 this.cleanUpChildren(node);
26379 * Clean up MS wordisms...
26381 cleanWord : function(node)
26384 this.cleanWord(this.doc.body);
26389 node.nodeName == 'SPAN' &&
26390 !node.hasAttributes() &&
26391 node.childNodes.length == 1 &&
26392 node.firstChild.nodeName == "#text"
26394 var textNode = node.firstChild;
26395 node.removeChild(textNode);
26396 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26397 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26399 node.parentNode.insertBefore(textNode, node);
26400 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26401 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26403 node.parentNode.removeChild(node);
26406 if (node.nodeName == "#text") {
26407 // clean up silly Windows -- stuff?
26410 if (node.nodeName == "#comment") {
26411 node.parentNode.removeChild(node);
26412 // clean up silly Windows -- stuff?
26416 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26417 node.parentNode.removeChild(node);
26420 //Roo.log(node.tagName);
26421 // remove - but keep children..
26422 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26423 //Roo.log('-- removed');
26424 while (node.childNodes.length) {
26425 var cn = node.childNodes[0];
26426 node.removeChild(cn);
26427 node.parentNode.insertBefore(cn, node);
26428 // move node to parent - and clean it..
26429 this.cleanWord(cn);
26431 node.parentNode.removeChild(node);
26432 /// no need to iterate chidlren = it's got none..
26433 //this.iterateChildren(node, this.cleanWord);
26437 if (node.className.length) {
26439 var cn = node.className.split(/\W+/);
26441 Roo.each(cn, function(cls) {
26442 if (cls.match(/Mso[a-zA-Z]+/)) {
26447 node.className = cna.length ? cna.join(' ') : '';
26449 node.removeAttribute("class");
26453 if (node.hasAttribute("lang")) {
26454 node.removeAttribute("lang");
26457 if (node.hasAttribute("style")) {
26459 var styles = node.getAttribute("style").split(";");
26461 Roo.each(styles, function(s) {
26462 if (!s.match(/:/)) {
26465 var kv = s.split(":");
26466 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26469 // what ever is left... we allow.
26472 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26473 if (!nstyle.length) {
26474 node.removeAttribute('style');
26477 this.iterateChildren(node, this.cleanWord);
26483 * iterateChildren of a Node, calling fn each time, using this as the scole..
26484 * @param {DomNode} node node to iterate children of.
26485 * @param {Function} fn method of this class to call on each item.
26487 iterateChildren : function(node, fn)
26489 if (!node.childNodes.length) {
26492 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26493 fn.call(this, node.childNodes[i])
26499 * cleanTableWidths.
26501 * Quite often pasting from word etc.. results in tables with column and widths.
26502 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26505 cleanTableWidths : function(node)
26510 this.cleanTableWidths(this.doc.body);
26515 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26518 Roo.log(node.tagName);
26519 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26520 this.iterateChildren(node, this.cleanTableWidths);
26523 if (node.hasAttribute('width')) {
26524 node.removeAttribute('width');
26528 if (node.hasAttribute("style")) {
26531 var styles = node.getAttribute("style").split(";");
26533 Roo.each(styles, function(s) {
26534 if (!s.match(/:/)) {
26537 var kv = s.split(":");
26538 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26541 // what ever is left... we allow.
26544 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26545 if (!nstyle.length) {
26546 node.removeAttribute('style');
26550 this.iterateChildren(node, this.cleanTableWidths);
26558 domToHTML : function(currentElement, depth, nopadtext) {
26560 depth = depth || 0;
26561 nopadtext = nopadtext || false;
26563 if (!currentElement) {
26564 return this.domToHTML(this.doc.body);
26567 //Roo.log(currentElement);
26569 var allText = false;
26570 var nodeName = currentElement.nodeName;
26571 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26573 if (nodeName == '#text') {
26575 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26580 if (nodeName != 'BODY') {
26583 // Prints the node tagName, such as <A>, <IMG>, etc
26586 for(i = 0; i < currentElement.attributes.length;i++) {
26588 var aname = currentElement.attributes.item(i).name;
26589 if (!currentElement.attributes.item(i).value.length) {
26592 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26595 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26604 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26607 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26612 // Traverse the tree
26614 var currentElementChild = currentElement.childNodes.item(i);
26615 var allText = true;
26616 var innerHTML = '';
26618 while (currentElementChild) {
26619 // Formatting code (indent the tree so it looks nice on the screen)
26620 var nopad = nopadtext;
26621 if (lastnode == 'SPAN') {
26625 if (currentElementChild.nodeName == '#text') {
26626 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26627 toadd = nopadtext ? toadd : toadd.trim();
26628 if (!nopad && toadd.length > 80) {
26629 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26631 innerHTML += toadd;
26634 currentElementChild = currentElement.childNodes.item(i);
26640 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26642 // Recursively traverse the tree structure of the child node
26643 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26644 lastnode = currentElementChild.nodeName;
26646 currentElementChild=currentElement.childNodes.item(i);
26652 // The remaining code is mostly for formatting the tree
26653 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26658 ret+= "</"+tagName+">";
26664 applyBlacklists : function()
26666 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26667 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26671 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26672 if (b.indexOf(tag) > -1) {
26675 this.white.push(tag);
26679 Roo.each(w, function(tag) {
26680 if (b.indexOf(tag) > -1) {
26683 if (this.white.indexOf(tag) > -1) {
26686 this.white.push(tag);
26691 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26692 if (w.indexOf(tag) > -1) {
26695 this.black.push(tag);
26699 Roo.each(b, function(tag) {
26700 if (w.indexOf(tag) > -1) {
26703 if (this.black.indexOf(tag) > -1) {
26706 this.black.push(tag);
26711 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26712 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26716 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26717 if (b.indexOf(tag) > -1) {
26720 this.cwhite.push(tag);
26724 Roo.each(w, function(tag) {
26725 if (b.indexOf(tag) > -1) {
26728 if (this.cwhite.indexOf(tag) > -1) {
26731 this.cwhite.push(tag);
26736 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26737 if (w.indexOf(tag) > -1) {
26740 this.cblack.push(tag);
26744 Roo.each(b, function(tag) {
26745 if (w.indexOf(tag) > -1) {
26748 if (this.cblack.indexOf(tag) > -1) {
26751 this.cblack.push(tag);
26756 setStylesheets : function(stylesheets)
26758 if(typeof(stylesheets) == 'string'){
26759 Roo.get(this.iframe.contentDocument.head).createChild({
26761 rel : 'stylesheet',
26770 Roo.each(stylesheets, function(s) {
26775 Roo.get(_this.iframe.contentDocument.head).createChild({
26777 rel : 'stylesheet',
26786 removeStylesheets : function()
26790 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26795 setStyle : function(style)
26797 Roo.get(this.iframe.contentDocument.head).createChild({
26806 // hide stuff that is not compatible
26820 * @event specialkey
26824 * @cfg {String} fieldClass @hide
26827 * @cfg {String} focusClass @hide
26830 * @cfg {String} autoCreate @hide
26833 * @cfg {String} inputType @hide
26836 * @cfg {String} invalidClass @hide
26839 * @cfg {String} invalidText @hide
26842 * @cfg {String} msgFx @hide
26845 * @cfg {String} validateOnBlur @hide
26849 Roo.HtmlEditorCore.white = [
26850 'area', 'br', 'img', 'input', 'hr', 'wbr',
26852 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26853 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26854 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26855 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26856 'table', 'ul', 'xmp',
26858 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26861 'dir', 'menu', 'ol', 'ul', 'dl',
26867 Roo.HtmlEditorCore.black = [
26868 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26870 'base', 'basefont', 'bgsound', 'blink', 'body',
26871 'frame', 'frameset', 'head', 'html', 'ilayer',
26872 'iframe', 'layer', 'link', 'meta', 'object',
26873 'script', 'style' ,'title', 'xml' // clean later..
26875 Roo.HtmlEditorCore.clean = [
26876 'script', 'style', 'title', 'xml'
26878 Roo.HtmlEditorCore.remove = [
26883 Roo.HtmlEditorCore.ablack = [
26887 Roo.HtmlEditorCore.aclean = [
26888 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26892 Roo.HtmlEditorCore.pwhite= [
26893 'http', 'https', 'mailto'
26896 // white listed style attributes.
26897 Roo.HtmlEditorCore.cwhite= [
26898 // 'text-align', /// default is to allow most things..
26904 // black listed style attributes.
26905 Roo.HtmlEditorCore.cblack= [
26906 // 'font-size' -- this can be set by the project
26910 Roo.HtmlEditorCore.swapCodes =[
26911 [ 8211, "–" ],
26912 [ 8212, "—" ],
26929 * @class Roo.bootstrap.HtmlEditor
26930 * @extends Roo.bootstrap.TextArea
26931 * Bootstrap HtmlEditor class
26934 * Create a new HtmlEditor
26935 * @param {Object} config The config object
26938 Roo.bootstrap.HtmlEditor = function(config){
26939 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26940 if (!this.toolbars) {
26941 this.toolbars = [];
26944 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26947 * @event initialize
26948 * Fires when the editor is fully initialized (including the iframe)
26949 * @param {HtmlEditor} this
26954 * Fires when the editor is first receives the focus. Any insertion must wait
26955 * until after this event.
26956 * @param {HtmlEditor} this
26960 * @event beforesync
26961 * Fires before the textarea is updated with content from the editor iframe. Return false
26962 * to cancel the sync.
26963 * @param {HtmlEditor} this
26964 * @param {String} html
26968 * @event beforepush
26969 * Fires before the iframe editor is updated with content from the textarea. Return false
26970 * to cancel the push.
26971 * @param {HtmlEditor} this
26972 * @param {String} html
26977 * Fires when the textarea is updated with content from the editor iframe.
26978 * @param {HtmlEditor} this
26979 * @param {String} html
26984 * Fires when the iframe editor is updated with content from the textarea.
26985 * @param {HtmlEditor} this
26986 * @param {String} html
26990 * @event editmodechange
26991 * Fires when the editor switches edit modes
26992 * @param {HtmlEditor} this
26993 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26995 editmodechange: true,
26997 * @event editorevent
26998 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26999 * @param {HtmlEditor} this
27003 * @event firstfocus
27004 * Fires when on first focus - needed by toolbars..
27005 * @param {HtmlEditor} this
27010 * Auto save the htmlEditor value as a file into Events
27011 * @param {HtmlEditor} this
27015 * @event savedpreview
27016 * preview the saved version of htmlEditor
27017 * @param {HtmlEditor} this
27024 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27028 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27033 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27038 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27043 * @cfg {Number} height (in pixels)
27047 * @cfg {Number} width (in pixels)
27052 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27055 stylesheets: false,
27060 // private properties
27061 validationEvent : false,
27063 initialized : false,
27066 onFocus : Roo.emptyFn,
27068 hideMode:'offsets',
27070 tbContainer : false,
27074 toolbarContainer :function() {
27075 return this.wrap.select('.x-html-editor-tb',true).first();
27079 * Protected method that will not generally be called directly. It
27080 * is called when the editor creates its toolbar. Override this method if you need to
27081 * add custom toolbar buttons.
27082 * @param {HtmlEditor} editor
27084 createToolbar : function(){
27085 Roo.log('renewing');
27086 Roo.log("create toolbars");
27088 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27089 this.toolbars[0].render(this.toolbarContainer());
27093 // if (!editor.toolbars || !editor.toolbars.length) {
27094 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27097 // for (var i =0 ; i < editor.toolbars.length;i++) {
27098 // editor.toolbars[i] = Roo.factory(
27099 // typeof(editor.toolbars[i]) == 'string' ?
27100 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27101 // Roo.bootstrap.HtmlEditor);
27102 // editor.toolbars[i].init(editor);
27108 onRender : function(ct, position)
27110 // Roo.log("Call onRender: " + this.xtype);
27112 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27114 this.wrap = this.inputEl().wrap({
27115 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27118 this.editorcore.onRender(ct, position);
27120 if (this.resizable) {
27121 this.resizeEl = new Roo.Resizable(this.wrap, {
27125 minHeight : this.height,
27126 height: this.height,
27127 handles : this.resizable,
27130 resize : function(r, w, h) {
27131 _t.onResize(w,h); // -something
27137 this.createToolbar(this);
27140 if(!this.width && this.resizable){
27141 this.setSize(this.wrap.getSize());
27143 if (this.resizeEl) {
27144 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27145 // should trigger onReize..
27151 onResize : function(w, h)
27153 Roo.log('resize: ' +w + ',' + h );
27154 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27158 if(this.inputEl() ){
27159 if(typeof w == 'number'){
27160 var aw = w - this.wrap.getFrameWidth('lr');
27161 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27164 if(typeof h == 'number'){
27165 var tbh = -11; // fixme it needs to tool bar size!
27166 for (var i =0; i < this.toolbars.length;i++) {
27167 // fixme - ask toolbars for heights?
27168 tbh += this.toolbars[i].el.getHeight();
27169 //if (this.toolbars[i].footer) {
27170 // tbh += this.toolbars[i].footer.el.getHeight();
27178 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27179 ah -= 5; // knock a few pixes off for look..
27180 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27184 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27185 this.editorcore.onResize(ew,eh);
27190 * Toggles the editor between standard and source edit mode.
27191 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27193 toggleSourceEdit : function(sourceEditMode)
27195 this.editorcore.toggleSourceEdit(sourceEditMode);
27197 if(this.editorcore.sourceEditMode){
27198 Roo.log('editor - showing textarea');
27201 // Roo.log(this.syncValue());
27203 this.inputEl().removeClass(['hide', 'x-hidden']);
27204 this.inputEl().dom.removeAttribute('tabIndex');
27205 this.inputEl().focus();
27207 Roo.log('editor - hiding textarea');
27209 // Roo.log(this.pushValue());
27212 this.inputEl().addClass(['hide', 'x-hidden']);
27213 this.inputEl().dom.setAttribute('tabIndex', -1);
27214 //this.deferFocus();
27217 if(this.resizable){
27218 this.setSize(this.wrap.getSize());
27221 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27224 // private (for BoxComponent)
27225 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27227 // private (for BoxComponent)
27228 getResizeEl : function(){
27232 // private (for BoxComponent)
27233 getPositionEl : function(){
27238 initEvents : function(){
27239 this.originalValue = this.getValue();
27243 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27246 // markInvalid : Roo.emptyFn,
27248 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27251 // clearInvalid : Roo.emptyFn,
27253 setValue : function(v){
27254 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27255 this.editorcore.pushValue();
27260 deferFocus : function(){
27261 this.focus.defer(10, this);
27265 focus : function(){
27266 this.editorcore.focus();
27272 onDestroy : function(){
27278 for (var i =0; i < this.toolbars.length;i++) {
27279 // fixme - ask toolbars for heights?
27280 this.toolbars[i].onDestroy();
27283 this.wrap.dom.innerHTML = '';
27284 this.wrap.remove();
27289 onFirstFocus : function(){
27290 //Roo.log("onFirstFocus");
27291 this.editorcore.onFirstFocus();
27292 for (var i =0; i < this.toolbars.length;i++) {
27293 this.toolbars[i].onFirstFocus();
27299 syncValue : function()
27301 this.editorcore.syncValue();
27304 pushValue : function()
27306 this.editorcore.pushValue();
27310 // hide stuff that is not compatible
27324 * @event specialkey
27328 * @cfg {String} fieldClass @hide
27331 * @cfg {String} focusClass @hide
27334 * @cfg {String} autoCreate @hide
27337 * @cfg {String} inputType @hide
27341 * @cfg {String} invalidText @hide
27344 * @cfg {String} msgFx @hide
27347 * @cfg {String} validateOnBlur @hide
27356 Roo.namespace('Roo.bootstrap.htmleditor');
27358 * @class Roo.bootstrap.HtmlEditorToolbar1
27364 new Roo.bootstrap.HtmlEditor({
27367 new Roo.bootstrap.HtmlEditorToolbar1({
27368 disable : { fonts: 1 , format: 1, ..., ... , ...],
27374 * @cfg {Object} disable List of elements to disable..
27375 * @cfg {Array} btns List of additional buttons.
27379 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27382 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27385 Roo.apply(this, config);
27387 // default disabled, based on 'good practice'..
27388 this.disable = this.disable || {};
27389 Roo.applyIf(this.disable, {
27392 specialElements : true
27394 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27396 this.editor = config.editor;
27397 this.editorcore = config.editor.editorcore;
27399 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27401 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27402 // dont call parent... till later.
27404 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27409 editorcore : false,
27414 "h1","h2","h3","h4","h5","h6",
27416 "abbr", "acronym", "address", "cite", "samp", "var",
27420 onRender : function(ct, position)
27422 // Roo.log("Call onRender: " + this.xtype);
27424 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27426 this.el.dom.style.marginBottom = '0';
27428 var editorcore = this.editorcore;
27429 var editor= this.editor;
27432 var btn = function(id,cmd , toggle, handler, html){
27434 var event = toggle ? 'toggle' : 'click';
27439 xns: Roo.bootstrap,
27443 enableToggle:toggle !== false,
27445 pressed : toggle ? false : null,
27448 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27449 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27455 // var cb_box = function...
27460 xns: Roo.bootstrap,
27465 xns: Roo.bootstrap,
27469 Roo.each(this.formats, function(f) {
27470 style.menu.items.push({
27472 xns: Roo.bootstrap,
27473 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27478 editorcore.insertTag(this.tagname);
27485 children.push(style);
27487 btn('bold',false,true);
27488 btn('italic',false,true);
27489 btn('align-left', 'justifyleft',true);
27490 btn('align-center', 'justifycenter',true);
27491 btn('align-right' , 'justifyright',true);
27492 btn('link', false, false, function(btn) {
27493 //Roo.log("create link?");
27494 var url = prompt(this.createLinkText, this.defaultLinkValue);
27495 if(url && url != 'http:/'+'/'){
27496 this.editorcore.relayCmd('createlink', url);
27499 btn('list','insertunorderedlist',true);
27500 btn('pencil', false,true, function(btn){
27502 this.toggleSourceEdit(btn.pressed);
27505 if (this.editor.btns.length > 0) {
27506 for (var i = 0; i<this.editor.btns.length; i++) {
27507 children.push(this.editor.btns[i]);
27515 xns: Roo.bootstrap,
27520 xns: Roo.bootstrap,
27525 cog.menu.items.push({
27527 xns: Roo.bootstrap,
27528 html : Clean styles,
27533 editorcore.insertTag(this.tagname);
27542 this.xtype = 'NavSimplebar';
27544 for(var i=0;i< children.length;i++) {
27546 this.buttons.add(this.addxtypeChild(children[i]));
27550 editor.on('editorevent', this.updateToolbar, this);
27552 onBtnClick : function(id)
27554 this.editorcore.relayCmd(id);
27555 this.editorcore.focus();
27559 * Protected method that will not generally be called directly. It triggers
27560 * a toolbar update by reading the markup state of the current selection in the editor.
27562 updateToolbar: function(){
27564 if(!this.editorcore.activated){
27565 this.editor.onFirstFocus(); // is this neeed?
27569 var btns = this.buttons;
27570 var doc = this.editorcore.doc;
27571 btns.get('bold').setActive(doc.queryCommandState('bold'));
27572 btns.get('italic').setActive(doc.queryCommandState('italic'));
27573 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27575 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27576 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27577 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27579 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27580 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27583 var ans = this.editorcore.getAllAncestors();
27584 if (this.formatCombo) {
27587 var store = this.formatCombo.store;
27588 this.formatCombo.setValue("");
27589 for (var i =0; i < ans.length;i++) {
27590 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27592 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27600 // hides menus... - so this cant be on a menu...
27601 Roo.bootstrap.MenuMgr.hideAll();
27603 Roo.bootstrap.MenuMgr.hideAll();
27604 //this.editorsyncValue();
27606 onFirstFocus: function() {
27607 this.buttons.each(function(item){
27611 toggleSourceEdit : function(sourceEditMode){
27614 if(sourceEditMode){
27615 Roo.log("disabling buttons");
27616 this.buttons.each( function(item){
27617 if(item.cmd != 'pencil'){
27623 Roo.log("enabling buttons");
27624 if(this.editorcore.initialized){
27625 this.buttons.each( function(item){
27631 Roo.log("calling toggole on editor");
27632 // tell the editor that it's been pressed..
27633 this.editor.toggleSourceEdit(sourceEditMode);
27647 * @class Roo.bootstrap.Markdown
27648 * @extends Roo.bootstrap.TextArea
27649 * Bootstrap Showdown editable area
27650 * @cfg {string} content
27653 * Create a new Showdown
27656 Roo.bootstrap.Markdown = function(config){
27657 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27661 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27665 initEvents : function()
27668 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27669 this.markdownEl = this.el.createChild({
27670 cls : 'roo-markdown-area'
27672 this.inputEl().addClass('d-none');
27673 if (this.getValue() == '') {
27674 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27677 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27679 this.markdownEl.on('click', this.toggleTextEdit, this);
27680 this.on('blur', this.toggleTextEdit, this);
27681 this.on('specialkey', this.resizeTextArea, this);
27684 toggleTextEdit : function()
27686 var sh = this.markdownEl.getHeight();
27687 this.inputEl().addClass('d-none');
27688 this.markdownEl.addClass('d-none');
27689 if (!this.editing) {
27691 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27692 this.inputEl().removeClass('d-none');
27693 this.inputEl().focus();
27694 this.editing = true;
27697 // show showdown...
27698 this.updateMarkdown();
27699 this.markdownEl.removeClass('d-none');
27700 this.editing = false;
27703 updateMarkdown : function()
27705 if (this.getValue() == '') {
27706 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27710 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27713 resizeTextArea: function () {
27716 Roo.log([sh, this.getValue().split("\n").length * 30]);
27717 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27719 setValue : function(val)
27721 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27722 if (!this.editing) {
27723 this.updateMarkdown();
27729 if (!this.editing) {
27730 this.toggleTextEdit();
27738 * Ext JS Library 1.1.1
27739 * Copyright(c) 2006-2007, Ext JS, LLC.
27741 * Originally Released Under LGPL - original licence link has changed is not relivant.
27744 * <script type="text/javascript">
27748 * @class Roo.bootstrap.PagingToolbar
27749 * @extends Roo.bootstrap.NavSimplebar
27750 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27752 * Create a new PagingToolbar
27753 * @param {Object} config The config object
27754 * @param {Roo.data.Store} store
27756 Roo.bootstrap.PagingToolbar = function(config)
27758 // old args format still supported... - xtype is prefered..
27759 // created from xtype...
27761 this.ds = config.dataSource;
27763 if (config.store && !this.ds) {
27764 this.store= Roo.factory(config.store, Roo.data);
27765 this.ds = this.store;
27766 this.ds.xmodule = this.xmodule || false;
27769 this.toolbarItems = [];
27770 if (config.items) {
27771 this.toolbarItems = config.items;
27774 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27779 this.bind(this.ds);
27782 if (Roo.bootstrap.version == 4) {
27783 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27785 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27790 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27792 * @cfg {Roo.data.Store} dataSource
27793 * The underlying data store providing the paged data
27796 * @cfg {String/HTMLElement/Element} container
27797 * container The id or element that will contain the toolbar
27800 * @cfg {Boolean} displayInfo
27801 * True to display the displayMsg (defaults to false)
27804 * @cfg {Number} pageSize
27805 * The number of records to display per page (defaults to 20)
27809 * @cfg {String} displayMsg
27810 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27812 displayMsg : 'Displaying {0} - {1} of {2}',
27814 * @cfg {String} emptyMsg
27815 * The message to display when no records are found (defaults to "No data to display")
27817 emptyMsg : 'No data to display',
27819 * Customizable piece of the default paging text (defaults to "Page")
27822 beforePageText : "Page",
27824 * Customizable piece of the default paging text (defaults to "of %0")
27827 afterPageText : "of {0}",
27829 * Customizable piece of the default paging text (defaults to "First Page")
27832 firstText : "First Page",
27834 * Customizable piece of the default paging text (defaults to "Previous Page")
27837 prevText : "Previous Page",
27839 * Customizable piece of the default paging text (defaults to "Next Page")
27842 nextText : "Next Page",
27844 * Customizable piece of the default paging text (defaults to "Last Page")
27847 lastText : "Last Page",
27849 * Customizable piece of the default paging text (defaults to "Refresh")
27852 refreshText : "Refresh",
27856 onRender : function(ct, position)
27858 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27859 this.navgroup.parentId = this.id;
27860 this.navgroup.onRender(this.el, null);
27861 // add the buttons to the navgroup
27863 if(this.displayInfo){
27864 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27865 this.displayEl = this.el.select('.x-paging-info', true).first();
27866 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27867 // this.displayEl = navel.el.select('span',true).first();
27873 Roo.each(_this.buttons, function(e){ // this might need to use render????
27874 Roo.factory(e).render(_this.el);
27878 Roo.each(_this.toolbarItems, function(e) {
27879 _this.navgroup.addItem(e);
27883 this.first = this.navgroup.addItem({
27884 tooltip: this.firstText,
27885 cls: "prev btn-outline-secondary",
27886 html : ' <i class="fa fa-step-backward"></i>',
27888 preventDefault: true,
27889 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27892 this.prev = this.navgroup.addItem({
27893 tooltip: this.prevText,
27894 cls: "prev btn-outline-secondary",
27895 html : ' <i class="fa fa-backward"></i>',
27897 preventDefault: true,
27898 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27900 //this.addSeparator();
27903 var field = this.navgroup.addItem( {
27905 cls : 'x-paging-position btn-outline-secondary',
27907 html : this.beforePageText +
27908 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27909 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27912 this.field = field.el.select('input', true).first();
27913 this.field.on("keydown", this.onPagingKeydown, this);
27914 this.field.on("focus", function(){this.dom.select();});
27917 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27918 //this.field.setHeight(18);
27919 //this.addSeparator();
27920 this.next = this.navgroup.addItem({
27921 tooltip: this.nextText,
27922 cls: "next btn-outline-secondary",
27923 html : ' <i class="fa fa-forward"></i>',
27925 preventDefault: true,
27926 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27928 this.last = this.navgroup.addItem({
27929 tooltip: this.lastText,
27930 html : ' <i class="fa fa-step-forward"></i>',
27931 cls: "next btn-outline-secondary",
27933 preventDefault: true,
27934 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27936 //this.addSeparator();
27937 this.loading = this.navgroup.addItem({
27938 tooltip: this.refreshText,
27939 cls: "btn-outline-secondary",
27940 html : ' <i class="fa fa-refresh"></i>',
27941 preventDefault: true,
27942 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27948 updateInfo : function(){
27949 if(this.displayEl){
27950 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27951 var msg = count == 0 ?
27955 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27957 this.displayEl.update(msg);
27962 onLoad : function(ds, r, o)
27964 this.cursor = o.params && o.params.start ? o.params.start : 0;
27966 var d = this.getPageData(),
27971 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27972 this.field.dom.value = ap;
27973 this.first.setDisabled(ap == 1);
27974 this.prev.setDisabled(ap == 1);
27975 this.next.setDisabled(ap == ps);
27976 this.last.setDisabled(ap == ps);
27977 this.loading.enable();
27982 getPageData : function(){
27983 var total = this.ds.getTotalCount();
27986 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27987 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27992 onLoadError : function(){
27993 this.loading.enable();
27997 onPagingKeydown : function(e){
27998 var k = e.getKey();
27999 var d = this.getPageData();
28001 var v = this.field.dom.value, pageNum;
28002 if(!v || isNaN(pageNum = parseInt(v, 10))){
28003 this.field.dom.value = d.activePage;
28006 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28007 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28010 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))
28012 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28013 this.field.dom.value = pageNum;
28014 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28017 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28019 var v = this.field.dom.value, pageNum;
28020 var increment = (e.shiftKey) ? 10 : 1;
28021 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28024 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28025 this.field.dom.value = d.activePage;
28028 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28030 this.field.dom.value = parseInt(v, 10) + increment;
28031 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28032 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28039 beforeLoad : function(){
28041 this.loading.disable();
28046 onClick : function(which){
28055 ds.load({params:{start: 0, limit: this.pageSize}});
28058 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28061 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28064 var total = ds.getTotalCount();
28065 var extra = total % this.pageSize;
28066 var lastStart = extra ? (total - extra) : total-this.pageSize;
28067 ds.load({params:{start: lastStart, limit: this.pageSize}});
28070 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28076 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28077 * @param {Roo.data.Store} store The data store to unbind
28079 unbind : function(ds){
28080 ds.un("beforeload", this.beforeLoad, this);
28081 ds.un("load", this.onLoad, this);
28082 ds.un("loadexception", this.onLoadError, this);
28083 ds.un("remove", this.updateInfo, this);
28084 ds.un("add", this.updateInfo, this);
28085 this.ds = undefined;
28089 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28090 * @param {Roo.data.Store} store The data store to bind
28092 bind : function(ds){
28093 ds.on("beforeload", this.beforeLoad, this);
28094 ds.on("load", this.onLoad, this);
28095 ds.on("loadexception", this.onLoadError, this);
28096 ds.on("remove", this.updateInfo, this);
28097 ds.on("add", this.updateInfo, this);
28108 * @class Roo.bootstrap.MessageBar
28109 * @extends Roo.bootstrap.Component
28110 * Bootstrap MessageBar class
28111 * @cfg {String} html contents of the MessageBar
28112 * @cfg {String} weight (info | success | warning | danger) default info
28113 * @cfg {String} beforeClass insert the bar before the given class
28114 * @cfg {Boolean} closable (true | false) default false
28115 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28118 * Create a new Element
28119 * @param {Object} config The config object
28122 Roo.bootstrap.MessageBar = function(config){
28123 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28126 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28132 beforeClass: 'bootstrap-sticky-wrap',
28134 getAutoCreate : function(){
28138 cls: 'alert alert-dismissable alert-' + this.weight,
28143 html: this.html || ''
28149 cfg.cls += ' alert-messages-fixed';
28163 onRender : function(ct, position)
28165 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28168 var cfg = Roo.apply({}, this.getAutoCreate());
28172 cfg.cls += ' ' + this.cls;
28175 cfg.style = this.style;
28177 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28179 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28182 this.el.select('>button.close').on('click', this.hide, this);
28188 if (!this.rendered) {
28194 this.fireEvent('show', this);
28200 if (!this.rendered) {
28206 this.fireEvent('hide', this);
28209 update : function()
28211 // var e = this.el.dom.firstChild;
28213 // if(this.closable){
28214 // e = e.nextSibling;
28217 // e.data = this.html || '';
28219 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28235 * @class Roo.bootstrap.Graph
28236 * @extends Roo.bootstrap.Component
28237 * Bootstrap Graph class
28241 @cfg {String} graphtype bar | vbar | pie
28242 @cfg {number} g_x coodinator | centre x (pie)
28243 @cfg {number} g_y coodinator | centre y (pie)
28244 @cfg {number} g_r radius (pie)
28245 @cfg {number} g_height height of the chart (respected by all elements in the set)
28246 @cfg {number} g_width width of the chart (respected by all elements in the set)
28247 @cfg {Object} title The title of the chart
28250 -opts (object) options for the chart
28252 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28253 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28255 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.
28256 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28258 o stretch (boolean)
28260 -opts (object) options for the pie
28263 o startAngle (number)
28264 o endAngle (number)
28268 * Create a new Input
28269 * @param {Object} config The config object
28272 Roo.bootstrap.Graph = function(config){
28273 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28279 * The img click event for the img.
28280 * @param {Roo.EventObject} e
28286 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28297 //g_colors: this.colors,
28304 getAutoCreate : function(){
28315 onRender : function(ct,position){
28318 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28320 if (typeof(Raphael) == 'undefined') {
28321 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28325 this.raphael = Raphael(this.el.dom);
28327 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28328 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28329 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28330 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28332 r.text(160, 10, "Single Series Chart").attr(txtattr);
28333 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28334 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28335 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28337 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28338 r.barchart(330, 10, 300, 220, data1);
28339 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28340 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28343 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28344 // r.barchart(30, 30, 560, 250, xdata, {
28345 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28346 // axis : "0 0 1 1",
28347 // axisxlabels : xdata
28348 // //yvalues : cols,
28351 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28353 // this.load(null,xdata,{
28354 // axis : "0 0 1 1",
28355 // axisxlabels : xdata
28360 load : function(graphtype,xdata,opts)
28362 this.raphael.clear();
28364 graphtype = this.graphtype;
28369 var r = this.raphael,
28370 fin = function () {
28371 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28373 fout = function () {
28374 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28376 pfin = function() {
28377 this.sector.stop();
28378 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28381 this.label[0].stop();
28382 this.label[0].attr({ r: 7.5 });
28383 this.label[1].attr({ "font-weight": 800 });
28386 pfout = function() {
28387 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28390 this.label[0].animate({ r: 5 }, 500, "bounce");
28391 this.label[1].attr({ "font-weight": 400 });
28397 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28400 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28403 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28404 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28406 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28413 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28418 setTitle: function(o)
28423 initEvents: function() {
28426 this.el.on('click', this.onClick, this);
28430 onClick : function(e)
28432 Roo.log('img onclick');
28433 this.fireEvent('click', this, e);
28445 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28448 * @class Roo.bootstrap.dash.NumberBox
28449 * @extends Roo.bootstrap.Component
28450 * Bootstrap NumberBox class
28451 * @cfg {String} headline Box headline
28452 * @cfg {String} content Box content
28453 * @cfg {String} icon Box icon
28454 * @cfg {String} footer Footer text
28455 * @cfg {String} fhref Footer href
28458 * Create a new NumberBox
28459 * @param {Object} config The config object
28463 Roo.bootstrap.dash.NumberBox = function(config){
28464 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28468 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28477 getAutoCreate : function(){
28481 cls : 'small-box ',
28489 cls : 'roo-headline',
28490 html : this.headline
28494 cls : 'roo-content',
28495 html : this.content
28509 cls : 'ion ' + this.icon
28518 cls : 'small-box-footer',
28519 href : this.fhref || '#',
28523 cfg.cn.push(footer);
28530 onRender : function(ct,position){
28531 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28538 setHeadline: function (value)
28540 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28543 setFooter: function (value, href)
28545 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28548 this.el.select('a.small-box-footer',true).first().attr('href', href);
28553 setContent: function (value)
28555 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28558 initEvents: function()
28572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28575 * @class Roo.bootstrap.dash.TabBox
28576 * @extends Roo.bootstrap.Component
28577 * Bootstrap TabBox class
28578 * @cfg {String} title Title of the TabBox
28579 * @cfg {String} icon Icon of the TabBox
28580 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28581 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28584 * Create a new TabBox
28585 * @param {Object} config The config object
28589 Roo.bootstrap.dash.TabBox = function(config){
28590 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28595 * When a pane is added
28596 * @param {Roo.bootstrap.dash.TabPane} pane
28600 * @event activatepane
28601 * When a pane is activated
28602 * @param {Roo.bootstrap.dash.TabPane} pane
28604 "activatepane" : true
28612 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28617 tabScrollable : false,
28619 getChildContainer : function()
28621 return this.el.select('.tab-content', true).first();
28624 getAutoCreate : function(){
28628 cls: 'pull-left header',
28636 cls: 'fa ' + this.icon
28642 cls: 'nav nav-tabs pull-right',
28648 if(this.tabScrollable){
28655 cls: 'nav nav-tabs pull-right',
28666 cls: 'nav-tabs-custom',
28671 cls: 'tab-content no-padding',
28679 initEvents : function()
28681 //Roo.log('add add pane handler');
28682 this.on('addpane', this.onAddPane, this);
28685 * Updates the box title
28686 * @param {String} html to set the title to.
28688 setTitle : function(value)
28690 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28692 onAddPane : function(pane)
28694 this.panes.push(pane);
28695 //Roo.log('addpane');
28697 // tabs are rendere left to right..
28698 if(!this.showtabs){
28702 var ctr = this.el.select('.nav-tabs', true).first();
28705 var existing = ctr.select('.nav-tab',true);
28706 var qty = existing.getCount();;
28709 var tab = ctr.createChild({
28711 cls : 'nav-tab' + (qty ? '' : ' active'),
28719 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28722 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28724 pane.el.addClass('active');
28729 onTabClick : function(ev,un,ob,pane)
28731 //Roo.log('tab - prev default');
28732 ev.preventDefault();
28735 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28736 pane.tab.addClass('active');
28737 //Roo.log(pane.title);
28738 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28739 // technically we should have a deactivate event.. but maybe add later.
28740 // and it should not de-activate the selected tab...
28741 this.fireEvent('activatepane', pane);
28742 pane.el.addClass('active');
28743 pane.fireEvent('activate');
28748 getActivePane : function()
28751 Roo.each(this.panes, function(p) {
28752 if(p.el.hasClass('active')){
28773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28775 * @class Roo.bootstrap.TabPane
28776 * @extends Roo.bootstrap.Component
28777 * Bootstrap TabPane class
28778 * @cfg {Boolean} active (false | true) Default false
28779 * @cfg {String} title title of panel
28783 * Create a new TabPane
28784 * @param {Object} config The config object
28787 Roo.bootstrap.dash.TabPane = function(config){
28788 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28794 * When a pane is activated
28795 * @param {Roo.bootstrap.dash.TabPane} pane
28802 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28807 // the tabBox that this is attached to.
28810 getAutoCreate : function()
28818 cfg.cls += ' active';
28823 initEvents : function()
28825 //Roo.log('trigger add pane handler');
28826 this.parent().fireEvent('addpane', this)
28830 * Updates the tab title
28831 * @param {String} html to set the title to.
28833 setTitle: function(str)
28839 this.tab.select('a', true).first().dom.innerHTML = str;
28856 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28859 * @class Roo.bootstrap.menu.Menu
28860 * @extends Roo.bootstrap.Component
28861 * Bootstrap Menu class - container for Menu
28862 * @cfg {String} html Text of the menu
28863 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28864 * @cfg {String} icon Font awesome icon
28865 * @cfg {String} pos Menu align to (top | bottom) default bottom
28869 * Create a new Menu
28870 * @param {Object} config The config object
28874 Roo.bootstrap.menu.Menu = function(config){
28875 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28879 * @event beforeshow
28880 * Fires before this menu is displayed
28881 * @param {Roo.bootstrap.menu.Menu} this
28885 * @event beforehide
28886 * Fires before this menu is hidden
28887 * @param {Roo.bootstrap.menu.Menu} this
28892 * Fires after this menu is displayed
28893 * @param {Roo.bootstrap.menu.Menu} this
28898 * Fires after this menu is hidden
28899 * @param {Roo.bootstrap.menu.Menu} this
28904 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28905 * @param {Roo.bootstrap.menu.Menu} this
28906 * @param {Roo.EventObject} e
28913 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28917 weight : 'default',
28922 getChildContainer : function() {
28923 if(this.isSubMenu){
28927 return this.el.select('ul.dropdown-menu', true).first();
28930 getAutoCreate : function()
28935 cls : 'roo-menu-text',
28943 cls : 'fa ' + this.icon
28954 cls : 'dropdown-button btn btn-' + this.weight,
28959 cls : 'dropdown-toggle btn btn-' + this.weight,
28969 cls : 'dropdown-menu'
28975 if(this.pos == 'top'){
28976 cfg.cls += ' dropup';
28979 if(this.isSubMenu){
28982 cls : 'dropdown-menu'
28989 onRender : function(ct, position)
28991 this.isSubMenu = ct.hasClass('dropdown-submenu');
28993 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28996 initEvents : function()
28998 if(this.isSubMenu){
29002 this.hidden = true;
29004 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29005 this.triggerEl.on('click', this.onTriggerPress, this);
29007 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29008 this.buttonEl.on('click', this.onClick, this);
29014 if(this.isSubMenu){
29018 return this.el.select('ul.dropdown-menu', true).first();
29021 onClick : function(e)
29023 this.fireEvent("click", this, e);
29026 onTriggerPress : function(e)
29028 if (this.isVisible()) {
29035 isVisible : function(){
29036 return !this.hidden;
29041 this.fireEvent("beforeshow", this);
29043 this.hidden = false;
29044 this.el.addClass('open');
29046 Roo.get(document).on("mouseup", this.onMouseUp, this);
29048 this.fireEvent("show", this);
29055 this.fireEvent("beforehide", this);
29057 this.hidden = true;
29058 this.el.removeClass('open');
29060 Roo.get(document).un("mouseup", this.onMouseUp);
29062 this.fireEvent("hide", this);
29065 onMouseUp : function()
29079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29082 * @class Roo.bootstrap.menu.Item
29083 * @extends Roo.bootstrap.Component
29084 * Bootstrap MenuItem class
29085 * @cfg {Boolean} submenu (true | false) default false
29086 * @cfg {String} html text of the item
29087 * @cfg {String} href the link
29088 * @cfg {Boolean} disable (true | false) default false
29089 * @cfg {Boolean} preventDefault (true | false) default true
29090 * @cfg {String} icon Font awesome icon
29091 * @cfg {String} pos Submenu align to (left | right) default right
29095 * Create a new Item
29096 * @param {Object} config The config object
29100 Roo.bootstrap.menu.Item = function(config){
29101 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29105 * Fires when the mouse is hovering over this menu
29106 * @param {Roo.bootstrap.menu.Item} this
29107 * @param {Roo.EventObject} e
29112 * Fires when the mouse exits this menu
29113 * @param {Roo.bootstrap.menu.Item} this
29114 * @param {Roo.EventObject} e
29120 * The raw click event for the entire grid.
29121 * @param {Roo.EventObject} e
29127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29132 preventDefault: true,
29137 getAutoCreate : function()
29142 cls : 'roo-menu-item-text',
29150 cls : 'fa ' + this.icon
29159 href : this.href || '#',
29166 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29170 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29172 if(this.pos == 'left'){
29173 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29180 initEvents : function()
29182 this.el.on('mouseover', this.onMouseOver, this);
29183 this.el.on('mouseout', this.onMouseOut, this);
29185 this.el.select('a', true).first().on('click', this.onClick, this);
29189 onClick : function(e)
29191 if(this.preventDefault){
29192 e.preventDefault();
29195 this.fireEvent("click", this, e);
29198 onMouseOver : function(e)
29200 if(this.submenu && this.pos == 'left'){
29201 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29204 this.fireEvent("mouseover", this, e);
29207 onMouseOut : function(e)
29209 this.fireEvent("mouseout", this, e);
29221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29224 * @class Roo.bootstrap.menu.Separator
29225 * @extends Roo.bootstrap.Component
29226 * Bootstrap Separator class
29229 * Create a new Separator
29230 * @param {Object} config The config object
29234 Roo.bootstrap.menu.Separator = function(config){
29235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29240 getAutoCreate : function(){
29243 cls: 'dropdown-divider divider'
29261 * @class Roo.bootstrap.Tooltip
29262 * Bootstrap Tooltip class
29263 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29264 * to determine which dom element triggers the tooltip.
29266 * It needs to add support for additional attributes like tooltip-position
29269 * Create a new Toolti
29270 * @param {Object} config The config object
29273 Roo.bootstrap.Tooltip = function(config){
29274 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29276 this.alignment = Roo.bootstrap.Tooltip.alignment;
29278 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29279 this.alignment = config.alignment;
29284 Roo.apply(Roo.bootstrap.Tooltip, {
29286 * @function init initialize tooltip monitoring.
29290 currentTip : false,
29291 currentRegion : false,
29297 Roo.get(document).on('mouseover', this.enter ,this);
29298 Roo.get(document).on('mouseout', this.leave, this);
29301 this.currentTip = new Roo.bootstrap.Tooltip();
29304 enter : function(ev)
29306 var dom = ev.getTarget();
29308 //Roo.log(['enter',dom]);
29309 var el = Roo.fly(dom);
29310 if (this.currentEl) {
29312 //Roo.log(this.currentEl);
29313 //Roo.log(this.currentEl.contains(dom));
29314 if (this.currentEl == el) {
29317 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29323 if (this.currentTip.el) {
29324 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29328 if(!el || el.dom == document){
29334 if (!el.attr('tooltip')) {
29335 pel = el.findParent("[tooltip]");
29337 bindEl = Roo.get(pel);
29343 // you can not look for children, as if el is the body.. then everythign is the child..
29344 if (!pel && !el.attr('tooltip')) { //
29345 if (!el.select("[tooltip]").elements.length) {
29348 // is the mouse over this child...?
29349 bindEl = el.select("[tooltip]").first();
29350 var xy = ev.getXY();
29351 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29352 //Roo.log("not in region.");
29355 //Roo.log("child element over..");
29358 this.currentEl = el;
29359 this.currentTip.bind(bindEl);
29360 this.currentRegion = Roo.lib.Region.getRegion(dom);
29361 this.currentTip.enter();
29364 leave : function(ev)
29366 var dom = ev.getTarget();
29367 //Roo.log(['leave',dom]);
29368 if (!this.currentEl) {
29373 if (dom != this.currentEl.dom) {
29376 var xy = ev.getXY();
29377 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29380 // only activate leave if mouse cursor is outside... bounding box..
29385 if (this.currentTip) {
29386 this.currentTip.leave();
29388 //Roo.log('clear currentEl');
29389 this.currentEl = false;
29394 'left' : ['r-l', [-2,0], 'right'],
29395 'right' : ['l-r', [2,0], 'left'],
29396 'bottom' : ['t-b', [0,2], 'top'],
29397 'top' : [ 'b-t', [0,-2], 'bottom']
29403 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29408 delay : null, // can be { show : 300 , hide: 500}
29412 hoverState : null, //???
29414 placement : 'bottom',
29418 getAutoCreate : function(){
29425 cls : 'tooltip-arrow arrow'
29428 cls : 'tooltip-inner'
29435 bind : function(el)
29440 initEvents : function()
29442 this.arrowEl = this.el.select('.arrow', true).first();
29443 this.innerEl = this.el.select('.tooltip-inner', true).first();
29446 enter : function () {
29448 if (this.timeout != null) {
29449 clearTimeout(this.timeout);
29452 this.hoverState = 'in';
29453 //Roo.log("enter - show");
29454 if (!this.delay || !this.delay.show) {
29459 this.timeout = setTimeout(function () {
29460 if (_t.hoverState == 'in') {
29463 }, this.delay.show);
29467 clearTimeout(this.timeout);
29469 this.hoverState = 'out';
29470 if (!this.delay || !this.delay.hide) {
29476 this.timeout = setTimeout(function () {
29477 //Roo.log("leave - timeout");
29479 if (_t.hoverState == 'out') {
29481 Roo.bootstrap.Tooltip.currentEl = false;
29486 show : function (msg)
29489 this.render(document.body);
29492 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29494 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29496 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29498 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29499 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29501 var placement = typeof this.placement == 'function' ?
29502 this.placement.call(this, this.el, on_el) :
29505 var autoToken = /\s?auto?\s?/i;
29506 var autoPlace = autoToken.test(placement);
29508 placement = placement.replace(autoToken, '') || 'top';
29512 //this.el.setXY([0,0]);
29514 //this.el.dom.style.display='block';
29516 //this.el.appendTo(on_el);
29518 var p = this.getPosition();
29519 var box = this.el.getBox();
29525 var align = this.alignment[placement];
29527 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29529 if(placement == 'top' || placement == 'bottom'){
29531 placement = 'right';
29534 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29535 placement = 'left';
29538 var scroll = Roo.select('body', true).first().getScroll();
29540 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29544 align = this.alignment[placement];
29546 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29550 var elems = document.getElementsByTagName('div');
29551 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29552 for (var i = 0; i < elems.length; i++) {
29553 var zindex = Number.parseInt(
29554 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29557 if (zindex > highest) {
29564 this.el.dom.style.zIndex = highest;
29566 this.el.alignTo(this.bindEl, align[0],align[1]);
29567 //var arrow = this.el.select('.arrow',true).first();
29568 //arrow.set(align[2],
29570 this.el.addClass(placement);
29571 this.el.addClass("bs-tooltip-"+ placement);
29573 this.el.addClass('in fade show');
29575 this.hoverState = null;
29577 if (this.el.hasClass('fade')) {
29592 //this.el.setXY([0,0]);
29593 this.el.removeClass(['show', 'in']);
29609 * @class Roo.bootstrap.LocationPicker
29610 * @extends Roo.bootstrap.Component
29611 * Bootstrap LocationPicker class
29612 * @cfg {Number} latitude Position when init default 0
29613 * @cfg {Number} longitude Position when init default 0
29614 * @cfg {Number} zoom default 15
29615 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29616 * @cfg {Boolean} mapTypeControl default false
29617 * @cfg {Boolean} disableDoubleClickZoom default false
29618 * @cfg {Boolean} scrollwheel default true
29619 * @cfg {Boolean} streetViewControl default false
29620 * @cfg {Number} radius default 0
29621 * @cfg {String} locationName
29622 * @cfg {Boolean} draggable default true
29623 * @cfg {Boolean} enableAutocomplete default false
29624 * @cfg {Boolean} enableReverseGeocode default true
29625 * @cfg {String} markerTitle
29628 * Create a new LocationPicker
29629 * @param {Object} config The config object
29633 Roo.bootstrap.LocationPicker = function(config){
29635 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29640 * Fires when the picker initialized.
29641 * @param {Roo.bootstrap.LocationPicker} this
29642 * @param {Google Location} location
29646 * @event positionchanged
29647 * Fires when the picker position changed.
29648 * @param {Roo.bootstrap.LocationPicker} this
29649 * @param {Google Location} location
29651 positionchanged : true,
29654 * Fires when the map resize.
29655 * @param {Roo.bootstrap.LocationPicker} this
29660 * Fires when the map show.
29661 * @param {Roo.bootstrap.LocationPicker} this
29666 * Fires when the map hide.
29667 * @param {Roo.bootstrap.LocationPicker} this
29672 * Fires when click the map.
29673 * @param {Roo.bootstrap.LocationPicker} this
29674 * @param {Map event} e
29678 * @event mapRightClick
29679 * Fires when right click the map.
29680 * @param {Roo.bootstrap.LocationPicker} this
29681 * @param {Map event} e
29683 mapRightClick : true,
29685 * @event markerClick
29686 * Fires when click the marker.
29687 * @param {Roo.bootstrap.LocationPicker} this
29688 * @param {Map event} e
29690 markerClick : true,
29692 * @event markerRightClick
29693 * Fires when right click the marker.
29694 * @param {Roo.bootstrap.LocationPicker} this
29695 * @param {Map event} e
29697 markerRightClick : true,
29699 * @event OverlayViewDraw
29700 * Fires when OverlayView Draw
29701 * @param {Roo.bootstrap.LocationPicker} this
29703 OverlayViewDraw : true,
29705 * @event OverlayViewOnAdd
29706 * Fires when OverlayView Draw
29707 * @param {Roo.bootstrap.LocationPicker} this
29709 OverlayViewOnAdd : true,
29711 * @event OverlayViewOnRemove
29712 * Fires when OverlayView Draw
29713 * @param {Roo.bootstrap.LocationPicker} this
29715 OverlayViewOnRemove : true,
29717 * @event OverlayViewShow
29718 * Fires when OverlayView Draw
29719 * @param {Roo.bootstrap.LocationPicker} this
29720 * @param {Pixel} cpx
29722 OverlayViewShow : true,
29724 * @event OverlayViewHide
29725 * Fires when OverlayView Draw
29726 * @param {Roo.bootstrap.LocationPicker} this
29728 OverlayViewHide : true,
29730 * @event loadexception
29731 * Fires when load google lib failed.
29732 * @param {Roo.bootstrap.LocationPicker} this
29734 loadexception : true
29739 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29741 gMapContext: false,
29747 mapTypeControl: false,
29748 disableDoubleClickZoom: false,
29750 streetViewControl: false,
29754 enableAutocomplete: false,
29755 enableReverseGeocode: true,
29758 getAutoCreate: function()
29763 cls: 'roo-location-picker'
29769 initEvents: function(ct, position)
29771 if(!this.el.getWidth() || this.isApplied()){
29775 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29780 initial: function()
29782 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29783 this.fireEvent('loadexception', this);
29787 if(!this.mapTypeId){
29788 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29791 this.gMapContext = this.GMapContext();
29793 this.initOverlayView();
29795 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29799 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29800 _this.setPosition(_this.gMapContext.marker.position);
29803 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29804 _this.fireEvent('mapClick', this, event);
29808 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29809 _this.fireEvent('mapRightClick', this, event);
29813 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29814 _this.fireEvent('markerClick', this, event);
29818 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29819 _this.fireEvent('markerRightClick', this, event);
29823 this.setPosition(this.gMapContext.location);
29825 this.fireEvent('initial', this, this.gMapContext.location);
29828 initOverlayView: function()
29832 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29836 _this.fireEvent('OverlayViewDraw', _this);
29841 _this.fireEvent('OverlayViewOnAdd', _this);
29844 onRemove: function()
29846 _this.fireEvent('OverlayViewOnRemove', _this);
29849 show: function(cpx)
29851 _this.fireEvent('OverlayViewShow', _this, cpx);
29856 _this.fireEvent('OverlayViewHide', _this);
29862 fromLatLngToContainerPixel: function(event)
29864 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29867 isApplied: function()
29869 return this.getGmapContext() == false ? false : true;
29872 getGmapContext: function()
29874 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29877 GMapContext: function()
29879 var position = new google.maps.LatLng(this.latitude, this.longitude);
29881 var _map = new google.maps.Map(this.el.dom, {
29884 mapTypeId: this.mapTypeId,
29885 mapTypeControl: this.mapTypeControl,
29886 disableDoubleClickZoom: this.disableDoubleClickZoom,
29887 scrollwheel: this.scrollwheel,
29888 streetViewControl: this.streetViewControl,
29889 locationName: this.locationName,
29890 draggable: this.draggable,
29891 enableAutocomplete: this.enableAutocomplete,
29892 enableReverseGeocode: this.enableReverseGeocode
29895 var _marker = new google.maps.Marker({
29896 position: position,
29898 title: this.markerTitle,
29899 draggable: this.draggable
29906 location: position,
29907 radius: this.radius,
29908 locationName: this.locationName,
29909 addressComponents: {
29910 formatted_address: null,
29911 addressLine1: null,
29912 addressLine2: null,
29914 streetNumber: null,
29918 stateOrProvince: null
29921 domContainer: this.el.dom,
29922 geodecoder: new google.maps.Geocoder()
29926 drawCircle: function(center, radius, options)
29928 if (this.gMapContext.circle != null) {
29929 this.gMapContext.circle.setMap(null);
29933 options = Roo.apply({}, options, {
29934 strokeColor: "#0000FF",
29935 strokeOpacity: .35,
29937 fillColor: "#0000FF",
29941 options.map = this.gMapContext.map;
29942 options.radius = radius;
29943 options.center = center;
29944 this.gMapContext.circle = new google.maps.Circle(options);
29945 return this.gMapContext.circle;
29951 setPosition: function(location)
29953 this.gMapContext.location = location;
29954 this.gMapContext.marker.setPosition(location);
29955 this.gMapContext.map.panTo(location);
29956 this.drawCircle(location, this.gMapContext.radius, {});
29960 if (this.gMapContext.settings.enableReverseGeocode) {
29961 this.gMapContext.geodecoder.geocode({
29962 latLng: this.gMapContext.location
29963 }, function(results, status) {
29965 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29966 _this.gMapContext.locationName = results[0].formatted_address;
29967 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29969 _this.fireEvent('positionchanged', this, location);
29976 this.fireEvent('positionchanged', this, location);
29981 google.maps.event.trigger(this.gMapContext.map, "resize");
29983 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29985 this.fireEvent('resize', this);
29988 setPositionByLatLng: function(latitude, longitude)
29990 this.setPosition(new google.maps.LatLng(latitude, longitude));
29993 getCurrentPosition: function()
29996 latitude: this.gMapContext.location.lat(),
29997 longitude: this.gMapContext.location.lng()
30001 getAddressName: function()
30003 return this.gMapContext.locationName;
30006 getAddressComponents: function()
30008 return this.gMapContext.addressComponents;
30011 address_component_from_google_geocode: function(address_components)
30015 for (var i = 0; i < address_components.length; i++) {
30016 var component = address_components[i];
30017 if (component.types.indexOf("postal_code") >= 0) {
30018 result.postalCode = component.short_name;
30019 } else if (component.types.indexOf("street_number") >= 0) {
30020 result.streetNumber = component.short_name;
30021 } else if (component.types.indexOf("route") >= 0) {
30022 result.streetName = component.short_name;
30023 } else if (component.types.indexOf("neighborhood") >= 0) {
30024 result.city = component.short_name;
30025 } else if (component.types.indexOf("locality") >= 0) {
30026 result.city = component.short_name;
30027 } else if (component.types.indexOf("sublocality") >= 0) {
30028 result.district = component.short_name;
30029 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30030 result.stateOrProvince = component.short_name;
30031 } else if (component.types.indexOf("country") >= 0) {
30032 result.country = component.short_name;
30036 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30037 result.addressLine2 = "";
30041 setZoomLevel: function(zoom)
30043 this.gMapContext.map.setZoom(zoom);
30056 this.fireEvent('show', this);
30067 this.fireEvent('hide', this);
30072 Roo.apply(Roo.bootstrap.LocationPicker, {
30074 OverlayView : function(map, options)
30076 options = options || {};
30083 * @class Roo.bootstrap.Alert
30084 * @extends Roo.bootstrap.Component
30085 * Bootstrap Alert class - shows an alert area box
30087 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30088 Enter a valid email address
30091 * @cfg {String} title The title of alert
30092 * @cfg {String} html The content of alert
30093 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30094 * @cfg {String} fa font-awesomeicon
30095 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30096 * @cfg {Boolean} close true to show a x closer
30100 * Create a new alert
30101 * @param {Object} config The config object
30105 Roo.bootstrap.Alert = function(config){
30106 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30110 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30116 faicon: false, // BC
30120 getAutoCreate : function()
30132 style : this.close ? '' : 'display:none'
30136 cls : 'roo-alert-icon'
30141 cls : 'roo-alert-title',
30146 cls : 'roo-alert-text',
30153 cfg.cn[0].cls += ' fa ' + this.faicon;
30156 cfg.cn[0].cls += ' fa ' + this.fa;
30160 cfg.cls += ' alert-' + this.weight;
30166 initEvents: function()
30168 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30169 this.titleEl = this.el.select('.roo-alert-title',true).first();
30170 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30171 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30172 if (this.seconds > 0) {
30173 this.hide.defer(this.seconds, this);
30177 * Set the Title Message HTML
30178 * @param {String} html
30180 setTitle : function(str)
30182 this.titleEl.dom.innerHTML = str;
30186 * Set the Body Message HTML
30187 * @param {String} html
30189 setHtml : function(str)
30191 this.htmlEl.dom.innerHTML = str;
30194 * Set the Weight of the alert
30195 * @param {String} (success|info|warning|danger) weight
30198 setWeight : function(weight)
30201 this.el.removeClass('alert-' + this.weight);
30204 this.weight = weight;
30206 this.el.addClass('alert-' + this.weight);
30209 * Set the Icon of the alert
30210 * @param {String} see fontawsome names (name without the 'fa-' bit)
30212 setIcon : function(icon)
30215 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30218 this.faicon = icon;
30220 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30245 * @class Roo.bootstrap.UploadCropbox
30246 * @extends Roo.bootstrap.Component
30247 * Bootstrap UploadCropbox class
30248 * @cfg {String} emptyText show when image has been loaded
30249 * @cfg {String} rotateNotify show when image too small to rotate
30250 * @cfg {Number} errorTimeout default 3000
30251 * @cfg {Number} minWidth default 300
30252 * @cfg {Number} minHeight default 300
30253 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30254 * @cfg {Boolean} isDocument (true|false) default false
30255 * @cfg {String} url action url
30256 * @cfg {String} paramName default 'imageUpload'
30257 * @cfg {String} method default POST
30258 * @cfg {Boolean} loadMask (true|false) default true
30259 * @cfg {Boolean} loadingText default 'Loading...'
30262 * Create a new UploadCropbox
30263 * @param {Object} config The config object
30266 Roo.bootstrap.UploadCropbox = function(config){
30267 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30271 * @event beforeselectfile
30272 * Fire before select file
30273 * @param {Roo.bootstrap.UploadCropbox} this
30275 "beforeselectfile" : true,
30278 * Fire after initEvent
30279 * @param {Roo.bootstrap.UploadCropbox} this
30284 * Fire after initEvent
30285 * @param {Roo.bootstrap.UploadCropbox} this
30286 * @param {String} data
30291 * Fire when preparing the file data
30292 * @param {Roo.bootstrap.UploadCropbox} this
30293 * @param {Object} file
30298 * Fire when get exception
30299 * @param {Roo.bootstrap.UploadCropbox} this
30300 * @param {XMLHttpRequest} xhr
30302 "exception" : true,
30304 * @event beforeloadcanvas
30305 * Fire before load the canvas
30306 * @param {Roo.bootstrap.UploadCropbox} this
30307 * @param {String} src
30309 "beforeloadcanvas" : true,
30312 * Fire when trash image
30313 * @param {Roo.bootstrap.UploadCropbox} this
30318 * Fire when download the image
30319 * @param {Roo.bootstrap.UploadCropbox} this
30323 * @event footerbuttonclick
30324 * Fire when footerbuttonclick
30325 * @param {Roo.bootstrap.UploadCropbox} this
30326 * @param {String} type
30328 "footerbuttonclick" : true,
30332 * @param {Roo.bootstrap.UploadCropbox} this
30337 * Fire when rotate the image
30338 * @param {Roo.bootstrap.UploadCropbox} this
30339 * @param {String} pos
30344 * Fire when inspect the file
30345 * @param {Roo.bootstrap.UploadCropbox} this
30346 * @param {Object} file
30351 * Fire when xhr upload the file
30352 * @param {Roo.bootstrap.UploadCropbox} this
30353 * @param {Object} data
30358 * Fire when arrange the file data
30359 * @param {Roo.bootstrap.UploadCropbox} this
30360 * @param {Object} formData
30365 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30368 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30370 emptyText : 'Click to upload image',
30371 rotateNotify : 'Image is too small to rotate',
30372 errorTimeout : 3000,
30386 cropType : 'image/jpeg',
30388 canvasLoaded : false,
30389 isDocument : false,
30391 paramName : 'imageUpload',
30393 loadingText : 'Loading...',
30396 getAutoCreate : function()
30400 cls : 'roo-upload-cropbox',
30404 cls : 'roo-upload-cropbox-selector',
30409 cls : 'roo-upload-cropbox-body',
30410 style : 'cursor:pointer',
30414 cls : 'roo-upload-cropbox-preview'
30418 cls : 'roo-upload-cropbox-thumb'
30422 cls : 'roo-upload-cropbox-empty-notify',
30423 html : this.emptyText
30427 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30428 html : this.rotateNotify
30434 cls : 'roo-upload-cropbox-footer',
30437 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30447 onRender : function(ct, position)
30449 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30451 if (this.buttons.length) {
30453 Roo.each(this.buttons, function(bb) {
30455 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30457 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30463 this.maskEl = this.el;
30467 initEvents : function()
30469 this.urlAPI = (window.createObjectURL && window) ||
30470 (window.URL && URL.revokeObjectURL && URL) ||
30471 (window.webkitURL && webkitURL);
30473 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30474 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30476 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30477 this.selectorEl.hide();
30479 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30480 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30482 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30483 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30484 this.thumbEl.hide();
30486 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30487 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30489 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30490 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30491 this.errorEl.hide();
30493 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30494 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30495 this.footerEl.hide();
30497 this.setThumbBoxSize();
30503 this.fireEvent('initial', this);
30510 window.addEventListener("resize", function() { _this.resize(); } );
30512 this.bodyEl.on('click', this.beforeSelectFile, this);
30515 this.bodyEl.on('touchstart', this.onTouchStart, this);
30516 this.bodyEl.on('touchmove', this.onTouchMove, this);
30517 this.bodyEl.on('touchend', this.onTouchEnd, this);
30521 this.bodyEl.on('mousedown', this.onMouseDown, this);
30522 this.bodyEl.on('mousemove', this.onMouseMove, this);
30523 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30524 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30525 Roo.get(document).on('mouseup', this.onMouseUp, this);
30528 this.selectorEl.on('change', this.onFileSelected, this);
30534 this.baseScale = 1;
30536 this.baseRotate = 1;
30537 this.dragable = false;
30538 this.pinching = false;
30541 this.cropData = false;
30542 this.notifyEl.dom.innerHTML = this.emptyText;
30544 this.selectorEl.dom.value = '';
30548 resize : function()
30550 if(this.fireEvent('resize', this) != false){
30551 this.setThumbBoxPosition();
30552 this.setCanvasPosition();
30556 onFooterButtonClick : function(e, el, o, type)
30559 case 'rotate-left' :
30560 this.onRotateLeft(e);
30562 case 'rotate-right' :
30563 this.onRotateRight(e);
30566 this.beforeSelectFile(e);
30581 this.fireEvent('footerbuttonclick', this, type);
30584 beforeSelectFile : function(e)
30586 e.preventDefault();
30588 if(this.fireEvent('beforeselectfile', this) != false){
30589 this.selectorEl.dom.click();
30593 onFileSelected : function(e)
30595 e.preventDefault();
30597 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30601 var file = this.selectorEl.dom.files[0];
30603 if(this.fireEvent('inspect', this, file) != false){
30604 this.prepare(file);
30609 trash : function(e)
30611 this.fireEvent('trash', this);
30614 download : function(e)
30616 this.fireEvent('download', this);
30619 loadCanvas : function(src)
30621 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30625 this.imageEl = document.createElement('img');
30629 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30631 this.imageEl.src = src;
30635 onLoadCanvas : function()
30637 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30638 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30640 this.bodyEl.un('click', this.beforeSelectFile, this);
30642 this.notifyEl.hide();
30643 this.thumbEl.show();
30644 this.footerEl.show();
30646 this.baseRotateLevel();
30648 if(this.isDocument){
30649 this.setThumbBoxSize();
30652 this.setThumbBoxPosition();
30654 this.baseScaleLevel();
30660 this.canvasLoaded = true;
30663 this.maskEl.unmask();
30668 setCanvasPosition : function()
30670 if(!this.canvasEl){
30674 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30675 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30677 this.previewEl.setLeft(pw);
30678 this.previewEl.setTop(ph);
30682 onMouseDown : function(e)
30686 this.dragable = true;
30687 this.pinching = false;
30689 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30690 this.dragable = false;
30694 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30695 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30699 onMouseMove : function(e)
30703 if(!this.canvasLoaded){
30707 if (!this.dragable){
30711 var minX = Math.ceil(this.thumbEl.getLeft(true));
30712 var minY = Math.ceil(this.thumbEl.getTop(true));
30714 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30715 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30717 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30718 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30720 x = x - this.mouseX;
30721 y = y - this.mouseY;
30723 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30724 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30726 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30727 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30729 this.previewEl.setLeft(bgX);
30730 this.previewEl.setTop(bgY);
30732 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30733 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30736 onMouseUp : function(e)
30740 this.dragable = false;
30743 onMouseWheel : function(e)
30747 this.startScale = this.scale;
30749 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30751 if(!this.zoomable()){
30752 this.scale = this.startScale;
30761 zoomable : function()
30763 var minScale = this.thumbEl.getWidth() / this.minWidth;
30765 if(this.minWidth < this.minHeight){
30766 minScale = this.thumbEl.getHeight() / this.minHeight;
30769 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30770 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30774 (this.rotate == 0 || this.rotate == 180) &&
30776 width > this.imageEl.OriginWidth ||
30777 height > this.imageEl.OriginHeight ||
30778 (width < this.minWidth && height < this.minHeight)
30786 (this.rotate == 90 || this.rotate == 270) &&
30788 width > this.imageEl.OriginWidth ||
30789 height > this.imageEl.OriginHeight ||
30790 (width < this.minHeight && height < this.minWidth)
30797 !this.isDocument &&
30798 (this.rotate == 0 || this.rotate == 180) &&
30800 width < this.minWidth ||
30801 width > this.imageEl.OriginWidth ||
30802 height < this.minHeight ||
30803 height > this.imageEl.OriginHeight
30810 !this.isDocument &&
30811 (this.rotate == 90 || this.rotate == 270) &&
30813 width < this.minHeight ||
30814 width > this.imageEl.OriginWidth ||
30815 height < this.minWidth ||
30816 height > this.imageEl.OriginHeight
30826 onRotateLeft : function(e)
30828 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30830 var minScale = this.thumbEl.getWidth() / this.minWidth;
30832 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30833 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30835 this.startScale = this.scale;
30837 while (this.getScaleLevel() < minScale){
30839 this.scale = this.scale + 1;
30841 if(!this.zoomable()){
30846 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30847 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30852 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30859 this.scale = this.startScale;
30861 this.onRotateFail();
30866 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30868 if(this.isDocument){
30869 this.setThumbBoxSize();
30870 this.setThumbBoxPosition();
30871 this.setCanvasPosition();
30876 this.fireEvent('rotate', this, 'left');
30880 onRotateRight : function(e)
30882 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30884 var minScale = this.thumbEl.getWidth() / this.minWidth;
30886 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30887 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30889 this.startScale = this.scale;
30891 while (this.getScaleLevel() < minScale){
30893 this.scale = this.scale + 1;
30895 if(!this.zoomable()){
30900 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30901 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30906 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30913 this.scale = this.startScale;
30915 this.onRotateFail();
30920 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30922 if(this.isDocument){
30923 this.setThumbBoxSize();
30924 this.setThumbBoxPosition();
30925 this.setCanvasPosition();
30930 this.fireEvent('rotate', this, 'right');
30933 onRotateFail : function()
30935 this.errorEl.show(true);
30939 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30944 this.previewEl.dom.innerHTML = '';
30946 var canvasEl = document.createElement("canvas");
30948 var contextEl = canvasEl.getContext("2d");
30950 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30951 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30952 var center = this.imageEl.OriginWidth / 2;
30954 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30955 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30956 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30957 center = this.imageEl.OriginHeight / 2;
30960 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30962 contextEl.translate(center, center);
30963 contextEl.rotate(this.rotate * Math.PI / 180);
30965 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30967 this.canvasEl = document.createElement("canvas");
30969 this.contextEl = this.canvasEl.getContext("2d");
30971 switch (this.rotate) {
30974 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30975 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30977 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30982 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30983 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30985 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30986 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);
30990 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30995 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30996 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30998 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30999 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);
31003 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);
31008 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31009 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31011 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31012 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31016 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);
31023 this.previewEl.appendChild(this.canvasEl);
31025 this.setCanvasPosition();
31030 if(!this.canvasLoaded){
31034 var imageCanvas = document.createElement("canvas");
31036 var imageContext = imageCanvas.getContext("2d");
31038 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31039 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31041 var center = imageCanvas.width / 2;
31043 imageContext.translate(center, center);
31045 imageContext.rotate(this.rotate * Math.PI / 180);
31047 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31049 var canvas = document.createElement("canvas");
31051 var context = canvas.getContext("2d");
31053 canvas.width = this.minWidth;
31054 canvas.height = this.minHeight;
31056 switch (this.rotate) {
31059 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31060 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31062 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31063 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31065 var targetWidth = this.minWidth - 2 * x;
31066 var targetHeight = this.minHeight - 2 * y;
31070 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31071 scale = targetWidth / width;
31074 if(x > 0 && y == 0){
31075 scale = targetHeight / height;
31078 if(x > 0 && y > 0){
31079 scale = targetWidth / width;
31081 if(width < height){
31082 scale = targetHeight / height;
31086 context.scale(scale, scale);
31088 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31089 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31091 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31092 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31094 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31099 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31100 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31102 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31103 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31105 var targetWidth = this.minWidth - 2 * x;
31106 var targetHeight = this.minHeight - 2 * y;
31110 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31111 scale = targetWidth / width;
31114 if(x > 0 && y == 0){
31115 scale = targetHeight / height;
31118 if(x > 0 && y > 0){
31119 scale = targetWidth / width;
31121 if(width < height){
31122 scale = targetHeight / height;
31126 context.scale(scale, scale);
31128 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31129 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31131 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31132 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31134 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31136 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31141 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31142 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31144 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31145 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31147 var targetWidth = this.minWidth - 2 * x;
31148 var targetHeight = this.minHeight - 2 * y;
31152 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31153 scale = targetWidth / width;
31156 if(x > 0 && y == 0){
31157 scale = targetHeight / height;
31160 if(x > 0 && y > 0){
31161 scale = targetWidth / width;
31163 if(width < height){
31164 scale = targetHeight / height;
31168 context.scale(scale, scale);
31170 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31171 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31173 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31174 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31176 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31177 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31179 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31184 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31185 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31187 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31188 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31190 var targetWidth = this.minWidth - 2 * x;
31191 var targetHeight = this.minHeight - 2 * y;
31195 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31196 scale = targetWidth / width;
31199 if(x > 0 && y == 0){
31200 scale = targetHeight / height;
31203 if(x > 0 && y > 0){
31204 scale = targetWidth / width;
31206 if(width < height){
31207 scale = targetHeight / height;
31211 context.scale(scale, scale);
31213 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31214 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31216 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31217 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31219 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31221 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31228 this.cropData = canvas.toDataURL(this.cropType);
31230 if(this.fireEvent('crop', this, this.cropData) !== false){
31231 this.process(this.file, this.cropData);
31238 setThumbBoxSize : function()
31242 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31243 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31244 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31246 this.minWidth = width;
31247 this.minHeight = height;
31249 if(this.rotate == 90 || this.rotate == 270){
31250 this.minWidth = height;
31251 this.minHeight = width;
31256 width = Math.ceil(this.minWidth * height / this.minHeight);
31258 if(this.minWidth > this.minHeight){
31260 height = Math.ceil(this.minHeight * width / this.minWidth);
31263 this.thumbEl.setStyle({
31264 width : width + 'px',
31265 height : height + 'px'
31272 setThumbBoxPosition : function()
31274 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31275 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31277 this.thumbEl.setLeft(x);
31278 this.thumbEl.setTop(y);
31282 baseRotateLevel : function()
31284 this.baseRotate = 1;
31287 typeof(this.exif) != 'undefined' &&
31288 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31289 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31291 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31294 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31298 baseScaleLevel : function()
31302 if(this.isDocument){
31304 if(this.baseRotate == 6 || this.baseRotate == 8){
31306 height = this.thumbEl.getHeight();
31307 this.baseScale = height / this.imageEl.OriginWidth;
31309 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31310 width = this.thumbEl.getWidth();
31311 this.baseScale = width / this.imageEl.OriginHeight;
31317 height = this.thumbEl.getHeight();
31318 this.baseScale = height / this.imageEl.OriginHeight;
31320 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31321 width = this.thumbEl.getWidth();
31322 this.baseScale = width / this.imageEl.OriginWidth;
31328 if(this.baseRotate == 6 || this.baseRotate == 8){
31330 width = this.thumbEl.getHeight();
31331 this.baseScale = width / this.imageEl.OriginHeight;
31333 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31334 height = this.thumbEl.getWidth();
31335 this.baseScale = height / this.imageEl.OriginHeight;
31338 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31339 height = this.thumbEl.getWidth();
31340 this.baseScale = height / this.imageEl.OriginHeight;
31342 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31343 width = this.thumbEl.getHeight();
31344 this.baseScale = width / this.imageEl.OriginWidth;
31351 width = this.thumbEl.getWidth();
31352 this.baseScale = width / this.imageEl.OriginWidth;
31354 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31355 height = this.thumbEl.getHeight();
31356 this.baseScale = height / this.imageEl.OriginHeight;
31359 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31361 height = this.thumbEl.getHeight();
31362 this.baseScale = height / this.imageEl.OriginHeight;
31364 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31365 width = this.thumbEl.getWidth();
31366 this.baseScale = width / this.imageEl.OriginWidth;
31374 getScaleLevel : function()
31376 return this.baseScale * Math.pow(1.1, this.scale);
31379 onTouchStart : function(e)
31381 if(!this.canvasLoaded){
31382 this.beforeSelectFile(e);
31386 var touches = e.browserEvent.touches;
31392 if(touches.length == 1){
31393 this.onMouseDown(e);
31397 if(touches.length != 2){
31403 for(var i = 0, finger; finger = touches[i]; i++){
31404 coords.push(finger.pageX, finger.pageY);
31407 var x = Math.pow(coords[0] - coords[2], 2);
31408 var y = Math.pow(coords[1] - coords[3], 2);
31410 this.startDistance = Math.sqrt(x + y);
31412 this.startScale = this.scale;
31414 this.pinching = true;
31415 this.dragable = false;
31419 onTouchMove : function(e)
31421 if(!this.pinching && !this.dragable){
31425 var touches = e.browserEvent.touches;
31432 this.onMouseMove(e);
31438 for(var i = 0, finger; finger = touches[i]; i++){
31439 coords.push(finger.pageX, finger.pageY);
31442 var x = Math.pow(coords[0] - coords[2], 2);
31443 var y = Math.pow(coords[1] - coords[3], 2);
31445 this.endDistance = Math.sqrt(x + y);
31447 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31449 if(!this.zoomable()){
31450 this.scale = this.startScale;
31458 onTouchEnd : function(e)
31460 this.pinching = false;
31461 this.dragable = false;
31465 process : function(file, crop)
31468 this.maskEl.mask(this.loadingText);
31471 this.xhr = new XMLHttpRequest();
31473 file.xhr = this.xhr;
31475 this.xhr.open(this.method, this.url, true);
31478 "Accept": "application/json",
31479 "Cache-Control": "no-cache",
31480 "X-Requested-With": "XMLHttpRequest"
31483 for (var headerName in headers) {
31484 var headerValue = headers[headerName];
31486 this.xhr.setRequestHeader(headerName, headerValue);
31492 this.xhr.onload = function()
31494 _this.xhrOnLoad(_this.xhr);
31497 this.xhr.onerror = function()
31499 _this.xhrOnError(_this.xhr);
31502 var formData = new FormData();
31504 formData.append('returnHTML', 'NO');
31507 formData.append('crop', crop);
31510 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31511 formData.append(this.paramName, file, file.name);
31514 if(typeof(file.filename) != 'undefined'){
31515 formData.append('filename', file.filename);
31518 if(typeof(file.mimetype) != 'undefined'){
31519 formData.append('mimetype', file.mimetype);
31522 if(this.fireEvent('arrange', this, formData) != false){
31523 this.xhr.send(formData);
31527 xhrOnLoad : function(xhr)
31530 this.maskEl.unmask();
31533 if (xhr.readyState !== 4) {
31534 this.fireEvent('exception', this, xhr);
31538 var response = Roo.decode(xhr.responseText);
31540 if(!response.success){
31541 this.fireEvent('exception', this, xhr);
31545 var response = Roo.decode(xhr.responseText);
31547 this.fireEvent('upload', this, response);
31551 xhrOnError : function()
31554 this.maskEl.unmask();
31557 Roo.log('xhr on error');
31559 var response = Roo.decode(xhr.responseText);
31565 prepare : function(file)
31568 this.maskEl.mask(this.loadingText);
31574 if(typeof(file) === 'string'){
31575 this.loadCanvas(file);
31579 if(!file || !this.urlAPI){
31584 this.cropType = file.type;
31588 if(this.fireEvent('prepare', this, this.file) != false){
31590 var reader = new FileReader();
31592 reader.onload = function (e) {
31593 if (e.target.error) {
31594 Roo.log(e.target.error);
31598 var buffer = e.target.result,
31599 dataView = new DataView(buffer),
31601 maxOffset = dataView.byteLength - 4,
31605 if (dataView.getUint16(0) === 0xffd8) {
31606 while (offset < maxOffset) {
31607 markerBytes = dataView.getUint16(offset);
31609 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31610 markerLength = dataView.getUint16(offset + 2) + 2;
31611 if (offset + markerLength > dataView.byteLength) {
31612 Roo.log('Invalid meta data: Invalid segment size.');
31616 if(markerBytes == 0xffe1){
31617 _this.parseExifData(
31624 offset += markerLength;
31634 var url = _this.urlAPI.createObjectURL(_this.file);
31636 _this.loadCanvas(url);
31641 reader.readAsArrayBuffer(this.file);
31647 parseExifData : function(dataView, offset, length)
31649 var tiffOffset = offset + 10,
31653 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31654 // No Exif data, might be XMP data instead
31658 // Check for the ASCII code for "Exif" (0x45786966):
31659 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31660 // No Exif data, might be XMP data instead
31663 if (tiffOffset + 8 > dataView.byteLength) {
31664 Roo.log('Invalid Exif data: Invalid segment size.');
31667 // Check for the two null bytes:
31668 if (dataView.getUint16(offset + 8) !== 0x0000) {
31669 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31672 // Check the byte alignment:
31673 switch (dataView.getUint16(tiffOffset)) {
31675 littleEndian = true;
31678 littleEndian = false;
31681 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31684 // Check for the TIFF tag marker (0x002A):
31685 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31686 Roo.log('Invalid Exif data: Missing TIFF marker.');
31689 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31690 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31692 this.parseExifTags(
31695 tiffOffset + dirOffset,
31700 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31705 if (dirOffset + 6 > dataView.byteLength) {
31706 Roo.log('Invalid Exif data: Invalid directory offset.');
31709 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31710 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31711 if (dirEndOffset + 4 > dataView.byteLength) {
31712 Roo.log('Invalid Exif data: Invalid directory size.');
31715 for (i = 0; i < tagsNumber; i += 1) {
31719 dirOffset + 2 + 12 * i, // tag offset
31723 // Return the offset to the next directory:
31724 return dataView.getUint32(dirEndOffset, littleEndian);
31727 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31729 var tag = dataView.getUint16(offset, littleEndian);
31731 this.exif[tag] = this.getExifValue(
31735 dataView.getUint16(offset + 2, littleEndian), // tag type
31736 dataView.getUint32(offset + 4, littleEndian), // tag length
31741 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31743 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31752 Roo.log('Invalid Exif data: Invalid tag type.');
31756 tagSize = tagType.size * length;
31757 // Determine if the value is contained in the dataOffset bytes,
31758 // or if the value at the dataOffset is a pointer to the actual data:
31759 dataOffset = tagSize > 4 ?
31760 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31761 if (dataOffset + tagSize > dataView.byteLength) {
31762 Roo.log('Invalid Exif data: Invalid data offset.');
31765 if (length === 1) {
31766 return tagType.getValue(dataView, dataOffset, littleEndian);
31769 for (i = 0; i < length; i += 1) {
31770 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31773 if (tagType.ascii) {
31775 // Concatenate the chars:
31776 for (i = 0; i < values.length; i += 1) {
31778 // Ignore the terminating NULL byte(s):
31779 if (c === '\u0000') {
31791 Roo.apply(Roo.bootstrap.UploadCropbox, {
31793 'Orientation': 0x0112
31797 1: 0, //'top-left',
31799 3: 180, //'bottom-right',
31800 // 4: 'bottom-left',
31802 6: 90, //'right-top',
31803 // 7: 'right-bottom',
31804 8: 270 //'left-bottom'
31808 // byte, 8-bit unsigned int:
31810 getValue: function (dataView, dataOffset) {
31811 return dataView.getUint8(dataOffset);
31815 // ascii, 8-bit byte:
31817 getValue: function (dataView, dataOffset) {
31818 return String.fromCharCode(dataView.getUint8(dataOffset));
31823 // short, 16 bit int:
31825 getValue: function (dataView, dataOffset, littleEndian) {
31826 return dataView.getUint16(dataOffset, littleEndian);
31830 // long, 32 bit int:
31832 getValue: function (dataView, dataOffset, littleEndian) {
31833 return dataView.getUint32(dataOffset, littleEndian);
31837 // rational = two long values, first is numerator, second is denominator:
31839 getValue: function (dataView, dataOffset, littleEndian) {
31840 return dataView.getUint32(dataOffset, littleEndian) /
31841 dataView.getUint32(dataOffset + 4, littleEndian);
31845 // slong, 32 bit signed int:
31847 getValue: function (dataView, dataOffset, littleEndian) {
31848 return dataView.getInt32(dataOffset, littleEndian);
31852 // srational, two slongs, first is numerator, second is denominator:
31854 getValue: function (dataView, dataOffset, littleEndian) {
31855 return dataView.getInt32(dataOffset, littleEndian) /
31856 dataView.getInt32(dataOffset + 4, littleEndian);
31866 cls : 'btn-group roo-upload-cropbox-rotate-left',
31867 action : 'rotate-left',
31871 cls : 'btn btn-default',
31872 html : '<i class="fa fa-undo"></i>'
31878 cls : 'btn-group roo-upload-cropbox-picture',
31879 action : 'picture',
31883 cls : 'btn btn-default',
31884 html : '<i class="fa fa-picture-o"></i>'
31890 cls : 'btn-group roo-upload-cropbox-rotate-right',
31891 action : 'rotate-right',
31895 cls : 'btn btn-default',
31896 html : '<i class="fa fa-repeat"></i>'
31904 cls : 'btn-group roo-upload-cropbox-rotate-left',
31905 action : 'rotate-left',
31909 cls : 'btn btn-default',
31910 html : '<i class="fa fa-undo"></i>'
31916 cls : 'btn-group roo-upload-cropbox-download',
31917 action : 'download',
31921 cls : 'btn btn-default',
31922 html : '<i class="fa fa-download"></i>'
31928 cls : 'btn-group roo-upload-cropbox-crop',
31933 cls : 'btn btn-default',
31934 html : '<i class="fa fa-crop"></i>'
31940 cls : 'btn-group roo-upload-cropbox-trash',
31945 cls : 'btn btn-default',
31946 html : '<i class="fa fa-trash"></i>'
31952 cls : 'btn-group roo-upload-cropbox-rotate-right',
31953 action : 'rotate-right',
31957 cls : 'btn btn-default',
31958 html : '<i class="fa fa-repeat"></i>'
31966 cls : 'btn-group roo-upload-cropbox-rotate-left',
31967 action : 'rotate-left',
31971 cls : 'btn btn-default',
31972 html : '<i class="fa fa-undo"></i>'
31978 cls : 'btn-group roo-upload-cropbox-rotate-right',
31979 action : 'rotate-right',
31983 cls : 'btn btn-default',
31984 html : '<i class="fa fa-repeat"></i>'
31997 * @class Roo.bootstrap.DocumentManager
31998 * @extends Roo.bootstrap.Component
31999 * Bootstrap DocumentManager class
32000 * @cfg {String} paramName default 'imageUpload'
32001 * @cfg {String} toolTipName default 'filename'
32002 * @cfg {String} method default POST
32003 * @cfg {String} url action url
32004 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32005 * @cfg {Boolean} multiple multiple upload default true
32006 * @cfg {Number} thumbSize default 300
32007 * @cfg {String} fieldLabel
32008 * @cfg {Number} labelWidth default 4
32009 * @cfg {String} labelAlign (left|top) default left
32010 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32011 * @cfg {Number} labellg set the width of label (1-12)
32012 * @cfg {Number} labelmd set the width of label (1-12)
32013 * @cfg {Number} labelsm set the width of label (1-12)
32014 * @cfg {Number} labelxs set the width of label (1-12)
32017 * Create a new DocumentManager
32018 * @param {Object} config The config object
32021 Roo.bootstrap.DocumentManager = function(config){
32022 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32025 this.delegates = [];
32030 * Fire when initial the DocumentManager
32031 * @param {Roo.bootstrap.DocumentManager} this
32036 * inspect selected file
32037 * @param {Roo.bootstrap.DocumentManager} this
32038 * @param {File} file
32043 * Fire when xhr load exception
32044 * @param {Roo.bootstrap.DocumentManager} this
32045 * @param {XMLHttpRequest} xhr
32047 "exception" : true,
32049 * @event afterupload
32050 * Fire when xhr load exception
32051 * @param {Roo.bootstrap.DocumentManager} this
32052 * @param {XMLHttpRequest} xhr
32054 "afterupload" : true,
32057 * prepare the form data
32058 * @param {Roo.bootstrap.DocumentManager} this
32059 * @param {Object} formData
32064 * Fire when remove the file
32065 * @param {Roo.bootstrap.DocumentManager} this
32066 * @param {Object} file
32071 * Fire after refresh the file
32072 * @param {Roo.bootstrap.DocumentManager} this
32077 * Fire after click the image
32078 * @param {Roo.bootstrap.DocumentManager} this
32079 * @param {Object} file
32084 * Fire when upload a image and editable set to true
32085 * @param {Roo.bootstrap.DocumentManager} this
32086 * @param {Object} file
32090 * @event beforeselectfile
32091 * Fire before select file
32092 * @param {Roo.bootstrap.DocumentManager} this
32094 "beforeselectfile" : true,
32097 * Fire before process file
32098 * @param {Roo.bootstrap.DocumentManager} this
32099 * @param {Object} file
32103 * @event previewrendered
32104 * Fire when preview rendered
32105 * @param {Roo.bootstrap.DocumentManager} this
32106 * @param {Object} file
32108 "previewrendered" : true,
32111 "previewResize" : true
32116 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32125 paramName : 'imageUpload',
32126 toolTipName : 'filename',
32129 labelAlign : 'left',
32139 getAutoCreate : function()
32141 var managerWidget = {
32143 cls : 'roo-document-manager',
32147 cls : 'roo-document-manager-selector',
32152 cls : 'roo-document-manager-uploader',
32156 cls : 'roo-document-manager-upload-btn',
32157 html : '<i class="fa fa-plus"></i>'
32168 cls : 'column col-md-12',
32173 if(this.fieldLabel.length){
32178 cls : 'column col-md-12',
32179 html : this.fieldLabel
32183 cls : 'column col-md-12',
32188 if(this.labelAlign == 'left'){
32193 html : this.fieldLabel
32202 if(this.labelWidth > 12){
32203 content[0].style = "width: " + this.labelWidth + 'px';
32206 if(this.labelWidth < 13 && this.labelmd == 0){
32207 this.labelmd = this.labelWidth;
32210 if(this.labellg > 0){
32211 content[0].cls += ' col-lg-' + this.labellg;
32212 content[1].cls += ' col-lg-' + (12 - this.labellg);
32215 if(this.labelmd > 0){
32216 content[0].cls += ' col-md-' + this.labelmd;
32217 content[1].cls += ' col-md-' + (12 - this.labelmd);
32220 if(this.labelsm > 0){
32221 content[0].cls += ' col-sm-' + this.labelsm;
32222 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32225 if(this.labelxs > 0){
32226 content[0].cls += ' col-xs-' + this.labelxs;
32227 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32235 cls : 'row clearfix',
32243 initEvents : function()
32245 this.managerEl = this.el.select('.roo-document-manager', true).first();
32246 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32248 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32249 this.selectorEl.hide();
32252 this.selectorEl.attr('multiple', 'multiple');
32255 this.selectorEl.on('change', this.onFileSelected, this);
32257 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32258 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32260 this.uploader.on('click', this.onUploaderClick, this);
32262 this.renderProgressDialog();
32266 window.addEventListener("resize", function() { _this.refresh(); } );
32268 this.fireEvent('initial', this);
32271 renderProgressDialog : function()
32275 this.progressDialog = new Roo.bootstrap.Modal({
32276 cls : 'roo-document-manager-progress-dialog',
32277 allow_close : false,
32288 btnclick : function() {
32289 _this.uploadCancel();
32295 this.progressDialog.render(Roo.get(document.body));
32297 this.progress = new Roo.bootstrap.Progress({
32298 cls : 'roo-document-manager-progress',
32303 this.progress.render(this.progressDialog.getChildContainer());
32305 this.progressBar = new Roo.bootstrap.ProgressBar({
32306 cls : 'roo-document-manager-progress-bar',
32309 aria_valuemax : 12,
32313 this.progressBar.render(this.progress.getChildContainer());
32316 onUploaderClick : function(e)
32318 e.preventDefault();
32320 if(this.fireEvent('beforeselectfile', this) != false){
32321 this.selectorEl.dom.click();
32326 onFileSelected : function(e)
32328 e.preventDefault();
32330 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32334 Roo.each(this.selectorEl.dom.files, function(file){
32335 if(this.fireEvent('inspect', this, file) != false){
32336 this.files.push(file);
32346 this.selectorEl.dom.value = '';
32348 if(!this.files || !this.files.length){
32352 if(this.boxes > 0 && this.files.length > this.boxes){
32353 this.files = this.files.slice(0, this.boxes);
32356 this.uploader.show();
32358 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32359 this.uploader.hide();
32368 Roo.each(this.files, function(file){
32370 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32371 var f = this.renderPreview(file);
32376 if(file.type.indexOf('image') != -1){
32377 this.delegates.push(
32379 _this.process(file);
32380 }).createDelegate(this)
32388 _this.process(file);
32389 }).createDelegate(this)
32394 this.files = files;
32396 this.delegates = this.delegates.concat(docs);
32398 if(!this.delegates.length){
32403 this.progressBar.aria_valuemax = this.delegates.length;
32410 arrange : function()
32412 if(!this.delegates.length){
32413 this.progressDialog.hide();
32418 var delegate = this.delegates.shift();
32420 this.progressDialog.show();
32422 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32424 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32429 refresh : function()
32431 this.uploader.show();
32433 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32434 this.uploader.hide();
32437 Roo.isTouch ? this.closable(false) : this.closable(true);
32439 this.fireEvent('refresh', this);
32442 onRemove : function(e, el, o)
32444 e.preventDefault();
32446 this.fireEvent('remove', this, o);
32450 remove : function(o)
32454 Roo.each(this.files, function(file){
32455 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32464 this.files = files;
32471 Roo.each(this.files, function(file){
32476 file.target.remove();
32485 onClick : function(e, el, o)
32487 e.preventDefault();
32489 this.fireEvent('click', this, o);
32493 closable : function(closable)
32495 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32497 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32509 xhrOnLoad : function(xhr)
32511 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32515 if (xhr.readyState !== 4) {
32517 this.fireEvent('exception', this, xhr);
32521 var response = Roo.decode(xhr.responseText);
32523 if(!response.success){
32525 this.fireEvent('exception', this, xhr);
32529 var file = this.renderPreview(response.data);
32531 this.files.push(file);
32535 this.fireEvent('afterupload', this, xhr);
32539 xhrOnError : function(xhr)
32541 Roo.log('xhr on error');
32543 var response = Roo.decode(xhr.responseText);
32550 process : function(file)
32552 if(this.fireEvent('process', this, file) !== false){
32553 if(this.editable && file.type.indexOf('image') != -1){
32554 this.fireEvent('edit', this, file);
32558 this.uploadStart(file, false);
32565 uploadStart : function(file, crop)
32567 this.xhr = new XMLHttpRequest();
32569 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32574 file.xhr = this.xhr;
32576 this.managerEl.createChild({
32578 cls : 'roo-document-manager-loading',
32582 tooltip : file.name,
32583 cls : 'roo-document-manager-thumb',
32584 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32590 this.xhr.open(this.method, this.url, true);
32593 "Accept": "application/json",
32594 "Cache-Control": "no-cache",
32595 "X-Requested-With": "XMLHttpRequest"
32598 for (var headerName in headers) {
32599 var headerValue = headers[headerName];
32601 this.xhr.setRequestHeader(headerName, headerValue);
32607 this.xhr.onload = function()
32609 _this.xhrOnLoad(_this.xhr);
32612 this.xhr.onerror = function()
32614 _this.xhrOnError(_this.xhr);
32617 var formData = new FormData();
32619 formData.append('returnHTML', 'NO');
32622 formData.append('crop', crop);
32625 formData.append(this.paramName, file, file.name);
32632 if(this.fireEvent('prepare', this, formData, options) != false){
32634 if(options.manually){
32638 this.xhr.send(formData);
32642 this.uploadCancel();
32645 uploadCancel : function()
32651 this.delegates = [];
32653 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32660 renderPreview : function(file)
32662 if(typeof(file.target) != 'undefined' && file.target){
32666 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32668 var previewEl = this.managerEl.createChild({
32670 cls : 'roo-document-manager-preview',
32674 tooltip : file[this.toolTipName],
32675 cls : 'roo-document-manager-thumb',
32676 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32681 html : '<i class="fa fa-times-circle"></i>'
32686 var close = previewEl.select('button.close', true).first();
32688 close.on('click', this.onRemove, this, file);
32690 file.target = previewEl;
32692 var image = previewEl.select('img', true).first();
32696 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32698 image.on('click', this.onClick, this, file);
32700 this.fireEvent('previewrendered', this, file);
32706 onPreviewLoad : function(file, image)
32708 if(typeof(file.target) == 'undefined' || !file.target){
32712 var width = image.dom.naturalWidth || image.dom.width;
32713 var height = image.dom.naturalHeight || image.dom.height;
32715 if(!this.previewResize) {
32719 if(width > height){
32720 file.target.addClass('wide');
32724 file.target.addClass('tall');
32729 uploadFromSource : function(file, crop)
32731 this.xhr = new XMLHttpRequest();
32733 this.managerEl.createChild({
32735 cls : 'roo-document-manager-loading',
32739 tooltip : file.name,
32740 cls : 'roo-document-manager-thumb',
32741 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32747 this.xhr.open(this.method, this.url, true);
32750 "Accept": "application/json",
32751 "Cache-Control": "no-cache",
32752 "X-Requested-With": "XMLHttpRequest"
32755 for (var headerName in headers) {
32756 var headerValue = headers[headerName];
32758 this.xhr.setRequestHeader(headerName, headerValue);
32764 this.xhr.onload = function()
32766 _this.xhrOnLoad(_this.xhr);
32769 this.xhr.onerror = function()
32771 _this.xhrOnError(_this.xhr);
32774 var formData = new FormData();
32776 formData.append('returnHTML', 'NO');
32778 formData.append('crop', crop);
32780 if(typeof(file.filename) != 'undefined'){
32781 formData.append('filename', file.filename);
32784 if(typeof(file.mimetype) != 'undefined'){
32785 formData.append('mimetype', file.mimetype);
32790 if(this.fireEvent('prepare', this, formData) != false){
32791 this.xhr.send(formData);
32801 * @class Roo.bootstrap.DocumentViewer
32802 * @extends Roo.bootstrap.Component
32803 * Bootstrap DocumentViewer class
32804 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32805 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32808 * Create a new DocumentViewer
32809 * @param {Object} config The config object
32812 Roo.bootstrap.DocumentViewer = function(config){
32813 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32818 * Fire after initEvent
32819 * @param {Roo.bootstrap.DocumentViewer} this
32825 * @param {Roo.bootstrap.DocumentViewer} this
32830 * Fire after download button
32831 * @param {Roo.bootstrap.DocumentViewer} this
32836 * Fire after trash button
32837 * @param {Roo.bootstrap.DocumentViewer} this
32844 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32846 showDownload : true,
32850 getAutoCreate : function()
32854 cls : 'roo-document-viewer',
32858 cls : 'roo-document-viewer-body',
32862 cls : 'roo-document-viewer-thumb',
32866 cls : 'roo-document-viewer-image'
32874 cls : 'roo-document-viewer-footer',
32877 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32881 cls : 'btn-group roo-document-viewer-download',
32885 cls : 'btn btn-default',
32886 html : '<i class="fa fa-download"></i>'
32892 cls : 'btn-group roo-document-viewer-trash',
32896 cls : 'btn btn-default',
32897 html : '<i class="fa fa-trash"></i>'
32910 initEvents : function()
32912 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32913 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32915 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32916 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32918 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32919 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32921 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32922 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32924 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32925 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32927 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32928 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32930 this.bodyEl.on('click', this.onClick, this);
32931 this.downloadBtn.on('click', this.onDownload, this);
32932 this.trashBtn.on('click', this.onTrash, this);
32934 this.downloadBtn.hide();
32935 this.trashBtn.hide();
32937 if(this.showDownload){
32938 this.downloadBtn.show();
32941 if(this.showTrash){
32942 this.trashBtn.show();
32945 if(!this.showDownload && !this.showTrash) {
32946 this.footerEl.hide();
32951 initial : function()
32953 this.fireEvent('initial', this);
32957 onClick : function(e)
32959 e.preventDefault();
32961 this.fireEvent('click', this);
32964 onDownload : function(e)
32966 e.preventDefault();
32968 this.fireEvent('download', this);
32971 onTrash : function(e)
32973 e.preventDefault();
32975 this.fireEvent('trash', this);
32987 * @class Roo.bootstrap.NavProgressBar
32988 * @extends Roo.bootstrap.Component
32989 * Bootstrap NavProgressBar class
32992 * Create a new nav progress bar
32993 * @param {Object} config The config object
32996 Roo.bootstrap.NavProgressBar = function(config){
32997 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32999 this.bullets = this.bullets || [];
33001 // Roo.bootstrap.NavProgressBar.register(this);
33005 * Fires when the active item changes
33006 * @param {Roo.bootstrap.NavProgressBar} this
33007 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33008 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33015 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33020 getAutoCreate : function()
33022 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33026 cls : 'roo-navigation-bar-group',
33030 cls : 'roo-navigation-top-bar'
33034 cls : 'roo-navigation-bullets-bar',
33038 cls : 'roo-navigation-bar'
33045 cls : 'roo-navigation-bottom-bar'
33055 initEvents: function()
33060 onRender : function(ct, position)
33062 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33064 if(this.bullets.length){
33065 Roo.each(this.bullets, function(b){
33074 addItem : function(cfg)
33076 var item = new Roo.bootstrap.NavProgressItem(cfg);
33078 item.parentId = this.id;
33079 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33082 var top = new Roo.bootstrap.Element({
33084 cls : 'roo-navigation-bar-text'
33087 var bottom = new Roo.bootstrap.Element({
33089 cls : 'roo-navigation-bar-text'
33092 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33093 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33095 var topText = new Roo.bootstrap.Element({
33097 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33100 var bottomText = new Roo.bootstrap.Element({
33102 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33105 topText.onRender(top.el, null);
33106 bottomText.onRender(bottom.el, null);
33109 item.bottomEl = bottom;
33112 this.barItems.push(item);
33117 getActive : function()
33119 var active = false;
33121 Roo.each(this.barItems, function(v){
33123 if (!v.isActive()) {
33135 setActiveItem : function(item)
33139 Roo.each(this.barItems, function(v){
33140 if (v.rid == item.rid) {
33144 if (v.isActive()) {
33145 v.setActive(false);
33150 item.setActive(true);
33152 this.fireEvent('changed', this, item, prev);
33155 getBarItem: function(rid)
33159 Roo.each(this.barItems, function(e) {
33160 if (e.rid != rid) {
33171 indexOfItem : function(item)
33175 Roo.each(this.barItems, function(v, i){
33177 if (v.rid != item.rid) {
33188 setActiveNext : function()
33190 var i = this.indexOfItem(this.getActive());
33192 if (i > this.barItems.length) {
33196 this.setActiveItem(this.barItems[i+1]);
33199 setActivePrev : function()
33201 var i = this.indexOfItem(this.getActive());
33207 this.setActiveItem(this.barItems[i-1]);
33210 format : function()
33212 if(!this.barItems.length){
33216 var width = 100 / this.barItems.length;
33218 Roo.each(this.barItems, function(i){
33219 i.el.setStyle('width', width + '%');
33220 i.topEl.el.setStyle('width', width + '%');
33221 i.bottomEl.el.setStyle('width', width + '%');
33230 * Nav Progress Item
33235 * @class Roo.bootstrap.NavProgressItem
33236 * @extends Roo.bootstrap.Component
33237 * Bootstrap NavProgressItem class
33238 * @cfg {String} rid the reference id
33239 * @cfg {Boolean} active (true|false) Is item active default false
33240 * @cfg {Boolean} disabled (true|false) Is item active default false
33241 * @cfg {String} html
33242 * @cfg {String} position (top|bottom) text position default bottom
33243 * @cfg {String} icon show icon instead of number
33246 * Create a new NavProgressItem
33247 * @param {Object} config The config object
33249 Roo.bootstrap.NavProgressItem = function(config){
33250 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33255 * The raw click event for the entire grid.
33256 * @param {Roo.bootstrap.NavProgressItem} this
33257 * @param {Roo.EventObject} e
33264 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33270 position : 'bottom',
33273 getAutoCreate : function()
33275 var iconCls = 'roo-navigation-bar-item-icon';
33277 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33281 cls: 'roo-navigation-bar-item',
33291 cfg.cls += ' active';
33294 cfg.cls += ' disabled';
33300 disable : function()
33302 this.setDisabled(true);
33305 enable : function()
33307 this.setDisabled(false);
33310 initEvents: function()
33312 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33314 this.iconEl.on('click', this.onClick, this);
33317 onClick : function(e)
33319 e.preventDefault();
33325 if(this.fireEvent('click', this, e) === false){
33329 this.parent().setActiveItem(this);
33332 isActive: function ()
33334 return this.active;
33337 setActive : function(state)
33339 if(this.active == state){
33343 this.active = state;
33346 this.el.addClass('active');
33350 this.el.removeClass('active');
33355 setDisabled : function(state)
33357 if(this.disabled == state){
33361 this.disabled = state;
33364 this.el.addClass('disabled');
33368 this.el.removeClass('disabled');
33371 tooltipEl : function()
33373 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33386 * @class Roo.bootstrap.FieldLabel
33387 * @extends Roo.bootstrap.Component
33388 * Bootstrap FieldLabel class
33389 * @cfg {String} html contents of the element
33390 * @cfg {String} tag tag of the element default label
33391 * @cfg {String} cls class of the element
33392 * @cfg {String} target label target
33393 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33394 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33395 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33396 * @cfg {String} iconTooltip default "This field is required"
33397 * @cfg {String} indicatorpos (left|right) default left
33400 * Create a new FieldLabel
33401 * @param {Object} config The config object
33404 Roo.bootstrap.FieldLabel = function(config){
33405 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33410 * Fires after the field has been marked as invalid.
33411 * @param {Roo.form.FieldLabel} this
33412 * @param {String} msg The validation message
33417 * Fires after the field has been validated with no errors.
33418 * @param {Roo.form.FieldLabel} this
33424 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33431 invalidClass : 'has-warning',
33432 validClass : 'has-success',
33433 iconTooltip : 'This field is required',
33434 indicatorpos : 'left',
33436 getAutoCreate : function(){
33439 if (!this.allowBlank) {
33445 cls : 'roo-bootstrap-field-label ' + this.cls,
33450 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33451 tooltip : this.iconTooltip
33460 if(this.indicatorpos == 'right'){
33463 cls : 'roo-bootstrap-field-label ' + this.cls,
33472 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33473 tooltip : this.iconTooltip
33482 initEvents: function()
33484 Roo.bootstrap.Element.superclass.initEvents.call(this);
33486 this.indicator = this.indicatorEl();
33488 if(this.indicator){
33489 this.indicator.removeClass('visible');
33490 this.indicator.addClass('invisible');
33493 Roo.bootstrap.FieldLabel.register(this);
33496 indicatorEl : function()
33498 var indicator = this.el.select('i.roo-required-indicator',true).first();
33509 * Mark this field as valid
33511 markValid : function()
33513 if(this.indicator){
33514 this.indicator.removeClass('visible');
33515 this.indicator.addClass('invisible');
33517 if (Roo.bootstrap.version == 3) {
33518 this.el.removeClass(this.invalidClass);
33519 this.el.addClass(this.validClass);
33521 this.el.removeClass('is-invalid');
33522 this.el.addClass('is-valid');
33526 this.fireEvent('valid', this);
33530 * Mark this field as invalid
33531 * @param {String} msg The validation message
33533 markInvalid : function(msg)
33535 if(this.indicator){
33536 this.indicator.removeClass('invisible');
33537 this.indicator.addClass('visible');
33539 if (Roo.bootstrap.version == 3) {
33540 this.el.removeClass(this.validClass);
33541 this.el.addClass(this.invalidClass);
33543 this.el.removeClass('is-valid');
33544 this.el.addClass('is-invalid');
33548 this.fireEvent('invalid', this, msg);
33554 Roo.apply(Roo.bootstrap.FieldLabel, {
33559 * register a FieldLabel Group
33560 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33562 register : function(label)
33564 if(this.groups.hasOwnProperty(label.target)){
33568 this.groups[label.target] = label;
33572 * fetch a FieldLabel Group based on the target
33573 * @param {string} target
33574 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33576 get: function(target) {
33577 if (typeof(this.groups[target]) == 'undefined') {
33581 return this.groups[target] ;
33590 * page DateSplitField.
33596 * @class Roo.bootstrap.DateSplitField
33597 * @extends Roo.bootstrap.Component
33598 * Bootstrap DateSplitField class
33599 * @cfg {string} fieldLabel - the label associated
33600 * @cfg {Number} labelWidth set the width of label (0-12)
33601 * @cfg {String} labelAlign (top|left)
33602 * @cfg {Boolean} dayAllowBlank (true|false) default false
33603 * @cfg {Boolean} monthAllowBlank (true|false) default false
33604 * @cfg {Boolean} yearAllowBlank (true|false) default false
33605 * @cfg {string} dayPlaceholder
33606 * @cfg {string} monthPlaceholder
33607 * @cfg {string} yearPlaceholder
33608 * @cfg {string} dayFormat default 'd'
33609 * @cfg {string} monthFormat default 'm'
33610 * @cfg {string} yearFormat default 'Y'
33611 * @cfg {Number} labellg set the width of label (1-12)
33612 * @cfg {Number} labelmd set the width of label (1-12)
33613 * @cfg {Number} labelsm set the width of label (1-12)
33614 * @cfg {Number} labelxs set the width of label (1-12)
33618 * Create a new DateSplitField
33619 * @param {Object} config The config object
33622 Roo.bootstrap.DateSplitField = function(config){
33623 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33629 * getting the data of years
33630 * @param {Roo.bootstrap.DateSplitField} this
33631 * @param {Object} years
33636 * getting the data of days
33637 * @param {Roo.bootstrap.DateSplitField} this
33638 * @param {Object} days
33643 * Fires after the field has been marked as invalid.
33644 * @param {Roo.form.Field} this
33645 * @param {String} msg The validation message
33650 * Fires after the field has been validated with no errors.
33651 * @param {Roo.form.Field} this
33657 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33660 labelAlign : 'top',
33662 dayAllowBlank : false,
33663 monthAllowBlank : false,
33664 yearAllowBlank : false,
33665 dayPlaceholder : '',
33666 monthPlaceholder : '',
33667 yearPlaceholder : '',
33671 isFormField : true,
33677 getAutoCreate : function()
33681 cls : 'row roo-date-split-field-group',
33686 cls : 'form-hidden-field roo-date-split-field-group-value',
33692 var labelCls = 'col-md-12';
33693 var contentCls = 'col-md-4';
33695 if(this.fieldLabel){
33699 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33703 html : this.fieldLabel
33708 if(this.labelAlign == 'left'){
33710 if(this.labelWidth > 12){
33711 label.style = "width: " + this.labelWidth + 'px';
33714 if(this.labelWidth < 13 && this.labelmd == 0){
33715 this.labelmd = this.labelWidth;
33718 if(this.labellg > 0){
33719 labelCls = ' col-lg-' + this.labellg;
33720 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33723 if(this.labelmd > 0){
33724 labelCls = ' col-md-' + this.labelmd;
33725 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33728 if(this.labelsm > 0){
33729 labelCls = ' col-sm-' + this.labelsm;
33730 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33733 if(this.labelxs > 0){
33734 labelCls = ' col-xs-' + this.labelxs;
33735 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33739 label.cls += ' ' + labelCls;
33741 cfg.cn.push(label);
33744 Roo.each(['day', 'month', 'year'], function(t){
33747 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33754 inputEl: function ()
33756 return this.el.select('.roo-date-split-field-group-value', true).first();
33759 onRender : function(ct, position)
33763 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33765 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33767 this.dayField = new Roo.bootstrap.ComboBox({
33768 allowBlank : this.dayAllowBlank,
33769 alwaysQuery : true,
33770 displayField : 'value',
33773 forceSelection : true,
33775 placeholder : this.dayPlaceholder,
33776 selectOnFocus : true,
33777 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33778 triggerAction : 'all',
33780 valueField : 'value',
33781 store : new Roo.data.SimpleStore({
33782 data : (function() {
33784 _this.fireEvent('days', _this, days);
33787 fields : [ 'value' ]
33790 select : function (_self, record, index)
33792 _this.setValue(_this.getValue());
33797 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33799 this.monthField = new Roo.bootstrap.MonthField({
33800 after : '<i class=\"fa fa-calendar\"></i>',
33801 allowBlank : this.monthAllowBlank,
33802 placeholder : this.monthPlaceholder,
33805 render : function (_self)
33807 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33808 e.preventDefault();
33812 select : function (_self, oldvalue, newvalue)
33814 _this.setValue(_this.getValue());
33819 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33821 this.yearField = new Roo.bootstrap.ComboBox({
33822 allowBlank : this.yearAllowBlank,
33823 alwaysQuery : true,
33824 displayField : 'value',
33827 forceSelection : true,
33829 placeholder : this.yearPlaceholder,
33830 selectOnFocus : true,
33831 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33832 triggerAction : 'all',
33834 valueField : 'value',
33835 store : new Roo.data.SimpleStore({
33836 data : (function() {
33838 _this.fireEvent('years', _this, years);
33841 fields : [ 'value' ]
33844 select : function (_self, record, index)
33846 _this.setValue(_this.getValue());
33851 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33854 setValue : function(v, format)
33856 this.inputEl.dom.value = v;
33858 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33860 var d = Date.parseDate(v, f);
33867 this.setDay(d.format(this.dayFormat));
33868 this.setMonth(d.format(this.monthFormat));
33869 this.setYear(d.format(this.yearFormat));
33876 setDay : function(v)
33878 this.dayField.setValue(v);
33879 this.inputEl.dom.value = this.getValue();
33884 setMonth : function(v)
33886 this.monthField.setValue(v, true);
33887 this.inputEl.dom.value = this.getValue();
33892 setYear : function(v)
33894 this.yearField.setValue(v);
33895 this.inputEl.dom.value = this.getValue();
33900 getDay : function()
33902 return this.dayField.getValue();
33905 getMonth : function()
33907 return this.monthField.getValue();
33910 getYear : function()
33912 return this.yearField.getValue();
33915 getValue : function()
33917 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33919 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33929 this.inputEl.dom.value = '';
33934 validate : function()
33936 var d = this.dayField.validate();
33937 var m = this.monthField.validate();
33938 var y = this.yearField.validate();
33943 (!this.dayAllowBlank && !d) ||
33944 (!this.monthAllowBlank && !m) ||
33945 (!this.yearAllowBlank && !y)
33950 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33959 this.markInvalid();
33964 markValid : function()
33967 var label = this.el.select('label', true).first();
33968 var icon = this.el.select('i.fa-star', true).first();
33974 this.fireEvent('valid', this);
33978 * Mark this field as invalid
33979 * @param {String} msg The validation message
33981 markInvalid : function(msg)
33984 var label = this.el.select('label', true).first();
33985 var icon = this.el.select('i.fa-star', true).first();
33987 if(label && !icon){
33988 this.el.select('.roo-date-split-field-label', true).createChild({
33990 cls : 'text-danger fa fa-lg fa-star',
33991 tooltip : 'This field is required',
33992 style : 'margin-right:5px;'
33996 this.fireEvent('invalid', this, msg);
33999 clearInvalid : function()
34001 var label = this.el.select('label', true).first();
34002 var icon = this.el.select('i.fa-star', true).first();
34008 this.fireEvent('valid', this);
34011 getName: function()
34021 * http://masonry.desandro.com
34023 * The idea is to render all the bricks based on vertical width...
34025 * The original code extends 'outlayer' - we might need to use that....
34031 * @class Roo.bootstrap.LayoutMasonry
34032 * @extends Roo.bootstrap.Component
34033 * Bootstrap Layout Masonry class
34036 * Create a new Element
34037 * @param {Object} config The config object
34040 Roo.bootstrap.LayoutMasonry = function(config){
34042 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34046 Roo.bootstrap.LayoutMasonry.register(this);
34052 * Fire after layout the items
34053 * @param {Roo.bootstrap.LayoutMasonry} this
34054 * @param {Roo.EventObject} e
34061 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34064 * @cfg {Boolean} isLayoutInstant = no animation?
34066 isLayoutInstant : false, // needed?
34069 * @cfg {Number} boxWidth width of the columns
34074 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34079 * @cfg {Number} padWidth padding below box..
34084 * @cfg {Number} gutter gutter width..
34089 * @cfg {Number} maxCols maximum number of columns
34095 * @cfg {Boolean} isAutoInitial defalut true
34097 isAutoInitial : true,
34102 * @cfg {Boolean} isHorizontal defalut false
34104 isHorizontal : false,
34106 currentSize : null,
34112 bricks: null, //CompositeElement
34116 _isLayoutInited : false,
34118 // isAlternative : false, // only use for vertical layout...
34121 * @cfg {Number} alternativePadWidth padding below box..
34123 alternativePadWidth : 50,
34125 selectedBrick : [],
34127 getAutoCreate : function(){
34129 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34133 cls: 'blog-masonary-wrapper ' + this.cls,
34135 cls : 'mas-boxes masonary'
34142 getChildContainer: function( )
34144 if (this.boxesEl) {
34145 return this.boxesEl;
34148 this.boxesEl = this.el.select('.mas-boxes').first();
34150 return this.boxesEl;
34154 initEvents : function()
34158 if(this.isAutoInitial){
34159 Roo.log('hook children rendered');
34160 this.on('childrenrendered', function() {
34161 Roo.log('children rendered');
34167 initial : function()
34169 this.selectedBrick = [];
34171 this.currentSize = this.el.getBox(true);
34173 Roo.EventManager.onWindowResize(this.resize, this);
34175 if(!this.isAutoInitial){
34183 //this.layout.defer(500,this);
34187 resize : function()
34189 var cs = this.el.getBox(true);
34192 this.currentSize.width == cs.width &&
34193 this.currentSize.x == cs.x &&
34194 this.currentSize.height == cs.height &&
34195 this.currentSize.y == cs.y
34197 Roo.log("no change in with or X or Y");
34201 this.currentSize = cs;
34207 layout : function()
34209 this._resetLayout();
34211 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34213 this.layoutItems( isInstant );
34215 this._isLayoutInited = true;
34217 this.fireEvent('layout', this);
34221 _resetLayout : function()
34223 if(this.isHorizontal){
34224 this.horizontalMeasureColumns();
34228 this.verticalMeasureColumns();
34232 verticalMeasureColumns : function()
34234 this.getContainerWidth();
34236 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34237 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34241 var boxWidth = this.boxWidth + this.padWidth;
34243 if(this.containerWidth < this.boxWidth){
34244 boxWidth = this.containerWidth
34247 var containerWidth = this.containerWidth;
34249 var cols = Math.floor(containerWidth / boxWidth);
34251 this.cols = Math.max( cols, 1 );
34253 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34255 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34257 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34259 this.colWidth = boxWidth + avail - this.padWidth;
34261 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34262 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34265 horizontalMeasureColumns : function()
34267 this.getContainerWidth();
34269 var boxWidth = this.boxWidth;
34271 if(this.containerWidth < boxWidth){
34272 boxWidth = this.containerWidth;
34275 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34277 this.el.setHeight(boxWidth);
34281 getContainerWidth : function()
34283 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34286 layoutItems : function( isInstant )
34288 Roo.log(this.bricks);
34290 var items = Roo.apply([], this.bricks);
34292 if(this.isHorizontal){
34293 this._horizontalLayoutItems( items , isInstant );
34297 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34298 // this._verticalAlternativeLayoutItems( items , isInstant );
34302 this._verticalLayoutItems( items , isInstant );
34306 _verticalLayoutItems : function ( items , isInstant)
34308 if ( !items || !items.length ) {
34313 ['xs', 'xs', 'xs', 'tall'],
34314 ['xs', 'xs', 'tall'],
34315 ['xs', 'xs', 'sm'],
34316 ['xs', 'xs', 'xs'],
34322 ['sm', 'xs', 'xs'],
34326 ['tall', 'xs', 'xs', 'xs'],
34327 ['tall', 'xs', 'xs'],
34339 Roo.each(items, function(item, k){
34341 switch (item.size) {
34342 // these layouts take up a full box,
34353 boxes.push([item]);
34376 var filterPattern = function(box, length)
34384 var pattern = box.slice(0, length);
34388 Roo.each(pattern, function(i){
34389 format.push(i.size);
34392 Roo.each(standard, function(s){
34394 if(String(s) != String(format)){
34403 if(!match && length == 1){
34408 filterPattern(box, length - 1);
34412 queue.push(pattern);
34414 box = box.slice(length, box.length);
34416 filterPattern(box, 4);
34422 Roo.each(boxes, function(box, k){
34428 if(box.length == 1){
34433 filterPattern(box, 4);
34437 this._processVerticalLayoutQueue( queue, isInstant );
34441 // _verticalAlternativeLayoutItems : function( items , isInstant )
34443 // if ( !items || !items.length ) {
34447 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34451 _horizontalLayoutItems : function ( items , isInstant)
34453 if ( !items || !items.length || items.length < 3) {
34459 var eItems = items.slice(0, 3);
34461 items = items.slice(3, items.length);
34464 ['xs', 'xs', 'xs', 'wide'],
34465 ['xs', 'xs', 'wide'],
34466 ['xs', 'xs', 'sm'],
34467 ['xs', 'xs', 'xs'],
34473 ['sm', 'xs', 'xs'],
34477 ['wide', 'xs', 'xs', 'xs'],
34478 ['wide', 'xs', 'xs'],
34491 Roo.each(items, function(item, k){
34493 switch (item.size) {
34504 boxes.push([item]);
34528 var filterPattern = function(box, length)
34536 var pattern = box.slice(0, length);
34540 Roo.each(pattern, function(i){
34541 format.push(i.size);
34544 Roo.each(standard, function(s){
34546 if(String(s) != String(format)){
34555 if(!match && length == 1){
34560 filterPattern(box, length - 1);
34564 queue.push(pattern);
34566 box = box.slice(length, box.length);
34568 filterPattern(box, 4);
34574 Roo.each(boxes, function(box, k){
34580 if(box.length == 1){
34585 filterPattern(box, 4);
34592 var pos = this.el.getBox(true);
34596 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34598 var hit_end = false;
34600 Roo.each(queue, function(box){
34604 Roo.each(box, function(b){
34606 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34616 Roo.each(box, function(b){
34618 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34621 mx = Math.max(mx, b.x);
34625 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34629 Roo.each(box, function(b){
34631 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34645 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34648 /** Sets position of item in DOM
34649 * @param {Element} item
34650 * @param {Number} x - horizontal position
34651 * @param {Number} y - vertical position
34652 * @param {Boolean} isInstant - disables transitions
34654 _processVerticalLayoutQueue : function( queue, isInstant )
34656 var pos = this.el.getBox(true);
34661 for (var i = 0; i < this.cols; i++){
34665 Roo.each(queue, function(box, k){
34667 var col = k % this.cols;
34669 Roo.each(box, function(b,kk){
34671 b.el.position('absolute');
34673 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34674 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34676 if(b.size == 'md-left' || b.size == 'md-right'){
34677 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34678 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34681 b.el.setWidth(width);
34682 b.el.setHeight(height);
34684 b.el.select('iframe',true).setSize(width,height);
34688 for (var i = 0; i < this.cols; i++){
34690 if(maxY[i] < maxY[col]){
34695 col = Math.min(col, i);
34699 x = pos.x + col * (this.colWidth + this.padWidth);
34703 var positions = [];
34705 switch (box.length){
34707 positions = this.getVerticalOneBoxColPositions(x, y, box);
34710 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34713 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34716 positions = this.getVerticalFourBoxColPositions(x, y, box);
34722 Roo.each(box, function(b,kk){
34724 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34726 var sz = b.el.getSize();
34728 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34736 for (var i = 0; i < this.cols; i++){
34737 mY = Math.max(mY, maxY[i]);
34740 this.el.setHeight(mY - pos.y);
34744 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34746 // var pos = this.el.getBox(true);
34749 // var maxX = pos.right;
34751 // var maxHeight = 0;
34753 // Roo.each(items, function(item, k){
34757 // item.el.position('absolute');
34759 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34761 // item.el.setWidth(width);
34763 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34765 // item.el.setHeight(height);
34768 // item.el.setXY([x, y], isInstant ? false : true);
34770 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34773 // y = y + height + this.alternativePadWidth;
34775 // maxHeight = maxHeight + height + this.alternativePadWidth;
34779 // this.el.setHeight(maxHeight);
34783 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34785 var pos = this.el.getBox(true);
34790 var maxX = pos.right;
34792 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34794 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34796 Roo.each(queue, function(box, k){
34798 Roo.each(box, function(b, kk){
34800 b.el.position('absolute');
34802 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34803 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34805 if(b.size == 'md-left' || b.size == 'md-right'){
34806 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34807 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34810 b.el.setWidth(width);
34811 b.el.setHeight(height);
34819 var positions = [];
34821 switch (box.length){
34823 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34826 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34829 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34832 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34838 Roo.each(box, function(b,kk){
34840 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34842 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34850 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34852 Roo.each(eItems, function(b,k){
34854 b.size = (k == 0) ? 'sm' : 'xs';
34855 b.x = (k == 0) ? 2 : 1;
34856 b.y = (k == 0) ? 2 : 1;
34858 b.el.position('absolute');
34860 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34862 b.el.setWidth(width);
34864 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34866 b.el.setHeight(height);
34870 var positions = [];
34873 x : maxX - this.unitWidth * 2 - this.gutter,
34878 x : maxX - this.unitWidth,
34879 y : minY + (this.unitWidth + this.gutter) * 2
34883 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34887 Roo.each(eItems, function(b,k){
34889 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34895 getVerticalOneBoxColPositions : function(x, y, box)
34899 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34901 if(box[0].size == 'md-left'){
34905 if(box[0].size == 'md-right'){
34910 x : x + (this.unitWidth + this.gutter) * rand,
34917 getVerticalTwoBoxColPositions : function(x, y, box)
34921 if(box[0].size == 'xs'){
34925 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34929 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34943 x : x + (this.unitWidth + this.gutter) * 2,
34944 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34951 getVerticalThreeBoxColPositions : function(x, y, box)
34955 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34963 x : x + (this.unitWidth + this.gutter) * 1,
34968 x : x + (this.unitWidth + this.gutter) * 2,
34976 if(box[0].size == 'xs' && box[1].size == 'xs'){
34985 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34989 x : x + (this.unitWidth + this.gutter) * 1,
35003 x : x + (this.unitWidth + this.gutter) * 2,
35008 x : x + (this.unitWidth + this.gutter) * 2,
35009 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35016 getVerticalFourBoxColPositions : function(x, y, box)
35020 if(box[0].size == 'xs'){
35029 y : y + (this.unitHeight + this.gutter) * 1
35034 y : y + (this.unitHeight + this.gutter) * 2
35038 x : x + (this.unitWidth + this.gutter) * 1,
35052 x : x + (this.unitWidth + this.gutter) * 2,
35057 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35058 y : y + (this.unitHeight + this.gutter) * 1
35062 x : x + (this.unitWidth + this.gutter) * 2,
35063 y : y + (this.unitWidth + this.gutter) * 2
35070 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35074 if(box[0].size == 'md-left'){
35076 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35083 if(box[0].size == 'md-right'){
35085 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35086 y : minY + (this.unitWidth + this.gutter) * 1
35092 var rand = Math.floor(Math.random() * (4 - box[0].y));
35095 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35096 y : minY + (this.unitWidth + this.gutter) * rand
35103 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35107 if(box[0].size == 'xs'){
35110 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35115 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35116 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35124 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35129 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35130 y : minY + (this.unitWidth + this.gutter) * 2
35137 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35141 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35144 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35149 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35150 y : minY + (this.unitWidth + this.gutter) * 1
35154 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35155 y : minY + (this.unitWidth + this.gutter) * 2
35162 if(box[0].size == 'xs' && box[1].size == 'xs'){
35165 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35170 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35175 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35176 y : minY + (this.unitWidth + this.gutter) * 1
35184 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35189 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35190 y : minY + (this.unitWidth + this.gutter) * 2
35194 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35195 y : minY + (this.unitWidth + this.gutter) * 2
35202 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35206 if(box[0].size == 'xs'){
35209 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35214 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35219 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),
35224 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35225 y : minY + (this.unitWidth + this.gutter) * 1
35233 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35238 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35239 y : minY + (this.unitWidth + this.gutter) * 2
35243 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35244 y : minY + (this.unitWidth + this.gutter) * 2
35248 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),
35249 y : minY + (this.unitWidth + this.gutter) * 2
35257 * remove a Masonry Brick
35258 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35260 removeBrick : function(brick_id)
35266 for (var i = 0; i<this.bricks.length; i++) {
35267 if (this.bricks[i].id == brick_id) {
35268 this.bricks.splice(i,1);
35269 this.el.dom.removeChild(Roo.get(brick_id).dom);
35276 * adds a Masonry Brick
35277 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35279 addBrick : function(cfg)
35281 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35282 //this.register(cn);
35283 cn.parentId = this.id;
35284 cn.render(this.el);
35289 * register a Masonry Brick
35290 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35293 register : function(brick)
35295 this.bricks.push(brick);
35296 brick.masonryId = this.id;
35300 * clear all the Masonry Brick
35302 clearAll : function()
35305 //this.getChildContainer().dom.innerHTML = "";
35306 this.el.dom.innerHTML = '';
35309 getSelected : function()
35311 if (!this.selectedBrick) {
35315 return this.selectedBrick;
35319 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35323 * register a Masonry Layout
35324 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35327 register : function(layout)
35329 this.groups[layout.id] = layout;
35332 * fetch a Masonry Layout based on the masonry layout ID
35333 * @param {string} the masonry layout to add
35334 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35337 get: function(layout_id) {
35338 if (typeof(this.groups[layout_id]) == 'undefined') {
35341 return this.groups[layout_id] ;
35353 * http://masonry.desandro.com
35355 * The idea is to render all the bricks based on vertical width...
35357 * The original code extends 'outlayer' - we might need to use that....
35363 * @class Roo.bootstrap.LayoutMasonryAuto
35364 * @extends Roo.bootstrap.Component
35365 * Bootstrap Layout Masonry class
35368 * Create a new Element
35369 * @param {Object} config The config object
35372 Roo.bootstrap.LayoutMasonryAuto = function(config){
35373 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35376 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35379 * @cfg {Boolean} isFitWidth - resize the width..
35381 isFitWidth : false, // options..
35383 * @cfg {Boolean} isOriginLeft = left align?
35385 isOriginLeft : true,
35387 * @cfg {Boolean} isOriginTop = top align?
35389 isOriginTop : false,
35391 * @cfg {Boolean} isLayoutInstant = no animation?
35393 isLayoutInstant : false, // needed?
35395 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35397 isResizingContainer : true,
35399 * @cfg {Number} columnWidth width of the columns
35405 * @cfg {Number} maxCols maximum number of columns
35410 * @cfg {Number} padHeight padding below box..
35416 * @cfg {Boolean} isAutoInitial defalut true
35419 isAutoInitial : true,
35425 initialColumnWidth : 0,
35426 currentSize : null,
35428 colYs : null, // array.
35435 bricks: null, //CompositeElement
35436 cols : 0, // array?
35437 // element : null, // wrapped now this.el
35438 _isLayoutInited : null,
35441 getAutoCreate : function(){
35445 cls: 'blog-masonary-wrapper ' + this.cls,
35447 cls : 'mas-boxes masonary'
35454 getChildContainer: function( )
35456 if (this.boxesEl) {
35457 return this.boxesEl;
35460 this.boxesEl = this.el.select('.mas-boxes').first();
35462 return this.boxesEl;
35466 initEvents : function()
35470 if(this.isAutoInitial){
35471 Roo.log('hook children rendered');
35472 this.on('childrenrendered', function() {
35473 Roo.log('children rendered');
35480 initial : function()
35482 this.reloadItems();
35484 this.currentSize = this.el.getBox(true);
35486 /// was window resize... - let's see if this works..
35487 Roo.EventManager.onWindowResize(this.resize, this);
35489 if(!this.isAutoInitial){
35494 this.layout.defer(500,this);
35497 reloadItems: function()
35499 this.bricks = this.el.select('.masonry-brick', true);
35501 this.bricks.each(function(b) {
35502 //Roo.log(b.getSize());
35503 if (!b.attr('originalwidth')) {
35504 b.attr('originalwidth', b.getSize().width);
35509 Roo.log(this.bricks.elements.length);
35512 resize : function()
35515 var cs = this.el.getBox(true);
35517 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35518 Roo.log("no change in with or X");
35521 this.currentSize = cs;
35525 layout : function()
35528 this._resetLayout();
35529 //this._manageStamps();
35531 // don't animate first layout
35532 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35533 this.layoutItems( isInstant );
35535 // flag for initalized
35536 this._isLayoutInited = true;
35539 layoutItems : function( isInstant )
35541 //var items = this._getItemsForLayout( this.items );
35542 // original code supports filtering layout items.. we just ignore it..
35544 this._layoutItems( this.bricks , isInstant );
35546 this._postLayout();
35548 _layoutItems : function ( items , isInstant)
35550 //this.fireEvent( 'layout', this, items );
35553 if ( !items || !items.elements.length ) {
35554 // no items, emit event with empty array
35559 items.each(function(item) {
35560 Roo.log("layout item");
35562 // get x/y object from method
35563 var position = this._getItemLayoutPosition( item );
35565 position.item = item;
35566 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35567 queue.push( position );
35570 this._processLayoutQueue( queue );
35572 /** Sets position of item in DOM
35573 * @param {Element} item
35574 * @param {Number} x - horizontal position
35575 * @param {Number} y - vertical position
35576 * @param {Boolean} isInstant - disables transitions
35578 _processLayoutQueue : function( queue )
35580 for ( var i=0, len = queue.length; i < len; i++ ) {
35581 var obj = queue[i];
35582 obj.item.position('absolute');
35583 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35589 * Any logic you want to do after each layout,
35590 * i.e. size the container
35592 _postLayout : function()
35594 this.resizeContainer();
35597 resizeContainer : function()
35599 if ( !this.isResizingContainer ) {
35602 var size = this._getContainerSize();
35604 this.el.setSize(size.width,size.height);
35605 this.boxesEl.setSize(size.width,size.height);
35611 _resetLayout : function()
35613 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35614 this.colWidth = this.el.getWidth();
35615 //this.gutter = this.el.getWidth();
35617 this.measureColumns();
35623 this.colYs.push( 0 );
35629 measureColumns : function()
35631 this.getContainerWidth();
35632 // if columnWidth is 0, default to outerWidth of first item
35633 if ( !this.columnWidth ) {
35634 var firstItem = this.bricks.first();
35635 Roo.log(firstItem);
35636 this.columnWidth = this.containerWidth;
35637 if (firstItem && firstItem.attr('originalwidth') ) {
35638 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35640 // columnWidth fall back to item of first element
35641 Roo.log("set column width?");
35642 this.initialColumnWidth = this.columnWidth ;
35644 // if first elem has no width, default to size of container
35649 if (this.initialColumnWidth) {
35650 this.columnWidth = this.initialColumnWidth;
35655 // column width is fixed at the top - however if container width get's smaller we should
35658 // this bit calcs how man columns..
35660 var columnWidth = this.columnWidth += this.gutter;
35662 // calculate columns
35663 var containerWidth = this.containerWidth + this.gutter;
35665 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35666 // fix rounding errors, typically with gutters
35667 var excess = columnWidth - containerWidth % columnWidth;
35670 // if overshoot is less than a pixel, round up, otherwise floor it
35671 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35672 cols = Math[ mathMethod ]( cols );
35673 this.cols = Math.max( cols, 1 );
35674 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35676 // padding positioning..
35677 var totalColWidth = this.cols * this.columnWidth;
35678 var padavail = this.containerWidth - totalColWidth;
35679 // so for 2 columns - we need 3 'pads'
35681 var padNeeded = (1+this.cols) * this.padWidth;
35683 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35685 this.columnWidth += padExtra
35686 //this.padWidth = Math.floor(padavail / ( this.cols));
35688 // adjust colum width so that padding is fixed??
35690 // we have 3 columns ... total = width * 3
35691 // we have X left over... that should be used by
35693 //if (this.expandC) {
35701 getContainerWidth : function()
35703 /* // container is parent if fit width
35704 var container = this.isFitWidth ? this.element.parentNode : this.element;
35705 // check that this.size and size are there
35706 // IE8 triggers resize on body size change, so they might not be
35708 var size = getSize( container ); //FIXME
35709 this.containerWidth = size && size.innerWidth; //FIXME
35712 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35716 _getItemLayoutPosition : function( item ) // what is item?
35718 // we resize the item to our columnWidth..
35720 item.setWidth(this.columnWidth);
35721 item.autoBoxAdjust = false;
35723 var sz = item.getSize();
35725 // how many columns does this brick span
35726 var remainder = this.containerWidth % this.columnWidth;
35728 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35729 // round if off by 1 pixel, otherwise use ceil
35730 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35731 colSpan = Math.min( colSpan, this.cols );
35733 // normally this should be '1' as we dont' currently allow multi width columns..
35735 var colGroup = this._getColGroup( colSpan );
35736 // get the minimum Y value from the columns
35737 var minimumY = Math.min.apply( Math, colGroup );
35738 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35740 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35742 // position the brick
35744 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35745 y: this.currentSize.y + minimumY + this.padHeight
35749 // apply setHeight to necessary columns
35750 var setHeight = minimumY + sz.height + this.padHeight;
35751 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35753 var setSpan = this.cols + 1 - colGroup.length;
35754 for ( var i = 0; i < setSpan; i++ ) {
35755 this.colYs[ shortColIndex + i ] = setHeight ;
35762 * @param {Number} colSpan - number of columns the element spans
35763 * @returns {Array} colGroup
35765 _getColGroup : function( colSpan )
35767 if ( colSpan < 2 ) {
35768 // if brick spans only one column, use all the column Ys
35773 // how many different places could this brick fit horizontally
35774 var groupCount = this.cols + 1 - colSpan;
35775 // for each group potential horizontal position
35776 for ( var i = 0; i < groupCount; i++ ) {
35777 // make an array of colY values for that one group
35778 var groupColYs = this.colYs.slice( i, i + colSpan );
35779 // and get the max value of the array
35780 colGroup[i] = Math.max.apply( Math, groupColYs );
35785 _manageStamp : function( stamp )
35787 var stampSize = stamp.getSize();
35788 var offset = stamp.getBox();
35789 // get the columns that this stamp affects
35790 var firstX = this.isOriginLeft ? offset.x : offset.right;
35791 var lastX = firstX + stampSize.width;
35792 var firstCol = Math.floor( firstX / this.columnWidth );
35793 firstCol = Math.max( 0, firstCol );
35795 var lastCol = Math.floor( lastX / this.columnWidth );
35796 // lastCol should not go over if multiple of columnWidth #425
35797 lastCol -= lastX % this.columnWidth ? 0 : 1;
35798 lastCol = Math.min( this.cols - 1, lastCol );
35800 // set colYs to bottom of the stamp
35801 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35804 for ( var i = firstCol; i <= lastCol; i++ ) {
35805 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35810 _getContainerSize : function()
35812 this.maxY = Math.max.apply( Math, this.colYs );
35817 if ( this.isFitWidth ) {
35818 size.width = this._getContainerFitWidth();
35824 _getContainerFitWidth : function()
35826 var unusedCols = 0;
35827 // count unused columns
35830 if ( this.colYs[i] !== 0 ) {
35835 // fit container to columns that have been used
35836 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35839 needsResizeLayout : function()
35841 var previousWidth = this.containerWidth;
35842 this.getContainerWidth();
35843 return previousWidth !== this.containerWidth;
35858 * @class Roo.bootstrap.MasonryBrick
35859 * @extends Roo.bootstrap.Component
35860 * Bootstrap MasonryBrick class
35863 * Create a new MasonryBrick
35864 * @param {Object} config The config object
35867 Roo.bootstrap.MasonryBrick = function(config){
35869 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35871 Roo.bootstrap.MasonryBrick.register(this);
35877 * When a MasonryBrick is clcik
35878 * @param {Roo.bootstrap.MasonryBrick} this
35879 * @param {Roo.EventObject} e
35885 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35888 * @cfg {String} title
35892 * @cfg {String} html
35896 * @cfg {String} bgimage
35900 * @cfg {String} videourl
35904 * @cfg {String} cls
35908 * @cfg {String} href
35912 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35917 * @cfg {String} placetitle (center|bottom)
35922 * @cfg {Boolean} isFitContainer defalut true
35924 isFitContainer : true,
35927 * @cfg {Boolean} preventDefault defalut false
35929 preventDefault : false,
35932 * @cfg {Boolean} inverse defalut false
35934 maskInverse : false,
35936 getAutoCreate : function()
35938 if(!this.isFitContainer){
35939 return this.getSplitAutoCreate();
35942 var cls = 'masonry-brick masonry-brick-full';
35944 if(this.href.length){
35945 cls += ' masonry-brick-link';
35948 if(this.bgimage.length){
35949 cls += ' masonry-brick-image';
35952 if(this.maskInverse){
35953 cls += ' mask-inverse';
35956 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35957 cls += ' enable-mask';
35961 cls += ' masonry-' + this.size + '-brick';
35964 if(this.placetitle.length){
35966 switch (this.placetitle) {
35968 cls += ' masonry-center-title';
35971 cls += ' masonry-bottom-title';
35978 if(!this.html.length && !this.bgimage.length){
35979 cls += ' masonry-center-title';
35982 if(!this.html.length && this.bgimage.length){
35983 cls += ' masonry-bottom-title';
35988 cls += ' ' + this.cls;
35992 tag: (this.href.length) ? 'a' : 'div',
35997 cls: 'masonry-brick-mask'
36001 cls: 'masonry-brick-paragraph',
36007 if(this.href.length){
36008 cfg.href = this.href;
36011 var cn = cfg.cn[1].cn;
36013 if(this.title.length){
36016 cls: 'masonry-brick-title',
36021 if(this.html.length){
36024 cls: 'masonry-brick-text',
36029 if (!this.title.length && !this.html.length) {
36030 cfg.cn[1].cls += ' hide';
36033 if(this.bgimage.length){
36036 cls: 'masonry-brick-image-view',
36041 if(this.videourl.length){
36042 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36043 // youtube support only?
36046 cls: 'masonry-brick-image-view',
36049 allowfullscreen : true
36057 getSplitAutoCreate : function()
36059 var cls = 'masonry-brick masonry-brick-split';
36061 if(this.href.length){
36062 cls += ' masonry-brick-link';
36065 if(this.bgimage.length){
36066 cls += ' masonry-brick-image';
36070 cls += ' masonry-' + this.size + '-brick';
36073 switch (this.placetitle) {
36075 cls += ' masonry-center-title';
36078 cls += ' masonry-bottom-title';
36081 if(!this.bgimage.length){
36082 cls += ' masonry-center-title';
36085 if(this.bgimage.length){
36086 cls += ' masonry-bottom-title';
36092 cls += ' ' + this.cls;
36096 tag: (this.href.length) ? 'a' : 'div',
36101 cls: 'masonry-brick-split-head',
36105 cls: 'masonry-brick-paragraph',
36112 cls: 'masonry-brick-split-body',
36118 if(this.href.length){
36119 cfg.href = this.href;
36122 if(this.title.length){
36123 cfg.cn[0].cn[0].cn.push({
36125 cls: 'masonry-brick-title',
36130 if(this.html.length){
36131 cfg.cn[1].cn.push({
36133 cls: 'masonry-brick-text',
36138 if(this.bgimage.length){
36139 cfg.cn[0].cn.push({
36141 cls: 'masonry-brick-image-view',
36146 if(this.videourl.length){
36147 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36148 // youtube support only?
36149 cfg.cn[0].cn.cn.push({
36151 cls: 'masonry-brick-image-view',
36154 allowfullscreen : true
36161 initEvents: function()
36163 switch (this.size) {
36196 this.el.on('touchstart', this.onTouchStart, this);
36197 this.el.on('touchmove', this.onTouchMove, this);
36198 this.el.on('touchend', this.onTouchEnd, this);
36199 this.el.on('contextmenu', this.onContextMenu, this);
36201 this.el.on('mouseenter' ,this.enter, this);
36202 this.el.on('mouseleave', this.leave, this);
36203 this.el.on('click', this.onClick, this);
36206 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36207 this.parent().bricks.push(this);
36212 onClick: function(e, el)
36214 var time = this.endTimer - this.startTimer;
36215 // Roo.log(e.preventDefault());
36218 e.preventDefault();
36223 if(!this.preventDefault){
36227 e.preventDefault();
36229 if (this.activeClass != '') {
36230 this.selectBrick();
36233 this.fireEvent('click', this, e);
36236 enter: function(e, el)
36238 e.preventDefault();
36240 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36244 if(this.bgimage.length && this.html.length){
36245 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36249 leave: function(e, el)
36251 e.preventDefault();
36253 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36257 if(this.bgimage.length && this.html.length){
36258 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36262 onTouchStart: function(e, el)
36264 // e.preventDefault();
36266 this.touchmoved = false;
36268 if(!this.isFitContainer){
36272 if(!this.bgimage.length || !this.html.length){
36276 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36278 this.timer = new Date().getTime();
36282 onTouchMove: function(e, el)
36284 this.touchmoved = true;
36287 onContextMenu : function(e,el)
36289 e.preventDefault();
36290 e.stopPropagation();
36294 onTouchEnd: function(e, el)
36296 // e.preventDefault();
36298 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36305 if(!this.bgimage.length || !this.html.length){
36307 if(this.href.length){
36308 window.location.href = this.href;
36314 if(!this.isFitContainer){
36318 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36320 window.location.href = this.href;
36323 //selection on single brick only
36324 selectBrick : function() {
36326 if (!this.parentId) {
36330 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36331 var index = m.selectedBrick.indexOf(this.id);
36334 m.selectedBrick.splice(index,1);
36335 this.el.removeClass(this.activeClass);
36339 for(var i = 0; i < m.selectedBrick.length; i++) {
36340 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36341 b.el.removeClass(b.activeClass);
36344 m.selectedBrick = [];
36346 m.selectedBrick.push(this.id);
36347 this.el.addClass(this.activeClass);
36351 isSelected : function(){
36352 return this.el.hasClass(this.activeClass);
36357 Roo.apply(Roo.bootstrap.MasonryBrick, {
36360 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36362 * register a Masonry Brick
36363 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36366 register : function(brick)
36368 //this.groups[brick.id] = brick;
36369 this.groups.add(brick.id, brick);
36372 * fetch a masonry brick based on the masonry brick ID
36373 * @param {string} the masonry brick to add
36374 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36377 get: function(brick_id)
36379 // if (typeof(this.groups[brick_id]) == 'undefined') {
36382 // return this.groups[brick_id] ;
36384 if(this.groups.key(brick_id)) {
36385 return this.groups.key(brick_id);
36403 * @class Roo.bootstrap.Brick
36404 * @extends Roo.bootstrap.Component
36405 * Bootstrap Brick class
36408 * Create a new Brick
36409 * @param {Object} config The config object
36412 Roo.bootstrap.Brick = function(config){
36413 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36419 * When a Brick is click
36420 * @param {Roo.bootstrap.Brick} this
36421 * @param {Roo.EventObject} e
36427 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36430 * @cfg {String} title
36434 * @cfg {String} html
36438 * @cfg {String} bgimage
36442 * @cfg {String} cls
36446 * @cfg {String} href
36450 * @cfg {String} video
36454 * @cfg {Boolean} square
36458 getAutoCreate : function()
36460 var cls = 'roo-brick';
36462 if(this.href.length){
36463 cls += ' roo-brick-link';
36466 if(this.bgimage.length){
36467 cls += ' roo-brick-image';
36470 if(!this.html.length && !this.bgimage.length){
36471 cls += ' roo-brick-center-title';
36474 if(!this.html.length && this.bgimage.length){
36475 cls += ' roo-brick-bottom-title';
36479 cls += ' ' + this.cls;
36483 tag: (this.href.length) ? 'a' : 'div',
36488 cls: 'roo-brick-paragraph',
36494 if(this.href.length){
36495 cfg.href = this.href;
36498 var cn = cfg.cn[0].cn;
36500 if(this.title.length){
36503 cls: 'roo-brick-title',
36508 if(this.html.length){
36511 cls: 'roo-brick-text',
36518 if(this.bgimage.length){
36521 cls: 'roo-brick-image-view',
36529 initEvents: function()
36531 if(this.title.length || this.html.length){
36532 this.el.on('mouseenter' ,this.enter, this);
36533 this.el.on('mouseleave', this.leave, this);
36536 Roo.EventManager.onWindowResize(this.resize, this);
36538 if(this.bgimage.length){
36539 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36540 this.imageEl.on('load', this.onImageLoad, this);
36547 onImageLoad : function()
36552 resize : function()
36554 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36556 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36558 if(this.bgimage.length){
36559 var image = this.el.select('.roo-brick-image-view', true).first();
36561 image.setWidth(paragraph.getWidth());
36564 image.setHeight(paragraph.getWidth());
36567 this.el.setHeight(image.getHeight());
36568 paragraph.setHeight(image.getHeight());
36574 enter: function(e, el)
36576 e.preventDefault();
36578 if(this.bgimage.length){
36579 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36580 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36584 leave: function(e, el)
36586 e.preventDefault();
36588 if(this.bgimage.length){
36589 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36590 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36605 * @class Roo.bootstrap.NumberField
36606 * @extends Roo.bootstrap.Input
36607 * Bootstrap NumberField class
36613 * Create a new NumberField
36614 * @param {Object} config The config object
36617 Roo.bootstrap.NumberField = function(config){
36618 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36621 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36624 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36626 allowDecimals : true,
36628 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36630 decimalSeparator : ".",
36632 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36634 decimalPrecision : 2,
36636 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36638 allowNegative : true,
36641 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36645 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36647 minValue : Number.NEGATIVE_INFINITY,
36649 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36651 maxValue : Number.MAX_VALUE,
36653 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36655 minText : "The minimum value for this field is {0}",
36657 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36659 maxText : "The maximum value for this field is {0}",
36661 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36662 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36664 nanText : "{0} is not a valid number",
36666 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36668 thousandsDelimiter : false,
36670 * @cfg {String} valueAlign alignment of value
36672 valueAlign : "left",
36674 getAutoCreate : function()
36676 var hiddenInput = {
36680 cls: 'hidden-number-input'
36684 hiddenInput.name = this.name;
36689 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36691 this.name = hiddenInput.name;
36693 if(cfg.cn.length > 0) {
36694 cfg.cn.push(hiddenInput);
36701 initEvents : function()
36703 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36705 var allowed = "0123456789";
36707 if(this.allowDecimals){
36708 allowed += this.decimalSeparator;
36711 if(this.allowNegative){
36715 if(this.thousandsDelimiter) {
36719 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36721 var keyPress = function(e){
36723 var k = e.getKey();
36725 var c = e.getCharCode();
36728 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36729 allowed.indexOf(String.fromCharCode(c)) === -1
36735 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36739 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36744 this.el.on("keypress", keyPress, this);
36747 validateValue : function(value)
36750 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36754 var num = this.parseValue(value);
36757 this.markInvalid(String.format(this.nanText, value));
36761 if(num < this.minValue){
36762 this.markInvalid(String.format(this.minText, this.minValue));
36766 if(num > this.maxValue){
36767 this.markInvalid(String.format(this.maxText, this.maxValue));
36774 getValue : function()
36776 var v = this.hiddenEl().getValue();
36778 return this.fixPrecision(this.parseValue(v));
36781 parseValue : function(value)
36783 if(this.thousandsDelimiter) {
36785 r = new RegExp(",", "g");
36786 value = value.replace(r, "");
36789 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36790 return isNaN(value) ? '' : value;
36793 fixPrecision : function(value)
36795 if(this.thousandsDelimiter) {
36797 r = new RegExp(",", "g");
36798 value = value.replace(r, "");
36801 var nan = isNaN(value);
36803 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36804 return nan ? '' : value;
36806 return parseFloat(value).toFixed(this.decimalPrecision);
36809 setValue : function(v)
36811 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36817 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36819 this.inputEl().dom.value = (v == '') ? '' :
36820 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36822 if(!this.allowZero && v === '0') {
36823 this.hiddenEl().dom.value = '';
36824 this.inputEl().dom.value = '';
36831 decimalPrecisionFcn : function(v)
36833 return Math.floor(v);
36836 beforeBlur : function()
36838 var v = this.parseValue(this.getRawValue());
36840 if(v || v === 0 || v === ''){
36845 hiddenEl : function()
36847 return this.el.select('input.hidden-number-input',true).first();
36859 * @class Roo.bootstrap.DocumentSlider
36860 * @extends Roo.bootstrap.Component
36861 * Bootstrap DocumentSlider class
36864 * Create a new DocumentViewer
36865 * @param {Object} config The config object
36868 Roo.bootstrap.DocumentSlider = function(config){
36869 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36876 * Fire after initEvent
36877 * @param {Roo.bootstrap.DocumentSlider} this
36882 * Fire after update
36883 * @param {Roo.bootstrap.DocumentSlider} this
36889 * @param {Roo.bootstrap.DocumentSlider} this
36895 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36901 getAutoCreate : function()
36905 cls : 'roo-document-slider',
36909 cls : 'roo-document-slider-header',
36913 cls : 'roo-document-slider-header-title'
36919 cls : 'roo-document-slider-body',
36923 cls : 'roo-document-slider-prev',
36927 cls : 'fa fa-chevron-left'
36933 cls : 'roo-document-slider-thumb',
36937 cls : 'roo-document-slider-image'
36943 cls : 'roo-document-slider-next',
36947 cls : 'fa fa-chevron-right'
36959 initEvents : function()
36961 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36962 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36964 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36965 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36967 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36968 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36970 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36971 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36973 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36974 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36976 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36977 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36979 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36980 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36982 this.thumbEl.on('click', this.onClick, this);
36984 this.prevIndicator.on('click', this.prev, this);
36986 this.nextIndicator.on('click', this.next, this);
36990 initial : function()
36992 if(this.files.length){
36993 this.indicator = 1;
36997 this.fireEvent('initial', this);
37000 update : function()
37002 this.imageEl.attr('src', this.files[this.indicator - 1]);
37004 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37006 this.prevIndicator.show();
37008 if(this.indicator == 1){
37009 this.prevIndicator.hide();
37012 this.nextIndicator.show();
37014 if(this.indicator == this.files.length){
37015 this.nextIndicator.hide();
37018 this.thumbEl.scrollTo('top');
37020 this.fireEvent('update', this);
37023 onClick : function(e)
37025 e.preventDefault();
37027 this.fireEvent('click', this);
37032 e.preventDefault();
37034 this.indicator = Math.max(1, this.indicator - 1);
37041 e.preventDefault();
37043 this.indicator = Math.min(this.files.length, this.indicator + 1);
37057 * @class Roo.bootstrap.RadioSet
37058 * @extends Roo.bootstrap.Input
37059 * Bootstrap RadioSet class
37060 * @cfg {String} indicatorpos (left|right) default left
37061 * @cfg {Boolean} inline (true|false) inline the element (default true)
37062 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37064 * Create a new RadioSet
37065 * @param {Object} config The config object
37068 Roo.bootstrap.RadioSet = function(config){
37070 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37074 Roo.bootstrap.RadioSet.register(this);
37079 * Fires when the element is checked or unchecked.
37080 * @param {Roo.bootstrap.RadioSet} this This radio
37081 * @param {Roo.bootstrap.Radio} item The checked item
37086 * Fires when the element is click.
37087 * @param {Roo.bootstrap.RadioSet} this This radio set
37088 * @param {Roo.bootstrap.Radio} item The checked item
37089 * @param {Roo.EventObject} e The event object
37096 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37104 indicatorpos : 'left',
37106 getAutoCreate : function()
37110 cls : 'roo-radio-set-label',
37114 html : this.fieldLabel
37118 if (Roo.bootstrap.version == 3) {
37121 if(this.indicatorpos == 'left'){
37124 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37125 tooltip : 'This field is required'
37130 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37131 tooltip : 'This field is required'
37137 cls : 'roo-radio-set-items'
37140 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37142 if (align === 'left' && this.fieldLabel.length) {
37145 cls : "roo-radio-set-right",
37151 if(this.labelWidth > 12){
37152 label.style = "width: " + this.labelWidth + 'px';
37155 if(this.labelWidth < 13 && this.labelmd == 0){
37156 this.labelmd = this.labelWidth;
37159 if(this.labellg > 0){
37160 label.cls += ' col-lg-' + this.labellg;
37161 items.cls += ' col-lg-' + (12 - this.labellg);
37164 if(this.labelmd > 0){
37165 label.cls += ' col-md-' + this.labelmd;
37166 items.cls += ' col-md-' + (12 - this.labelmd);
37169 if(this.labelsm > 0){
37170 label.cls += ' col-sm-' + this.labelsm;
37171 items.cls += ' col-sm-' + (12 - this.labelsm);
37174 if(this.labelxs > 0){
37175 label.cls += ' col-xs-' + this.labelxs;
37176 items.cls += ' col-xs-' + (12 - this.labelxs);
37182 cls : 'roo-radio-set',
37186 cls : 'roo-radio-set-input',
37189 value : this.value ? this.value : ''
37196 if(this.weight.length){
37197 cfg.cls += ' roo-radio-' + this.weight;
37201 cfg.cls += ' roo-radio-set-inline';
37205 ['xs','sm','md','lg'].map(function(size){
37206 if (settings[size]) {
37207 cfg.cls += ' col-' + size + '-' + settings[size];
37215 initEvents : function()
37217 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37218 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37220 if(!this.fieldLabel.length){
37221 this.labelEl.hide();
37224 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37225 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37227 this.indicator = this.indicatorEl();
37229 if(this.indicator){
37230 this.indicator.addClass('invisible');
37233 this.originalValue = this.getValue();
37237 inputEl: function ()
37239 return this.el.select('.roo-radio-set-input', true).first();
37242 getChildContainer : function()
37244 return this.itemsEl;
37247 register : function(item)
37249 this.radioes.push(item);
37253 validate : function()
37255 if(this.getVisibilityEl().hasClass('hidden')){
37261 Roo.each(this.radioes, function(i){
37270 if(this.allowBlank) {
37274 if(this.disabled || valid){
37279 this.markInvalid();
37284 markValid : function()
37286 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37287 this.indicatorEl().removeClass('visible');
37288 this.indicatorEl().addClass('invisible');
37292 if (Roo.bootstrap.version == 3) {
37293 this.el.removeClass([this.invalidClass, this.validClass]);
37294 this.el.addClass(this.validClass);
37296 this.el.removeClass(['is-invalid','is-valid']);
37297 this.el.addClass(['is-valid']);
37299 this.fireEvent('valid', this);
37302 markInvalid : function(msg)
37304 if(this.allowBlank || this.disabled){
37308 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37309 this.indicatorEl().removeClass('invisible');
37310 this.indicatorEl().addClass('visible');
37312 if (Roo.bootstrap.version == 3) {
37313 this.el.removeClass([this.invalidClass, this.validClass]);
37314 this.el.addClass(this.invalidClass);
37316 this.el.removeClass(['is-invalid','is-valid']);
37317 this.el.addClass(['is-invalid']);
37320 this.fireEvent('invalid', this, msg);
37324 setValue : function(v, suppressEvent)
37326 if(this.value === v){
37333 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37336 Roo.each(this.radioes, function(i){
37338 i.el.removeClass('checked');
37341 Roo.each(this.radioes, function(i){
37343 if(i.value === v || i.value.toString() === v.toString()){
37345 i.el.addClass('checked');
37347 if(suppressEvent !== true){
37348 this.fireEvent('check', this, i);
37359 clearInvalid : function(){
37361 if(!this.el || this.preventMark){
37365 this.el.removeClass([this.invalidClass]);
37367 this.fireEvent('valid', this);
37372 Roo.apply(Roo.bootstrap.RadioSet, {
37376 register : function(set)
37378 this.groups[set.name] = set;
37381 get: function(name)
37383 if (typeof(this.groups[name]) == 'undefined') {
37387 return this.groups[name] ;
37393 * Ext JS Library 1.1.1
37394 * Copyright(c) 2006-2007, Ext JS, LLC.
37396 * Originally Released Under LGPL - original licence link has changed is not relivant.
37399 * <script type="text/javascript">
37404 * @class Roo.bootstrap.SplitBar
37405 * @extends Roo.util.Observable
37406 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37410 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37411 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37412 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37413 split.minSize = 100;
37414 split.maxSize = 600;
37415 split.animate = true;
37416 split.on('moved', splitterMoved);
37419 * Create a new SplitBar
37420 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37421 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37422 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37423 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37424 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37425 position of the SplitBar).
37427 Roo.bootstrap.SplitBar = function(cfg){
37432 // dragElement : elm
37433 // resizingElement: el,
37435 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37436 // placement : Roo.bootstrap.SplitBar.LEFT ,
37437 // existingProxy ???
37440 this.el = Roo.get(cfg.dragElement, true);
37441 this.el.dom.unselectable = "on";
37443 this.resizingEl = Roo.get(cfg.resizingElement, true);
37447 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37448 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37451 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37454 * The minimum size of the resizing element. (Defaults to 0)
37460 * The maximum size of the resizing element. (Defaults to 2000)
37463 this.maxSize = 2000;
37466 * Whether to animate the transition to the new size
37469 this.animate = false;
37472 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37475 this.useShim = false;
37480 if(!cfg.existingProxy){
37482 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37484 this.proxy = Roo.get(cfg.existingProxy).dom;
37487 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37490 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37493 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37496 this.dragSpecs = {};
37499 * @private The adapter to use to positon and resize elements
37501 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37502 this.adapter.init(this);
37504 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37506 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37507 this.el.addClass("roo-splitbar-h");
37510 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37511 this.el.addClass("roo-splitbar-v");
37517 * Fires when the splitter is moved (alias for {@link #event-moved})
37518 * @param {Roo.bootstrap.SplitBar} this
37519 * @param {Number} newSize the new width or height
37524 * Fires when the splitter is moved
37525 * @param {Roo.bootstrap.SplitBar} this
37526 * @param {Number} newSize the new width or height
37530 * @event beforeresize
37531 * Fires before the splitter is dragged
37532 * @param {Roo.bootstrap.SplitBar} this
37534 "beforeresize" : true,
37536 "beforeapply" : true
37539 Roo.util.Observable.call(this);
37542 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37543 onStartProxyDrag : function(x, y){
37544 this.fireEvent("beforeresize", this);
37546 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37548 o.enableDisplayMode("block");
37549 // all splitbars share the same overlay
37550 Roo.bootstrap.SplitBar.prototype.overlay = o;
37552 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37553 this.overlay.show();
37554 Roo.get(this.proxy).setDisplayed("block");
37555 var size = this.adapter.getElementSize(this);
37556 this.activeMinSize = this.getMinimumSize();;
37557 this.activeMaxSize = this.getMaximumSize();;
37558 var c1 = size - this.activeMinSize;
37559 var c2 = Math.max(this.activeMaxSize - size, 0);
37560 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37561 this.dd.resetConstraints();
37562 this.dd.setXConstraint(
37563 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37564 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37566 this.dd.setYConstraint(0, 0);
37568 this.dd.resetConstraints();
37569 this.dd.setXConstraint(0, 0);
37570 this.dd.setYConstraint(
37571 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37572 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37575 this.dragSpecs.startSize = size;
37576 this.dragSpecs.startPoint = [x, y];
37577 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37581 * @private Called after the drag operation by the DDProxy
37583 onEndProxyDrag : function(e){
37584 Roo.get(this.proxy).setDisplayed(false);
37585 var endPoint = Roo.lib.Event.getXY(e);
37587 this.overlay.hide();
37590 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37591 newSize = this.dragSpecs.startSize +
37592 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37593 endPoint[0] - this.dragSpecs.startPoint[0] :
37594 this.dragSpecs.startPoint[0] - endPoint[0]
37597 newSize = this.dragSpecs.startSize +
37598 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37599 endPoint[1] - this.dragSpecs.startPoint[1] :
37600 this.dragSpecs.startPoint[1] - endPoint[1]
37603 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37604 if(newSize != this.dragSpecs.startSize){
37605 if(this.fireEvent('beforeapply', this, newSize) !== false){
37606 this.adapter.setElementSize(this, newSize);
37607 this.fireEvent("moved", this, newSize);
37608 this.fireEvent("resize", this, newSize);
37614 * Get the adapter this SplitBar uses
37615 * @return The adapter object
37617 getAdapter : function(){
37618 return this.adapter;
37622 * Set the adapter this SplitBar uses
37623 * @param {Object} adapter A SplitBar adapter object
37625 setAdapter : function(adapter){
37626 this.adapter = adapter;
37627 this.adapter.init(this);
37631 * Gets the minimum size for the resizing element
37632 * @return {Number} The minimum size
37634 getMinimumSize : function(){
37635 return this.minSize;
37639 * Sets the minimum size for the resizing element
37640 * @param {Number} minSize The minimum size
37642 setMinimumSize : function(minSize){
37643 this.minSize = minSize;
37647 * Gets the maximum size for the resizing element
37648 * @return {Number} The maximum size
37650 getMaximumSize : function(){
37651 return this.maxSize;
37655 * Sets the maximum size for the resizing element
37656 * @param {Number} maxSize The maximum size
37658 setMaximumSize : function(maxSize){
37659 this.maxSize = maxSize;
37663 * Sets the initialize size for the resizing element
37664 * @param {Number} size The initial size
37666 setCurrentSize : function(size){
37667 var oldAnimate = this.animate;
37668 this.animate = false;
37669 this.adapter.setElementSize(this, size);
37670 this.animate = oldAnimate;
37674 * Destroy this splitbar.
37675 * @param {Boolean} removeEl True to remove the element
37677 destroy : function(removeEl){
37679 this.shim.remove();
37682 this.proxy.parentNode.removeChild(this.proxy);
37690 * @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.
37692 Roo.bootstrap.SplitBar.createProxy = function(dir){
37693 var proxy = new Roo.Element(document.createElement("div"));
37694 proxy.unselectable();
37695 var cls = 'roo-splitbar-proxy';
37696 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37697 document.body.appendChild(proxy.dom);
37702 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37703 * Default Adapter. It assumes the splitter and resizing element are not positioned
37704 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37706 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37709 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37710 // do nothing for now
37711 init : function(s){
37715 * Called before drag operations to get the current size of the resizing element.
37716 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37718 getElementSize : function(s){
37719 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37720 return s.resizingEl.getWidth();
37722 return s.resizingEl.getHeight();
37727 * Called after drag operations to set the size of the resizing element.
37728 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37729 * @param {Number} newSize The new size to set
37730 * @param {Function} onComplete A function to be invoked when resizing is complete
37732 setElementSize : function(s, newSize, onComplete){
37733 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37735 s.resizingEl.setWidth(newSize);
37737 onComplete(s, newSize);
37740 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37745 s.resizingEl.setHeight(newSize);
37747 onComplete(s, newSize);
37750 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37757 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37758 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37759 * Adapter that moves the splitter element to align with the resized sizing element.
37760 * Used with an absolute positioned SplitBar.
37761 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37762 * document.body, make sure you assign an id to the body element.
37764 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37765 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37766 this.container = Roo.get(container);
37769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37770 init : function(s){
37771 this.basic.init(s);
37774 getElementSize : function(s){
37775 return this.basic.getElementSize(s);
37778 setElementSize : function(s, newSize, onComplete){
37779 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37782 moveSplitter : function(s){
37783 var yes = Roo.bootstrap.SplitBar;
37784 switch(s.placement){
37786 s.el.setX(s.resizingEl.getRight());
37789 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37792 s.el.setY(s.resizingEl.getBottom());
37795 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37802 * Orientation constant - Create a vertical SplitBar
37806 Roo.bootstrap.SplitBar.VERTICAL = 1;
37809 * Orientation constant - Create a horizontal SplitBar
37813 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37816 * Placement constant - The resizing element is to the left of the splitter element
37820 Roo.bootstrap.SplitBar.LEFT = 1;
37823 * Placement constant - The resizing element is to the right of the splitter element
37827 Roo.bootstrap.SplitBar.RIGHT = 2;
37830 * Placement constant - The resizing element is positioned above the splitter element
37834 Roo.bootstrap.SplitBar.TOP = 3;
37837 * Placement constant - The resizing element is positioned under splitter element
37841 Roo.bootstrap.SplitBar.BOTTOM = 4;
37842 Roo.namespace("Roo.bootstrap.layout");/*
37844 * Ext JS Library 1.1.1
37845 * Copyright(c) 2006-2007, Ext JS, LLC.
37847 * Originally Released Under LGPL - original licence link has changed is not relivant.
37850 * <script type="text/javascript">
37854 * @class Roo.bootstrap.layout.Manager
37855 * @extends Roo.bootstrap.Component
37856 * Base class for layout managers.
37858 Roo.bootstrap.layout.Manager = function(config)
37860 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37866 /** false to disable window resize monitoring @type Boolean */
37867 this.monitorWindowResize = true;
37872 * Fires when a layout is performed.
37873 * @param {Roo.LayoutManager} this
37877 * @event regionresized
37878 * Fires when the user resizes a region.
37879 * @param {Roo.LayoutRegion} region The resized region
37880 * @param {Number} newSize The new size (width for east/west, height for north/south)
37882 "regionresized" : true,
37884 * @event regioncollapsed
37885 * Fires when a region is collapsed.
37886 * @param {Roo.LayoutRegion} region The collapsed region
37888 "regioncollapsed" : true,
37890 * @event regionexpanded
37891 * Fires when a region is expanded.
37892 * @param {Roo.LayoutRegion} region The expanded region
37894 "regionexpanded" : true
37896 this.updating = false;
37899 this.el = Roo.get(config.el);
37905 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37910 monitorWindowResize : true,
37916 onRender : function(ct, position)
37919 this.el = Roo.get(ct);
37922 //this.fireEvent('render',this);
37926 initEvents: function()
37930 // ie scrollbar fix
37931 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37932 document.body.scroll = "no";
37933 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37934 this.el.position('relative');
37936 this.id = this.el.id;
37937 this.el.addClass("roo-layout-container");
37938 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37939 if(this.el.dom != document.body ) {
37940 this.el.on('resize', this.layout,this);
37941 this.el.on('show', this.layout,this);
37947 * Returns true if this layout is currently being updated
37948 * @return {Boolean}
37950 isUpdating : function(){
37951 return this.updating;
37955 * Suspend the LayoutManager from doing auto-layouts while
37956 * making multiple add or remove calls
37958 beginUpdate : function(){
37959 this.updating = true;
37963 * Restore auto-layouts and optionally disable the manager from performing a layout
37964 * @param {Boolean} noLayout true to disable a layout update
37966 endUpdate : function(noLayout){
37967 this.updating = false;
37973 layout: function(){
37977 onRegionResized : function(region, newSize){
37978 this.fireEvent("regionresized", region, newSize);
37982 onRegionCollapsed : function(region){
37983 this.fireEvent("regioncollapsed", region);
37986 onRegionExpanded : function(region){
37987 this.fireEvent("regionexpanded", region);
37991 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37992 * performs box-model adjustments.
37993 * @return {Object} The size as an object {width: (the width), height: (the height)}
37995 getViewSize : function()
37998 if(this.el.dom != document.body){
37999 size = this.el.getSize();
38001 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38003 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38004 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38009 * Returns the Element this layout is bound to.
38010 * @return {Roo.Element}
38012 getEl : function(){
38017 * Returns the specified region.
38018 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38019 * @return {Roo.LayoutRegion}
38021 getRegion : function(target){
38022 return this.regions[target.toLowerCase()];
38025 onWindowResize : function(){
38026 if(this.monitorWindowResize){
38033 * Ext JS Library 1.1.1
38034 * Copyright(c) 2006-2007, Ext JS, LLC.
38036 * Originally Released Under LGPL - original licence link has changed is not relivant.
38039 * <script type="text/javascript">
38042 * @class Roo.bootstrap.layout.Border
38043 * @extends Roo.bootstrap.layout.Manager
38044 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38045 * please see: examples/bootstrap/nested.html<br><br>
38047 <b>The container the layout is rendered into can be either the body element or any other element.
38048 If it is not the body element, the container needs to either be an absolute positioned element,
38049 or you will need to add "position:relative" to the css of the container. You will also need to specify
38050 the container size if it is not the body element.</b>
38053 * Create a new Border
38054 * @param {Object} config Configuration options
38056 Roo.bootstrap.layout.Border = function(config){
38057 config = config || {};
38058 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38062 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38063 if(config[region]){
38064 config[region].region = region;
38065 this.addRegion(config[region]);
38071 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38073 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38075 parent : false, // this might point to a 'nest' or a ???
38078 * Creates and adds a new region if it doesn't already exist.
38079 * @param {String} target The target region key (north, south, east, west or center).
38080 * @param {Object} config The regions config object
38081 * @return {BorderLayoutRegion} The new region
38083 addRegion : function(config)
38085 if(!this.regions[config.region]){
38086 var r = this.factory(config);
38087 this.bindRegion(r);
38089 return this.regions[config.region];
38093 bindRegion : function(r){
38094 this.regions[r.config.region] = r;
38096 r.on("visibilitychange", this.layout, this);
38097 r.on("paneladded", this.layout, this);
38098 r.on("panelremoved", this.layout, this);
38099 r.on("invalidated", this.layout, this);
38100 r.on("resized", this.onRegionResized, this);
38101 r.on("collapsed", this.onRegionCollapsed, this);
38102 r.on("expanded", this.onRegionExpanded, this);
38106 * Performs a layout update.
38108 layout : function()
38110 if(this.updating) {
38114 // render all the rebions if they have not been done alreayd?
38115 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38116 if(this.regions[region] && !this.regions[region].bodyEl){
38117 this.regions[region].onRender(this.el)
38121 var size = this.getViewSize();
38122 var w = size.width;
38123 var h = size.height;
38128 //var x = 0, y = 0;
38130 var rs = this.regions;
38131 var north = rs["north"];
38132 var south = rs["south"];
38133 var west = rs["west"];
38134 var east = rs["east"];
38135 var center = rs["center"];
38136 //if(this.hideOnLayout){ // not supported anymore
38137 //c.el.setStyle("display", "none");
38139 if(north && north.isVisible()){
38140 var b = north.getBox();
38141 var m = north.getMargins();
38142 b.width = w - (m.left+m.right);
38145 centerY = b.height + b.y + m.bottom;
38146 centerH -= centerY;
38147 north.updateBox(this.safeBox(b));
38149 if(south && south.isVisible()){
38150 var b = south.getBox();
38151 var m = south.getMargins();
38152 b.width = w - (m.left+m.right);
38154 var totalHeight = (b.height + m.top + m.bottom);
38155 b.y = h - totalHeight + m.top;
38156 centerH -= totalHeight;
38157 south.updateBox(this.safeBox(b));
38159 if(west && west.isVisible()){
38160 var b = west.getBox();
38161 var m = west.getMargins();
38162 b.height = centerH - (m.top+m.bottom);
38164 b.y = centerY + m.top;
38165 var totalWidth = (b.width + m.left + m.right);
38166 centerX += totalWidth;
38167 centerW -= totalWidth;
38168 west.updateBox(this.safeBox(b));
38170 if(east && east.isVisible()){
38171 var b = east.getBox();
38172 var m = east.getMargins();
38173 b.height = centerH - (m.top+m.bottom);
38174 var totalWidth = (b.width + m.left + m.right);
38175 b.x = w - totalWidth + m.left;
38176 b.y = centerY + m.top;
38177 centerW -= totalWidth;
38178 east.updateBox(this.safeBox(b));
38181 var m = center.getMargins();
38183 x: centerX + m.left,
38184 y: centerY + m.top,
38185 width: centerW - (m.left+m.right),
38186 height: centerH - (m.top+m.bottom)
38188 //if(this.hideOnLayout){
38189 //center.el.setStyle("display", "block");
38191 center.updateBox(this.safeBox(centerBox));
38194 this.fireEvent("layout", this);
38198 safeBox : function(box){
38199 box.width = Math.max(0, box.width);
38200 box.height = Math.max(0, box.height);
38205 * Adds a ContentPanel (or subclass) to this layout.
38206 * @param {String} target The target region key (north, south, east, west or center).
38207 * @param {Roo.ContentPanel} panel The panel to add
38208 * @return {Roo.ContentPanel} The added panel
38210 add : function(target, panel){
38212 target = target.toLowerCase();
38213 return this.regions[target].add(panel);
38217 * Remove a ContentPanel (or subclass) to this layout.
38218 * @param {String} target The target region key (north, south, east, west or center).
38219 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38220 * @return {Roo.ContentPanel} The removed panel
38222 remove : function(target, panel){
38223 target = target.toLowerCase();
38224 return this.regions[target].remove(panel);
38228 * Searches all regions for a panel with the specified id
38229 * @param {String} panelId
38230 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38232 findPanel : function(panelId){
38233 var rs = this.regions;
38234 for(var target in rs){
38235 if(typeof rs[target] != "function"){
38236 var p = rs[target].getPanel(panelId);
38246 * Searches all regions for a panel with the specified id and activates (shows) it.
38247 * @param {String/ContentPanel} panelId The panels id or the panel itself
38248 * @return {Roo.ContentPanel} The shown panel or null
38250 showPanel : function(panelId) {
38251 var rs = this.regions;
38252 for(var target in rs){
38253 var r = rs[target];
38254 if(typeof r != "function"){
38255 if(r.hasPanel(panelId)){
38256 return r.showPanel(panelId);
38264 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38265 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38268 restoreState : function(provider){
38270 provider = Roo.state.Manager;
38272 var sm = new Roo.LayoutStateManager();
38273 sm.init(this, provider);
38279 * Adds a xtype elements to the layout.
38283 xtype : 'ContentPanel',
38290 xtype : 'NestedLayoutPanel',
38296 items : [ ... list of content panels or nested layout panels.. ]
38300 * @param {Object} cfg Xtype definition of item to add.
38302 addxtype : function(cfg)
38304 // basically accepts a pannel...
38305 // can accept a layout region..!?!?
38306 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38309 // theory? children can only be panels??
38311 //if (!cfg.xtype.match(/Panel$/)) {
38316 if (typeof(cfg.region) == 'undefined') {
38317 Roo.log("Failed to add Panel, region was not set");
38321 var region = cfg.region;
38327 xitems = cfg.items;
38332 if ( region == 'center') {
38333 Roo.log("Center: " + cfg.title);
38339 case 'Content': // ContentPanel (el, cfg)
38340 case 'Scroll': // ContentPanel (el, cfg)
38342 cfg.autoCreate = cfg.autoCreate || true;
38343 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38345 // var el = this.el.createChild();
38346 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38349 this.add(region, ret);
38353 case 'TreePanel': // our new panel!
38354 cfg.el = this.el.createChild();
38355 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38356 this.add(region, ret);
38361 // create a new Layout (which is a Border Layout...
38363 var clayout = cfg.layout;
38364 clayout.el = this.el.createChild();
38365 clayout.items = clayout.items || [];
38369 // replace this exitems with the clayout ones..
38370 xitems = clayout.items;
38372 // force background off if it's in center...
38373 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38374 cfg.background = false;
38376 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38379 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38380 //console.log('adding nested layout panel ' + cfg.toSource());
38381 this.add(region, ret);
38382 nb = {}; /// find first...
38387 // needs grid and region
38389 //var el = this.getRegion(region).el.createChild();
38391 *var el = this.el.createChild();
38392 // create the grid first...
38393 cfg.grid.container = el;
38394 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38397 if (region == 'center' && this.active ) {
38398 cfg.background = false;
38401 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38403 this.add(region, ret);
38405 if (cfg.background) {
38406 // render grid on panel activation (if panel background)
38407 ret.on('activate', function(gp) {
38408 if (!gp.grid.rendered) {
38409 // gp.grid.render(el);
38413 // cfg.grid.render(el);
38419 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38420 // it was the old xcomponent building that caused this before.
38421 // espeically if border is the top element in the tree.
38431 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38433 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38434 this.add(region, ret);
38438 throw "Can not add '" + cfg.xtype + "' to Border";
38444 this.beginUpdate();
38448 Roo.each(xitems, function(i) {
38449 region = nb && i.region ? i.region : false;
38451 var add = ret.addxtype(i);
38454 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38455 if (!i.background) {
38456 abn[region] = nb[region] ;
38463 // make the last non-background panel active..
38464 //if (nb) { Roo.log(abn); }
38467 for(var r in abn) {
38468 region = this.getRegion(r);
38470 // tried using nb[r], but it does not work..
38472 region.showPanel(abn[r]);
38483 factory : function(cfg)
38486 var validRegions = Roo.bootstrap.layout.Border.regions;
38488 var target = cfg.region;
38491 var r = Roo.bootstrap.layout;
38495 return new r.North(cfg);
38497 return new r.South(cfg);
38499 return new r.East(cfg);
38501 return new r.West(cfg);
38503 return new r.Center(cfg);
38505 throw 'Layout region "'+target+'" not supported.';
38512 * Ext JS Library 1.1.1
38513 * Copyright(c) 2006-2007, Ext JS, LLC.
38515 * Originally Released Under LGPL - original licence link has changed is not relivant.
38518 * <script type="text/javascript">
38522 * @class Roo.bootstrap.layout.Basic
38523 * @extends Roo.util.Observable
38524 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38525 * and does not have a titlebar, tabs or any other features. All it does is size and position
38526 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38527 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38528 * @cfg {string} region the region that it inhabits..
38529 * @cfg {bool} skipConfig skip config?
38533 Roo.bootstrap.layout.Basic = function(config){
38535 this.mgr = config.mgr;
38537 this.position = config.region;
38539 var skipConfig = config.skipConfig;
38543 * @scope Roo.BasicLayoutRegion
38547 * @event beforeremove
38548 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38549 * @param {Roo.LayoutRegion} this
38550 * @param {Roo.ContentPanel} panel The panel
38551 * @param {Object} e The cancel event object
38553 "beforeremove" : true,
38555 * @event invalidated
38556 * Fires when the layout for this region is changed.
38557 * @param {Roo.LayoutRegion} this
38559 "invalidated" : true,
38561 * @event visibilitychange
38562 * Fires when this region is shown or hidden
38563 * @param {Roo.LayoutRegion} this
38564 * @param {Boolean} visibility true or false
38566 "visibilitychange" : true,
38568 * @event paneladded
38569 * Fires when a panel is added.
38570 * @param {Roo.LayoutRegion} this
38571 * @param {Roo.ContentPanel} panel The panel
38573 "paneladded" : true,
38575 * @event panelremoved
38576 * Fires when a panel is removed.
38577 * @param {Roo.LayoutRegion} this
38578 * @param {Roo.ContentPanel} panel The panel
38580 "panelremoved" : true,
38582 * @event beforecollapse
38583 * Fires when this region before collapse.
38584 * @param {Roo.LayoutRegion} this
38586 "beforecollapse" : true,
38589 * Fires when this region is collapsed.
38590 * @param {Roo.LayoutRegion} this
38592 "collapsed" : true,
38595 * Fires when this region is expanded.
38596 * @param {Roo.LayoutRegion} this
38601 * Fires when this region is slid into view.
38602 * @param {Roo.LayoutRegion} this
38604 "slideshow" : true,
38607 * Fires when this region slides out of view.
38608 * @param {Roo.LayoutRegion} this
38610 "slidehide" : true,
38612 * @event panelactivated
38613 * Fires when a panel is activated.
38614 * @param {Roo.LayoutRegion} this
38615 * @param {Roo.ContentPanel} panel The activated panel
38617 "panelactivated" : true,
38620 * Fires when the user resizes this region.
38621 * @param {Roo.LayoutRegion} this
38622 * @param {Number} newSize The new size (width for east/west, height for north/south)
38626 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38627 this.panels = new Roo.util.MixedCollection();
38628 this.panels.getKey = this.getPanelId.createDelegate(this);
38630 this.activePanel = null;
38631 // ensure listeners are added...
38633 if (config.listeners || config.events) {
38634 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38635 listeners : config.listeners || {},
38636 events : config.events || {}
38640 if(skipConfig !== true){
38641 this.applyConfig(config);
38645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38647 getPanelId : function(p){
38651 applyConfig : function(config){
38652 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38653 this.config = config;
38658 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38659 * the width, for horizontal (north, south) the height.
38660 * @param {Number} newSize The new width or height
38662 resizeTo : function(newSize){
38663 var el = this.el ? this.el :
38664 (this.activePanel ? this.activePanel.getEl() : null);
38666 switch(this.position){
38669 el.setWidth(newSize);
38670 this.fireEvent("resized", this, newSize);
38674 el.setHeight(newSize);
38675 this.fireEvent("resized", this, newSize);
38681 getBox : function(){
38682 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38685 getMargins : function(){
38686 return this.margins;
38689 updateBox : function(box){
38691 var el = this.activePanel.getEl();
38692 el.dom.style.left = box.x + "px";
38693 el.dom.style.top = box.y + "px";
38694 this.activePanel.setSize(box.width, box.height);
38698 * Returns the container element for this region.
38699 * @return {Roo.Element}
38701 getEl : function(){
38702 return this.activePanel;
38706 * Returns true if this region is currently visible.
38707 * @return {Boolean}
38709 isVisible : function(){
38710 return this.activePanel ? true : false;
38713 setActivePanel : function(panel){
38714 panel = this.getPanel(panel);
38715 if(this.activePanel && this.activePanel != panel){
38716 this.activePanel.setActiveState(false);
38717 this.activePanel.getEl().setLeftTop(-10000,-10000);
38719 this.activePanel = panel;
38720 panel.setActiveState(true);
38722 panel.setSize(this.box.width, this.box.height);
38724 this.fireEvent("panelactivated", this, panel);
38725 this.fireEvent("invalidated");
38729 * Show the specified panel.
38730 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38731 * @return {Roo.ContentPanel} The shown panel or null
38733 showPanel : function(panel){
38734 panel = this.getPanel(panel);
38736 this.setActivePanel(panel);
38742 * Get the active panel for this region.
38743 * @return {Roo.ContentPanel} The active panel or null
38745 getActivePanel : function(){
38746 return this.activePanel;
38750 * Add the passed ContentPanel(s)
38751 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38752 * @return {Roo.ContentPanel} The panel added (if only one was added)
38754 add : function(panel){
38755 if(arguments.length > 1){
38756 for(var i = 0, len = arguments.length; i < len; i++) {
38757 this.add(arguments[i]);
38761 if(this.hasPanel(panel)){
38762 this.showPanel(panel);
38765 var el = panel.getEl();
38766 if(el.dom.parentNode != this.mgr.el.dom){
38767 this.mgr.el.dom.appendChild(el.dom);
38769 if(panel.setRegion){
38770 panel.setRegion(this);
38772 this.panels.add(panel);
38773 el.setStyle("position", "absolute");
38774 if(!panel.background){
38775 this.setActivePanel(panel);
38776 if(this.config.initialSize && this.panels.getCount()==1){
38777 this.resizeTo(this.config.initialSize);
38780 this.fireEvent("paneladded", this, panel);
38785 * Returns true if the panel is in this region.
38786 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38787 * @return {Boolean}
38789 hasPanel : function(panel){
38790 if(typeof panel == "object"){ // must be panel obj
38791 panel = panel.getId();
38793 return this.getPanel(panel) ? true : false;
38797 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38799 * @param {Boolean} preservePanel Overrides the config preservePanel option
38800 * @return {Roo.ContentPanel} The panel that was removed
38802 remove : function(panel, preservePanel){
38803 panel = this.getPanel(panel);
38808 this.fireEvent("beforeremove", this, panel, e);
38809 if(e.cancel === true){
38812 var panelId = panel.getId();
38813 this.panels.removeKey(panelId);
38818 * Returns the panel specified or null if it's not in this region.
38819 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820 * @return {Roo.ContentPanel}
38822 getPanel : function(id){
38823 if(typeof id == "object"){ // must be panel obj
38826 return this.panels.get(id);
38830 * Returns this regions position (north/south/east/west/center).
38833 getPosition: function(){
38834 return this.position;
38838 * Ext JS Library 1.1.1
38839 * Copyright(c) 2006-2007, Ext JS, LLC.
38841 * Originally Released Under LGPL - original licence link has changed is not relivant.
38844 * <script type="text/javascript">
38848 * @class Roo.bootstrap.layout.Region
38849 * @extends Roo.bootstrap.layout.Basic
38850 * This class represents a region in a layout manager.
38852 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38853 * @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})
38854 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38855 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38856 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38857 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38858 * @cfg {String} title The title for the region (overrides panel titles)
38859 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38860 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38861 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38862 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38863 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38864 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38865 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38866 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38867 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38868 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38870 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38871 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38872 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38873 * @cfg {Number} width For East/West panels
38874 * @cfg {Number} height For North/South panels
38875 * @cfg {Boolean} split To show the splitter
38876 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38878 * @cfg {string} cls Extra CSS classes to add to region
38880 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38881 * @cfg {string} region the region that it inhabits..
38884 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38885 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38887 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38888 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38889 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38891 Roo.bootstrap.layout.Region = function(config)
38893 this.applyConfig(config);
38895 var mgr = config.mgr;
38896 var pos = config.region;
38897 config.skipConfig = true;
38898 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38901 this.onRender(mgr.el);
38904 this.visible = true;
38905 this.collapsed = false;
38906 this.unrendered_panels = [];
38909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38911 position: '', // set by wrapper (eg. north/south etc..)
38912 unrendered_panels : null, // unrendered panels.
38914 tabPosition : false,
38916 mgr: false, // points to 'Border'
38919 createBody : function(){
38920 /** This region's body element
38921 * @type Roo.Element */
38922 this.bodyEl = this.el.createChild({
38924 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38928 onRender: function(ctr, pos)
38930 var dh = Roo.DomHelper;
38931 /** This region's container element
38932 * @type Roo.Element */
38933 this.el = dh.append(ctr.dom, {
38935 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38937 /** This region's title element
38938 * @type Roo.Element */
38940 this.titleEl = dh.append(this.el.dom, {
38942 unselectable: "on",
38943 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38945 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38946 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38950 this.titleEl.enableDisplayMode();
38951 /** This region's title text element
38952 * @type HTMLElement */
38953 this.titleTextEl = this.titleEl.dom.firstChild;
38954 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38956 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38957 this.closeBtn.enableDisplayMode();
38958 this.closeBtn.on("click", this.closeClicked, this);
38959 this.closeBtn.hide();
38961 this.createBody(this.config);
38962 if(this.config.hideWhenEmpty){
38964 this.on("paneladded", this.validateVisibility, this);
38965 this.on("panelremoved", this.validateVisibility, this);
38967 if(this.autoScroll){
38968 this.bodyEl.setStyle("overflow", "auto");
38970 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38972 //if(c.titlebar !== false){
38973 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38974 this.titleEl.hide();
38976 this.titleEl.show();
38977 if(this.config.title){
38978 this.titleTextEl.innerHTML = this.config.title;
38982 if(this.config.collapsed){
38983 this.collapse(true);
38985 if(this.config.hidden){
38989 if (this.unrendered_panels && this.unrendered_panels.length) {
38990 for (var i =0;i< this.unrendered_panels.length; i++) {
38991 this.add(this.unrendered_panels[i]);
38993 this.unrendered_panels = null;
38999 applyConfig : function(c)
39002 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39003 var dh = Roo.DomHelper;
39004 if(c.titlebar !== false){
39005 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39006 this.collapseBtn.on("click", this.collapse, this);
39007 this.collapseBtn.enableDisplayMode();
39009 if(c.showPin === true || this.showPin){
39010 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39011 this.stickBtn.enableDisplayMode();
39012 this.stickBtn.on("click", this.expand, this);
39013 this.stickBtn.hide();
39018 /** This region's collapsed element
39019 * @type Roo.Element */
39022 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39023 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39026 if(c.floatable !== false){
39027 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39028 this.collapsedEl.on("click", this.collapseClick, this);
39031 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39032 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39033 id: "message", unselectable: "on", style:{"float":"left"}});
39034 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39036 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39037 this.expandBtn.on("click", this.expand, this);
39041 if(this.collapseBtn){
39042 this.collapseBtn.setVisible(c.collapsible == true);
39045 this.cmargins = c.cmargins || this.cmargins ||
39046 (this.position == "west" || this.position == "east" ?
39047 {top: 0, left: 2, right:2, bottom: 0} :
39048 {top: 2, left: 0, right:0, bottom: 2});
39050 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39053 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39055 this.autoScroll = c.autoScroll || false;
39060 this.duration = c.duration || .30;
39061 this.slideDuration = c.slideDuration || .45;
39066 * Returns true if this region is currently visible.
39067 * @return {Boolean}
39069 isVisible : function(){
39070 return this.visible;
39074 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39075 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39077 //setCollapsedTitle : function(title){
39078 // title = title || " ";
39079 // if(this.collapsedTitleTextEl){
39080 // this.collapsedTitleTextEl.innerHTML = title;
39084 getBox : function(){
39086 // if(!this.collapsed){
39087 b = this.el.getBox(false, true);
39089 // b = this.collapsedEl.getBox(false, true);
39094 getMargins : function(){
39095 return this.margins;
39096 //return this.collapsed ? this.cmargins : this.margins;
39099 highlight : function(){
39100 this.el.addClass("x-layout-panel-dragover");
39103 unhighlight : function(){
39104 this.el.removeClass("x-layout-panel-dragover");
39107 updateBox : function(box)
39109 if (!this.bodyEl) {
39110 return; // not rendered yet..
39114 if(!this.collapsed){
39115 this.el.dom.style.left = box.x + "px";
39116 this.el.dom.style.top = box.y + "px";
39117 this.updateBody(box.width, box.height);
39119 this.collapsedEl.dom.style.left = box.x + "px";
39120 this.collapsedEl.dom.style.top = box.y + "px";
39121 this.collapsedEl.setSize(box.width, box.height);
39124 this.tabs.autoSizeTabs();
39128 updateBody : function(w, h)
39131 this.el.setWidth(w);
39132 w -= this.el.getBorderWidth("rl");
39133 if(this.config.adjustments){
39134 w += this.config.adjustments[0];
39137 if(h !== null && h > 0){
39138 this.el.setHeight(h);
39139 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39140 h -= this.el.getBorderWidth("tb");
39141 if(this.config.adjustments){
39142 h += this.config.adjustments[1];
39144 this.bodyEl.setHeight(h);
39146 h = this.tabs.syncHeight(h);
39149 if(this.panelSize){
39150 w = w !== null ? w : this.panelSize.width;
39151 h = h !== null ? h : this.panelSize.height;
39153 if(this.activePanel){
39154 var el = this.activePanel.getEl();
39155 w = w !== null ? w : el.getWidth();
39156 h = h !== null ? h : el.getHeight();
39157 this.panelSize = {width: w, height: h};
39158 this.activePanel.setSize(w, h);
39160 if(Roo.isIE && this.tabs){
39161 this.tabs.el.repaint();
39166 * Returns the container element for this region.
39167 * @return {Roo.Element}
39169 getEl : function(){
39174 * Hides this region.
39177 //if(!this.collapsed){
39178 this.el.dom.style.left = "-2000px";
39181 // this.collapsedEl.dom.style.left = "-2000px";
39182 // this.collapsedEl.hide();
39184 this.visible = false;
39185 this.fireEvent("visibilitychange", this, false);
39189 * Shows this region if it was previously hidden.
39192 //if(!this.collapsed){
39195 // this.collapsedEl.show();
39197 this.visible = true;
39198 this.fireEvent("visibilitychange", this, true);
39201 closeClicked : function(){
39202 if(this.activePanel){
39203 this.remove(this.activePanel);
39207 collapseClick : function(e){
39209 e.stopPropagation();
39212 e.stopPropagation();
39218 * Collapses this region.
39219 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39222 collapse : function(skipAnim, skipCheck = false){
39223 if(this.collapsed) {
39227 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39229 this.collapsed = true;
39231 this.split.el.hide();
39233 if(this.config.animate && skipAnim !== true){
39234 this.fireEvent("invalidated", this);
39235 this.animateCollapse();
39237 this.el.setLocation(-20000,-20000);
39239 this.collapsedEl.show();
39240 this.fireEvent("collapsed", this);
39241 this.fireEvent("invalidated", this);
39247 animateCollapse : function(){
39252 * Expands this region if it was previously collapsed.
39253 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39254 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39257 expand : function(e, skipAnim){
39259 e.stopPropagation();
39261 if(!this.collapsed || this.el.hasActiveFx()) {
39265 this.afterSlideIn();
39268 this.collapsed = false;
39269 if(this.config.animate && skipAnim !== true){
39270 this.animateExpand();
39274 this.split.el.show();
39276 this.collapsedEl.setLocation(-2000,-2000);
39277 this.collapsedEl.hide();
39278 this.fireEvent("invalidated", this);
39279 this.fireEvent("expanded", this);
39283 animateExpand : function(){
39287 initTabs : function()
39289 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39291 var ts = new Roo.bootstrap.panel.Tabs({
39292 el: this.bodyEl.dom,
39294 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39295 disableTooltips: this.config.disableTabTips,
39296 toolbar : this.config.toolbar
39299 if(this.config.hideTabs){
39300 ts.stripWrap.setDisplayed(false);
39303 ts.resizeTabs = this.config.resizeTabs === true;
39304 ts.minTabWidth = this.config.minTabWidth || 40;
39305 ts.maxTabWidth = this.config.maxTabWidth || 250;
39306 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39307 ts.monitorResize = false;
39308 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39309 ts.bodyEl.addClass('roo-layout-tabs-body');
39310 this.panels.each(this.initPanelAsTab, this);
39313 initPanelAsTab : function(panel){
39314 var ti = this.tabs.addTab(
39318 this.config.closeOnTab && panel.isClosable(),
39321 if(panel.tabTip !== undefined){
39322 ti.setTooltip(panel.tabTip);
39324 ti.on("activate", function(){
39325 this.setActivePanel(panel);
39328 if(this.config.closeOnTab){
39329 ti.on("beforeclose", function(t, e){
39331 this.remove(panel);
39335 panel.tabItem = ti;
39340 updatePanelTitle : function(panel, title)
39342 if(this.activePanel == panel){
39343 this.updateTitle(title);
39346 var ti = this.tabs.getTab(panel.getEl().id);
39348 if(panel.tabTip !== undefined){
39349 ti.setTooltip(panel.tabTip);
39354 updateTitle : function(title){
39355 if(this.titleTextEl && !this.config.title){
39356 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39360 setActivePanel : function(panel)
39362 panel = this.getPanel(panel);
39363 if(this.activePanel && this.activePanel != panel){
39364 if(this.activePanel.setActiveState(false) === false){
39368 this.activePanel = panel;
39369 panel.setActiveState(true);
39370 if(this.panelSize){
39371 panel.setSize(this.panelSize.width, this.panelSize.height);
39374 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39376 this.updateTitle(panel.getTitle());
39378 this.fireEvent("invalidated", this);
39380 this.fireEvent("panelactivated", this, panel);
39384 * Shows the specified panel.
39385 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39386 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39388 showPanel : function(panel)
39390 panel = this.getPanel(panel);
39393 var tab = this.tabs.getTab(panel.getEl().id);
39394 if(tab.isHidden()){
39395 this.tabs.unhideTab(tab.id);
39399 this.setActivePanel(panel);
39406 * Get the active panel for this region.
39407 * @return {Roo.ContentPanel} The active panel or null
39409 getActivePanel : function(){
39410 return this.activePanel;
39413 validateVisibility : function(){
39414 if(this.panels.getCount() < 1){
39415 this.updateTitle(" ");
39416 this.closeBtn.hide();
39419 if(!this.isVisible()){
39426 * Adds the passed ContentPanel(s) to this region.
39427 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39428 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39430 add : function(panel)
39432 if(arguments.length > 1){
39433 for(var i = 0, len = arguments.length; i < len; i++) {
39434 this.add(arguments[i]);
39439 // if we have not been rendered yet, then we can not really do much of this..
39440 if (!this.bodyEl) {
39441 this.unrendered_panels.push(panel);
39448 if(this.hasPanel(panel)){
39449 this.showPanel(panel);
39452 panel.setRegion(this);
39453 this.panels.add(panel);
39454 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39455 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39456 // and hide them... ???
39457 this.bodyEl.dom.appendChild(panel.getEl().dom);
39458 if(panel.background !== true){
39459 this.setActivePanel(panel);
39461 this.fireEvent("paneladded", this, panel);
39468 this.initPanelAsTab(panel);
39472 if(panel.background !== true){
39473 this.tabs.activate(panel.getEl().id);
39475 this.fireEvent("paneladded", this, panel);
39480 * Hides the tab for the specified panel.
39481 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39483 hidePanel : function(panel){
39484 if(this.tabs && (panel = this.getPanel(panel))){
39485 this.tabs.hideTab(panel.getEl().id);
39490 * Unhides the tab for a previously hidden panel.
39491 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39493 unhidePanel : function(panel){
39494 if(this.tabs && (panel = this.getPanel(panel))){
39495 this.tabs.unhideTab(panel.getEl().id);
39499 clearPanels : function(){
39500 while(this.panels.getCount() > 0){
39501 this.remove(this.panels.first());
39506 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39507 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508 * @param {Boolean} preservePanel Overrides the config preservePanel option
39509 * @return {Roo.ContentPanel} The panel that was removed
39511 remove : function(panel, preservePanel)
39513 panel = this.getPanel(panel);
39518 this.fireEvent("beforeremove", this, panel, e);
39519 if(e.cancel === true){
39522 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39523 var panelId = panel.getId();
39524 this.panels.removeKey(panelId);
39526 document.body.appendChild(panel.getEl().dom);
39529 this.tabs.removeTab(panel.getEl().id);
39530 }else if (!preservePanel){
39531 this.bodyEl.dom.removeChild(panel.getEl().dom);
39533 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39534 var p = this.panels.first();
39535 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39536 tempEl.appendChild(p.getEl().dom);
39537 this.bodyEl.update("");
39538 this.bodyEl.dom.appendChild(p.getEl().dom);
39540 this.updateTitle(p.getTitle());
39542 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39543 this.setActivePanel(p);
39545 panel.setRegion(null);
39546 if(this.activePanel == panel){
39547 this.activePanel = null;
39549 if(this.config.autoDestroy !== false && preservePanel !== true){
39550 try{panel.destroy();}catch(e){}
39552 this.fireEvent("panelremoved", this, panel);
39557 * Returns the TabPanel component used by this region
39558 * @return {Roo.TabPanel}
39560 getTabs : function(){
39564 createTool : function(parentEl, className){
39565 var btn = Roo.DomHelper.append(parentEl, {
39567 cls: "x-layout-tools-button",
39570 cls: "roo-layout-tools-button-inner " + className,
39574 btn.addClassOnOver("roo-layout-tools-button-over");
39579 * Ext JS Library 1.1.1
39580 * Copyright(c) 2006-2007, Ext JS, LLC.
39582 * Originally Released Under LGPL - original licence link has changed is not relivant.
39585 * <script type="text/javascript">
39591 * @class Roo.SplitLayoutRegion
39592 * @extends Roo.LayoutRegion
39593 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39595 Roo.bootstrap.layout.Split = function(config){
39596 this.cursor = config.cursor;
39597 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39600 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39602 splitTip : "Drag to resize.",
39603 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39604 useSplitTips : false,
39606 applyConfig : function(config){
39607 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39610 onRender : function(ctr,pos) {
39612 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39613 if(!this.config.split){
39618 var splitEl = Roo.DomHelper.append(ctr.dom, {
39620 id: this.el.id + "-split",
39621 cls: "roo-layout-split roo-layout-split-"+this.position,
39624 /** The SplitBar for this region
39625 * @type Roo.SplitBar */
39626 // does not exist yet...
39627 Roo.log([this.position, this.orientation]);
39629 this.split = new Roo.bootstrap.SplitBar({
39630 dragElement : splitEl,
39631 resizingElement: this.el,
39632 orientation : this.orientation
39635 this.split.on("moved", this.onSplitMove, this);
39636 this.split.useShim = this.config.useShim === true;
39637 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39638 if(this.useSplitTips){
39639 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39641 //if(config.collapsible){
39642 // this.split.el.on("dblclick", this.collapse, this);
39645 if(typeof this.config.minSize != "undefined"){
39646 this.split.minSize = this.config.minSize;
39648 if(typeof this.config.maxSize != "undefined"){
39649 this.split.maxSize = this.config.maxSize;
39651 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39652 this.hideSplitter();
39657 getHMaxSize : function(){
39658 var cmax = this.config.maxSize || 10000;
39659 var center = this.mgr.getRegion("center");
39660 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39663 getVMaxSize : function(){
39664 var cmax = this.config.maxSize || 10000;
39665 var center = this.mgr.getRegion("center");
39666 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39669 onSplitMove : function(split, newSize){
39670 this.fireEvent("resized", this, newSize);
39674 * Returns the {@link Roo.SplitBar} for this region.
39675 * @return {Roo.SplitBar}
39677 getSplitBar : function(){
39682 this.hideSplitter();
39683 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39686 hideSplitter : function(){
39688 this.split.el.setLocation(-2000,-2000);
39689 this.split.el.hide();
39695 this.split.el.show();
39697 Roo.bootstrap.layout.Split.superclass.show.call(this);
39700 beforeSlide: function(){
39701 if(Roo.isGecko){// firefox overflow auto bug workaround
39702 this.bodyEl.clip();
39704 this.tabs.bodyEl.clip();
39706 if(this.activePanel){
39707 this.activePanel.getEl().clip();
39709 if(this.activePanel.beforeSlide){
39710 this.activePanel.beforeSlide();
39716 afterSlide : function(){
39717 if(Roo.isGecko){// firefox overflow auto bug workaround
39718 this.bodyEl.unclip();
39720 this.tabs.bodyEl.unclip();
39722 if(this.activePanel){
39723 this.activePanel.getEl().unclip();
39724 if(this.activePanel.afterSlide){
39725 this.activePanel.afterSlide();
39731 initAutoHide : function(){
39732 if(this.autoHide !== false){
39733 if(!this.autoHideHd){
39734 var st = new Roo.util.DelayedTask(this.slideIn, this);
39735 this.autoHideHd = {
39736 "mouseout": function(e){
39737 if(!e.within(this.el, true)){
39741 "mouseover" : function(e){
39747 this.el.on(this.autoHideHd);
39751 clearAutoHide : function(){
39752 if(this.autoHide !== false){
39753 this.el.un("mouseout", this.autoHideHd.mouseout);
39754 this.el.un("mouseover", this.autoHideHd.mouseover);
39758 clearMonitor : function(){
39759 Roo.get(document).un("click", this.slideInIf, this);
39762 // these names are backwards but not changed for compat
39763 slideOut : function(){
39764 if(this.isSlid || this.el.hasActiveFx()){
39767 this.isSlid = true;
39768 if(this.collapseBtn){
39769 this.collapseBtn.hide();
39771 this.closeBtnState = this.closeBtn.getStyle('display');
39772 this.closeBtn.hide();
39774 this.stickBtn.show();
39777 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39778 this.beforeSlide();
39779 this.el.setStyle("z-index", 10001);
39780 this.el.slideIn(this.getSlideAnchor(), {
39781 callback: function(){
39783 this.initAutoHide();
39784 Roo.get(document).on("click", this.slideInIf, this);
39785 this.fireEvent("slideshow", this);
39792 afterSlideIn : function(){
39793 this.clearAutoHide();
39794 this.isSlid = false;
39795 this.clearMonitor();
39796 this.el.setStyle("z-index", "");
39797 if(this.collapseBtn){
39798 this.collapseBtn.show();
39800 this.closeBtn.setStyle('display', this.closeBtnState);
39802 this.stickBtn.hide();
39804 this.fireEvent("slidehide", this);
39807 slideIn : function(cb){
39808 if(!this.isSlid || this.el.hasActiveFx()){
39812 this.isSlid = false;
39813 this.beforeSlide();
39814 this.el.slideOut(this.getSlideAnchor(), {
39815 callback: function(){
39816 this.el.setLeftTop(-10000, -10000);
39818 this.afterSlideIn();
39826 slideInIf : function(e){
39827 if(!e.within(this.el)){
39832 animateCollapse : function(){
39833 this.beforeSlide();
39834 this.el.setStyle("z-index", 20000);
39835 var anchor = this.getSlideAnchor();
39836 this.el.slideOut(anchor, {
39837 callback : function(){
39838 this.el.setStyle("z-index", "");
39839 this.collapsedEl.slideIn(anchor, {duration:.3});
39841 this.el.setLocation(-10000,-10000);
39843 this.fireEvent("collapsed", this);
39850 animateExpand : function(){
39851 this.beforeSlide();
39852 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39853 this.el.setStyle("z-index", 20000);
39854 this.collapsedEl.hide({
39857 this.el.slideIn(this.getSlideAnchor(), {
39858 callback : function(){
39859 this.el.setStyle("z-index", "");
39862 this.split.el.show();
39864 this.fireEvent("invalidated", this);
39865 this.fireEvent("expanded", this);
39893 getAnchor : function(){
39894 return this.anchors[this.position];
39897 getCollapseAnchor : function(){
39898 return this.canchors[this.position];
39901 getSlideAnchor : function(){
39902 return this.sanchors[this.position];
39905 getAlignAdj : function(){
39906 var cm = this.cmargins;
39907 switch(this.position){
39923 getExpandAdj : function(){
39924 var c = this.collapsedEl, cm = this.cmargins;
39925 switch(this.position){
39927 return [-(cm.right+c.getWidth()+cm.left), 0];
39930 return [cm.right+c.getWidth()+cm.left, 0];
39933 return [0, -(cm.top+cm.bottom+c.getHeight())];
39936 return [0, cm.top+cm.bottom+c.getHeight()];
39942 * Ext JS Library 1.1.1
39943 * Copyright(c) 2006-2007, Ext JS, LLC.
39945 * Originally Released Under LGPL - original licence link has changed is not relivant.
39948 * <script type="text/javascript">
39951 * These classes are private internal classes
39953 Roo.bootstrap.layout.Center = function(config){
39954 config.region = "center";
39955 Roo.bootstrap.layout.Region.call(this, config);
39956 this.visible = true;
39957 this.minWidth = config.minWidth || 20;
39958 this.minHeight = config.minHeight || 20;
39961 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39963 // center panel can't be hidden
39967 // center panel can't be hidden
39970 getMinWidth: function(){
39971 return this.minWidth;
39974 getMinHeight: function(){
39975 return this.minHeight;
39989 Roo.bootstrap.layout.North = function(config)
39991 config.region = 'north';
39992 config.cursor = 'n-resize';
39994 Roo.bootstrap.layout.Split.call(this, config);
39998 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39999 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40000 this.split.el.addClass("roo-layout-split-v");
40002 //var size = config.initialSize || config.height;
40003 //if(this.el && typeof size != "undefined"){
40004 // this.el.setHeight(size);
40007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40009 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40012 onRender : function(ctr, pos)
40014 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40015 var size = this.config.initialSize || this.config.height;
40016 if(this.el && typeof size != "undefined"){
40017 this.el.setHeight(size);
40022 getBox : function(){
40023 if(this.collapsed){
40024 return this.collapsedEl.getBox();
40026 var box = this.el.getBox();
40028 box.height += this.split.el.getHeight();
40033 updateBox : function(box){
40034 if(this.split && !this.collapsed){
40035 box.height -= this.split.el.getHeight();
40036 this.split.el.setLeft(box.x);
40037 this.split.el.setTop(box.y+box.height);
40038 this.split.el.setWidth(box.width);
40040 if(this.collapsed){
40041 this.updateBody(box.width, null);
40043 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40051 Roo.bootstrap.layout.South = function(config){
40052 config.region = 'south';
40053 config.cursor = 's-resize';
40054 Roo.bootstrap.layout.Split.call(this, config);
40056 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40057 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40058 this.split.el.addClass("roo-layout-split-v");
40063 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40064 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40066 onRender : function(ctr, pos)
40068 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40069 var size = this.config.initialSize || this.config.height;
40070 if(this.el && typeof size != "undefined"){
40071 this.el.setHeight(size);
40076 getBox : function(){
40077 if(this.collapsed){
40078 return this.collapsedEl.getBox();
40080 var box = this.el.getBox();
40082 var sh = this.split.el.getHeight();
40089 updateBox : function(box){
40090 if(this.split && !this.collapsed){
40091 var sh = this.split.el.getHeight();
40094 this.split.el.setLeft(box.x);
40095 this.split.el.setTop(box.y-sh);
40096 this.split.el.setWidth(box.width);
40098 if(this.collapsed){
40099 this.updateBody(box.width, null);
40101 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40105 Roo.bootstrap.layout.East = function(config){
40106 config.region = "east";
40107 config.cursor = "e-resize";
40108 Roo.bootstrap.layout.Split.call(this, config);
40110 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40111 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40112 this.split.el.addClass("roo-layout-split-h");
40116 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40117 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40119 onRender : function(ctr, pos)
40121 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40122 var size = this.config.initialSize || this.config.width;
40123 if(this.el && typeof size != "undefined"){
40124 this.el.setWidth(size);
40129 getBox : function(){
40130 if(this.collapsed){
40131 return this.collapsedEl.getBox();
40133 var box = this.el.getBox();
40135 var sw = this.split.el.getWidth();
40142 updateBox : function(box){
40143 if(this.split && !this.collapsed){
40144 var sw = this.split.el.getWidth();
40146 this.split.el.setLeft(box.x);
40147 this.split.el.setTop(box.y);
40148 this.split.el.setHeight(box.height);
40151 if(this.collapsed){
40152 this.updateBody(null, box.height);
40154 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40158 Roo.bootstrap.layout.West = function(config){
40159 config.region = "west";
40160 config.cursor = "w-resize";
40162 Roo.bootstrap.layout.Split.call(this, config);
40164 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40165 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40166 this.split.el.addClass("roo-layout-split-h");
40170 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40171 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40173 onRender: function(ctr, pos)
40175 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40176 var size = this.config.initialSize || this.config.width;
40177 if(typeof size != "undefined"){
40178 this.el.setWidth(size);
40182 getBox : function(){
40183 if(this.collapsed){
40184 return this.collapsedEl.getBox();
40186 var box = this.el.getBox();
40187 if (box.width == 0) {
40188 box.width = this.config.width; // kludge?
40191 box.width += this.split.el.getWidth();
40196 updateBox : function(box){
40197 if(this.split && !this.collapsed){
40198 var sw = this.split.el.getWidth();
40200 this.split.el.setLeft(box.x+box.width);
40201 this.split.el.setTop(box.y);
40202 this.split.el.setHeight(box.height);
40204 if(this.collapsed){
40205 this.updateBody(null, box.height);
40207 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40209 });Roo.namespace("Roo.bootstrap.panel");/*
40211 * Ext JS Library 1.1.1
40212 * Copyright(c) 2006-2007, Ext JS, LLC.
40214 * Originally Released Under LGPL - original licence link has changed is not relivant.
40217 * <script type="text/javascript">
40220 * @class Roo.ContentPanel
40221 * @extends Roo.util.Observable
40222 * A basic ContentPanel element.
40223 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40224 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40225 * @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
40226 * @cfg {Boolean} closable True if the panel can be closed/removed
40227 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40228 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40229 * @cfg {Toolbar} toolbar A toolbar for this panel
40230 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40231 * @cfg {String} title The title for this panel
40232 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40233 * @cfg {String} url Calls {@link #setUrl} with this value
40234 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40235 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40236 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40237 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40238 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40239 * @cfg {Boolean} badges render the badges
40240 * @cfg {String} cls extra classes to use
40241 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40244 * Create a new ContentPanel.
40245 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40246 * @param {String/Object} config A string to set only the title or a config object
40247 * @param {String} content (optional) Set the HTML content for this panel
40248 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40250 Roo.bootstrap.panel.Content = function( config){
40252 this.tpl = config.tpl || false;
40254 var el = config.el;
40255 var content = config.content;
40257 if(config.autoCreate){ // xtype is available if this is called from factory
40260 this.el = Roo.get(el);
40261 if(!this.el && config && config.autoCreate){
40262 if(typeof config.autoCreate == "object"){
40263 if(!config.autoCreate.id){
40264 config.autoCreate.id = config.id||el;
40266 this.el = Roo.DomHelper.append(document.body,
40267 config.autoCreate, true);
40271 cls: (config.cls || '') +
40272 (config.background ? ' bg-' + config.background : '') +
40273 " roo-layout-inactive-content",
40276 if (config.iframe) {
40280 style : 'border: 0px',
40281 src : 'about:blank'
40287 elcfg.html = config.html;
40291 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40292 if (config.iframe) {
40293 this.iframeEl = this.el.select('iframe',true).first();
40298 this.closable = false;
40299 this.loaded = false;
40300 this.active = false;
40303 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40305 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40307 this.wrapEl = this.el; //this.el.wrap();
40309 if (config.toolbar.items) {
40310 ti = config.toolbar.items ;
40311 delete config.toolbar.items ;
40315 this.toolbar.render(this.wrapEl, 'before');
40316 for(var i =0;i < ti.length;i++) {
40317 // Roo.log(['add child', items[i]]);
40318 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40320 this.toolbar.items = nitems;
40321 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40322 delete config.toolbar;
40326 // xtype created footer. - not sure if will work as we normally have to render first..
40327 if (this.footer && !this.footer.el && this.footer.xtype) {
40328 if (!this.wrapEl) {
40329 this.wrapEl = this.el.wrap();
40332 this.footer.container = this.wrapEl.createChild();
40334 this.footer = Roo.factory(this.footer, Roo);
40339 if(typeof config == "string"){
40340 this.title = config;
40342 Roo.apply(this, config);
40346 this.resizeEl = Roo.get(this.resizeEl, true);
40348 this.resizeEl = this.el;
40350 // handle view.xtype
40358 * Fires when this panel is activated.
40359 * @param {Roo.ContentPanel} this
40363 * @event deactivate
40364 * Fires when this panel is activated.
40365 * @param {Roo.ContentPanel} this
40367 "deactivate" : true,
40371 * Fires when this panel is resized if fitToFrame is true.
40372 * @param {Roo.ContentPanel} this
40373 * @param {Number} width The width after any component adjustments
40374 * @param {Number} height The height after any component adjustments
40380 * Fires when this tab is created
40381 * @param {Roo.ContentPanel} this
40387 * Fires when this content is scrolled
40388 * @param {Roo.ContentPanel} this
40389 * @param {Event} scrollEvent
40400 if(this.autoScroll && !this.iframe){
40401 this.resizeEl.setStyle("overflow", "auto");
40402 this.resizeEl.on('scroll', this.onScroll, this);
40404 // fix randome scrolling
40405 //this.el.on('scroll', function() {
40406 // Roo.log('fix random scolling');
40407 // this.scrollTo('top',0);
40410 content = content || this.content;
40412 this.setContent(content);
40414 if(config && config.url){
40415 this.setUrl(this.url, this.params, this.loadOnce);
40420 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40422 if (this.view && typeof(this.view.xtype) != 'undefined') {
40423 this.view.el = this.el.appendChild(document.createElement("div"));
40424 this.view = Roo.factory(this.view);
40425 this.view.render && this.view.render(false, '');
40429 this.fireEvent('render', this);
40432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40442 /* Resize Element - use this to work out scroll etc. */
40445 setRegion : function(region){
40446 this.region = region;
40447 this.setActiveClass(region && !this.background);
40451 setActiveClass: function(state)
40454 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40455 this.el.setStyle('position','relative');
40457 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40458 this.el.setStyle('position', 'absolute');
40463 * Returns the toolbar for this Panel if one was configured.
40464 * @return {Roo.Toolbar}
40466 getToolbar : function(){
40467 return this.toolbar;
40470 setActiveState : function(active)
40472 this.active = active;
40473 this.setActiveClass(active);
40475 if(this.fireEvent("deactivate", this) === false){
40480 this.fireEvent("activate", this);
40484 * Updates this panel's element (not for iframe)
40485 * @param {String} content The new content
40486 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40488 setContent : function(content, loadScripts){
40493 this.el.update(content, loadScripts);
40496 ignoreResize : function(w, h){
40497 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40500 this.lastSize = {width: w, height: h};
40505 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40506 * @return {Roo.UpdateManager} The UpdateManager
40508 getUpdateManager : function(){
40512 return this.el.getUpdateManager();
40515 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40516 * Does not work with IFRAME contents
40517 * @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:
40520 url: "your-url.php",
40521 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40522 callback: yourFunction,
40523 scope: yourObject, //(optional scope)
40526 text: "Loading...",
40532 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40533 * 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.
40534 * @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}
40535 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40536 * @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.
40537 * @return {Roo.ContentPanel} this
40545 var um = this.el.getUpdateManager();
40546 um.update.apply(um, arguments);
40552 * 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.
40553 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40554 * @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)
40555 * @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)
40556 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40558 setUrl : function(url, params, loadOnce){
40560 this.iframeEl.dom.src = url;
40564 if(this.refreshDelegate){
40565 this.removeListener("activate", this.refreshDelegate);
40567 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40568 this.on("activate", this.refreshDelegate);
40569 return this.el.getUpdateManager();
40572 _handleRefresh : function(url, params, loadOnce){
40573 if(!loadOnce || !this.loaded){
40574 var updater = this.el.getUpdateManager();
40575 updater.update(url, params, this._setLoaded.createDelegate(this));
40579 _setLoaded : function(){
40580 this.loaded = true;
40584 * Returns this panel's id
40587 getId : function(){
40592 * Returns this panel's element - used by regiosn to add.
40593 * @return {Roo.Element}
40595 getEl : function(){
40596 return this.wrapEl || this.el;
40601 adjustForComponents : function(width, height)
40603 //Roo.log('adjustForComponents ');
40604 if(this.resizeEl != this.el){
40605 width -= this.el.getFrameWidth('lr');
40606 height -= this.el.getFrameWidth('tb');
40609 var te = this.toolbar.getEl();
40610 te.setWidth(width);
40611 height -= te.getHeight();
40614 var te = this.footer.getEl();
40615 te.setWidth(width);
40616 height -= te.getHeight();
40620 if(this.adjustments){
40621 width += this.adjustments[0];
40622 height += this.adjustments[1];
40624 return {"width": width, "height": height};
40627 setSize : function(width, height){
40628 if(this.fitToFrame && !this.ignoreResize(width, height)){
40629 if(this.fitContainer && this.resizeEl != this.el){
40630 this.el.setSize(width, height);
40632 var size = this.adjustForComponents(width, height);
40634 this.iframeEl.setSize(width,height);
40637 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40638 this.fireEvent('resize', this, size.width, size.height);
40645 * Returns this panel's title
40648 getTitle : function(){
40650 if (typeof(this.title) != 'object') {
40655 for (var k in this.title) {
40656 if (!this.title.hasOwnProperty(k)) {
40660 if (k.indexOf('-') >= 0) {
40661 var s = k.split('-');
40662 for (var i = 0; i<s.length; i++) {
40663 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40666 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40673 * Set this panel's title
40674 * @param {String} title
40676 setTitle : function(title){
40677 this.title = title;
40679 this.region.updatePanelTitle(this, title);
40684 * Returns true is this panel was configured to be closable
40685 * @return {Boolean}
40687 isClosable : function(){
40688 return this.closable;
40691 beforeSlide : function(){
40693 this.resizeEl.clip();
40696 afterSlide : function(){
40698 this.resizeEl.unclip();
40702 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40703 * Will fail silently if the {@link #setUrl} method has not been called.
40704 * This does not activate the panel, just updates its content.
40706 refresh : function(){
40707 if(this.refreshDelegate){
40708 this.loaded = false;
40709 this.refreshDelegate();
40714 * Destroys this panel
40716 destroy : function(){
40717 this.el.removeAllListeners();
40718 var tempEl = document.createElement("span");
40719 tempEl.appendChild(this.el.dom);
40720 tempEl.innerHTML = "";
40726 * form - if the content panel contains a form - this is a reference to it.
40727 * @type {Roo.form.Form}
40731 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40732 * This contains a reference to it.
40738 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40748 * @param {Object} cfg Xtype definition of item to add.
40752 getChildContainer: function () {
40753 return this.getEl();
40757 onScroll : function(e)
40759 this.fireEvent('scroll', this, e);
40764 var ret = new Roo.factory(cfg);
40769 if (cfg.xtype.match(/^Form$/)) {
40772 //if (this.footer) {
40773 // el = this.footer.container.insertSibling(false, 'before');
40775 el = this.el.createChild();
40778 this.form = new Roo.form.Form(cfg);
40781 if ( this.form.allItems.length) {
40782 this.form.render(el.dom);
40786 // should only have one of theses..
40787 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40788 // views.. should not be just added - used named prop 'view''
40790 cfg.el = this.el.appendChild(document.createElement("div"));
40793 var ret = new Roo.factory(cfg);
40795 ret.render && ret.render(false, ''); // render blank..
40805 * @class Roo.bootstrap.panel.Grid
40806 * @extends Roo.bootstrap.panel.Content
40808 * Create a new GridPanel.
40809 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40810 * @param {Object} config A the config object
40816 Roo.bootstrap.panel.Grid = function(config)
40820 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40821 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40823 config.el = this.wrapper;
40824 //this.el = this.wrapper;
40826 if (config.container) {
40827 // ctor'ed from a Border/panel.grid
40830 this.wrapper.setStyle("overflow", "hidden");
40831 this.wrapper.addClass('roo-grid-container');
40836 if(config.toolbar){
40837 var tool_el = this.wrapper.createChild();
40838 this.toolbar = Roo.factory(config.toolbar);
40840 if (config.toolbar.items) {
40841 ti = config.toolbar.items ;
40842 delete config.toolbar.items ;
40846 this.toolbar.render(tool_el);
40847 for(var i =0;i < ti.length;i++) {
40848 // Roo.log(['add child', items[i]]);
40849 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40851 this.toolbar.items = nitems;
40853 delete config.toolbar;
40856 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40857 config.grid.scrollBody = true;;
40858 config.grid.monitorWindowResize = false; // turn off autosizing
40859 config.grid.autoHeight = false;
40860 config.grid.autoWidth = false;
40862 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40864 if (config.background) {
40865 // render grid on panel activation (if panel background)
40866 this.on('activate', function(gp) {
40867 if (!gp.grid.rendered) {
40868 gp.grid.render(this.wrapper);
40869 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40874 this.grid.render(this.wrapper);
40875 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40878 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40879 // ??? needed ??? config.el = this.wrapper;
40884 // xtype created footer. - not sure if will work as we normally have to render first..
40885 if (this.footer && !this.footer.el && this.footer.xtype) {
40887 var ctr = this.grid.getView().getFooterPanel(true);
40888 this.footer.dataSource = this.grid.dataSource;
40889 this.footer = Roo.factory(this.footer, Roo);
40890 this.footer.render(ctr);
40900 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40901 getId : function(){
40902 return this.grid.id;
40906 * Returns the grid for this panel
40907 * @return {Roo.bootstrap.Table}
40909 getGrid : function(){
40913 setSize : function(width, height){
40914 if(!this.ignoreResize(width, height)){
40915 var grid = this.grid;
40916 var size = this.adjustForComponents(width, height);
40917 // tfoot is not a footer?
40920 var gridel = grid.getGridEl();
40921 gridel.setSize(size.width, size.height);
40923 var tbd = grid.getGridEl().select('tbody', true).first();
40924 var thd = grid.getGridEl().select('thead',true).first();
40925 var tbf= grid.getGridEl().select('tfoot', true).first();
40928 size.height -= tbf.getHeight();
40931 size.height -= thd.getHeight();
40934 tbd.setSize(size.width, size.height );
40935 // this is for the account management tab -seems to work there.
40936 var thd = grid.getGridEl().select('thead',true).first();
40938 // tbd.setSize(size.width, size.height - thd.getHeight());
40947 beforeSlide : function(){
40948 this.grid.getView().scroller.clip();
40951 afterSlide : function(){
40952 this.grid.getView().scroller.unclip();
40955 destroy : function(){
40956 this.grid.destroy();
40958 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40963 * @class Roo.bootstrap.panel.Nest
40964 * @extends Roo.bootstrap.panel.Content
40966 * Create a new Panel, that can contain a layout.Border.
40969 * @param {Roo.BorderLayout} layout The layout for this panel
40970 * @param {String/Object} config A string to set only the title or a config object
40972 Roo.bootstrap.panel.Nest = function(config)
40974 // construct with only one argument..
40975 /* FIXME - implement nicer consturctors
40976 if (layout.layout) {
40978 layout = config.layout;
40979 delete config.layout;
40981 if (layout.xtype && !layout.getEl) {
40982 // then layout needs constructing..
40983 layout = Roo.factory(layout, Roo);
40987 config.el = config.layout.getEl();
40989 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40991 config.layout.monitorWindowResize = false; // turn off autosizing
40992 this.layout = config.layout;
40993 this.layout.getEl().addClass("roo-layout-nested-layout");
40994 this.layout.parent = this;
41001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41003 setSize : function(width, height){
41004 if(!this.ignoreResize(width, height)){
41005 var size = this.adjustForComponents(width, height);
41006 var el = this.layout.getEl();
41007 if (size.height < 1) {
41008 el.setWidth(size.width);
41010 el.setSize(size.width, size.height);
41012 var touch = el.dom.offsetWidth;
41013 this.layout.layout();
41014 // ie requires a double layout on the first pass
41015 if(Roo.isIE && !this.initialized){
41016 this.initialized = true;
41017 this.layout.layout();
41022 // activate all subpanels if not currently active..
41024 setActiveState : function(active){
41025 this.active = active;
41026 this.setActiveClass(active);
41029 this.fireEvent("deactivate", this);
41033 this.fireEvent("activate", this);
41034 // not sure if this should happen before or after..
41035 if (!this.layout) {
41036 return; // should not happen..
41039 for (var r in this.layout.regions) {
41040 reg = this.layout.getRegion(r);
41041 if (reg.getActivePanel()) {
41042 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41043 reg.setActivePanel(reg.getActivePanel());
41046 if (!reg.panels.length) {
41049 reg.showPanel(reg.getPanel(0));
41058 * Returns the nested BorderLayout for this panel
41059 * @return {Roo.BorderLayout}
41061 getLayout : function(){
41062 return this.layout;
41066 * Adds a xtype elements to the layout of the nested panel
41070 xtype : 'ContentPanel',
41077 xtype : 'NestedLayoutPanel',
41083 items : [ ... list of content panels or nested layout panels.. ]
41087 * @param {Object} cfg Xtype definition of item to add.
41089 addxtype : function(cfg) {
41090 return this.layout.addxtype(cfg);
41095 * Ext JS Library 1.1.1
41096 * Copyright(c) 2006-2007, Ext JS, LLC.
41098 * Originally Released Under LGPL - original licence link has changed is not relivant.
41101 * <script type="text/javascript">
41104 * @class Roo.TabPanel
41105 * @extends Roo.util.Observable
41106 * A lightweight tab container.
41110 // basic tabs 1, built from existing content
41111 var tabs = new Roo.TabPanel("tabs1");
41112 tabs.addTab("script", "View Script");
41113 tabs.addTab("markup", "View Markup");
41114 tabs.activate("script");
41116 // more advanced tabs, built from javascript
41117 var jtabs = new Roo.TabPanel("jtabs");
41118 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41120 // set up the UpdateManager
41121 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41122 var updater = tab2.getUpdateManager();
41123 updater.setDefaultUrl("ajax1.htm");
41124 tab2.on('activate', updater.refresh, updater, true);
41126 // Use setUrl for Ajax loading
41127 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41128 tab3.setUrl("ajax2.htm", null, true);
41131 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41134 jtabs.activate("jtabs-1");
41137 * Create a new TabPanel.
41138 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41139 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41141 Roo.bootstrap.panel.Tabs = function(config){
41143 * The container element for this TabPanel.
41144 * @type Roo.Element
41146 this.el = Roo.get(config.el);
41149 if(typeof config == "boolean"){
41150 this.tabPosition = config ? "bottom" : "top";
41152 Roo.apply(this, config);
41156 if(this.tabPosition == "bottom"){
41157 // if tabs are at the bottom = create the body first.
41158 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41159 this.el.addClass("roo-tabs-bottom");
41161 // next create the tabs holders
41163 if (this.tabPosition == "west"){
41165 var reg = this.region; // fake it..
41167 if (!reg.mgr.parent) {
41170 reg = reg.mgr.parent.region;
41172 Roo.log("got nest?");
41174 if (reg.mgr.getRegion('west')) {
41175 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41176 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41177 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41178 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41179 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41187 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41188 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41189 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41190 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41195 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41198 // finally - if tabs are at the top, then create the body last..
41199 if(this.tabPosition != "bottom"){
41200 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41201 * @type Roo.Element
41203 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41204 this.el.addClass("roo-tabs-top");
41208 this.bodyEl.setStyle("position", "relative");
41210 this.active = null;
41211 this.activateDelegate = this.activate.createDelegate(this);
41216 * Fires when the active tab changes
41217 * @param {Roo.TabPanel} this
41218 * @param {Roo.TabPanelItem} activePanel The new active tab
41222 * @event beforetabchange
41223 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41224 * @param {Roo.TabPanel} this
41225 * @param {Object} e Set cancel to true on this object to cancel the tab change
41226 * @param {Roo.TabPanelItem} tab The tab being changed to
41228 "beforetabchange" : true
41231 Roo.EventManager.onWindowResize(this.onResize, this);
41232 this.cpad = this.el.getPadding("lr");
41233 this.hiddenCount = 0;
41236 // toolbar on the tabbar support...
41237 if (this.toolbar) {
41238 alert("no toolbar support yet");
41239 this.toolbar = false;
41241 var tcfg = this.toolbar;
41242 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41243 this.toolbar = new Roo.Toolbar(tcfg);
41244 if (Roo.isSafari) {
41245 var tbl = tcfg.container.child('table', true);
41246 tbl.setAttribute('width', '100%');
41254 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41257 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41259 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41261 tabPosition : "top",
41263 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41265 currentTabWidth : 0,
41267 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41271 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41275 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41277 preferredTabWidth : 175,
41279 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41281 resizeTabs : false,
41283 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41285 monitorResize : true,
41287 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41289 toolbar : false, // set by caller..
41291 region : false, /// set by caller
41293 disableTooltips : true, // not used yet...
41296 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41297 * @param {String} id The id of the div to use <b>or create</b>
41298 * @param {String} text The text for the tab
41299 * @param {String} content (optional) Content to put in the TabPanelItem body
41300 * @param {Boolean} closable (optional) True to create a close icon on the tab
41301 * @return {Roo.TabPanelItem} The created TabPanelItem
41303 addTab : function(id, text, content, closable, tpl)
41305 var item = new Roo.bootstrap.panel.TabItem({
41309 closable : closable,
41312 this.addTabItem(item);
41314 item.setContent(content);
41320 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41321 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41322 * @return {Roo.TabPanelItem}
41324 getTab : function(id){
41325 return this.items[id];
41329 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41330 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41332 hideTab : function(id){
41333 var t = this.items[id];
41336 this.hiddenCount++;
41337 this.autoSizeTabs();
41342 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41343 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41345 unhideTab : function(id){
41346 var t = this.items[id];
41348 t.setHidden(false);
41349 this.hiddenCount--;
41350 this.autoSizeTabs();
41355 * Adds an existing {@link Roo.TabPanelItem}.
41356 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41358 addTabItem : function(item)
41360 this.items[item.id] = item;
41361 this.items.push(item);
41362 this.autoSizeTabs();
41363 // if(this.resizeTabs){
41364 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41365 // this.autoSizeTabs();
41367 // item.autoSize();
41372 * Removes a {@link Roo.TabPanelItem}.
41373 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41375 removeTab : function(id){
41376 var items = this.items;
41377 var tab = items[id];
41378 if(!tab) { return; }
41379 var index = items.indexOf(tab);
41380 if(this.active == tab && items.length > 1){
41381 var newTab = this.getNextAvailable(index);
41386 this.stripEl.dom.removeChild(tab.pnode.dom);
41387 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41388 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41390 items.splice(index, 1);
41391 delete this.items[tab.id];
41392 tab.fireEvent("close", tab);
41393 tab.purgeListeners();
41394 this.autoSizeTabs();
41397 getNextAvailable : function(start){
41398 var items = this.items;
41400 // look for a next tab that will slide over to
41401 // replace the one being removed
41402 while(index < items.length){
41403 var item = items[++index];
41404 if(item && !item.isHidden()){
41408 // if one isn't found select the previous tab (on the left)
41411 var item = items[--index];
41412 if(item && !item.isHidden()){
41420 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41421 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41423 disableTab : function(id){
41424 var tab = this.items[id];
41425 if(tab && this.active != tab){
41431 * Enables a {@link Roo.TabPanelItem} that is disabled.
41432 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41434 enableTab : function(id){
41435 var tab = this.items[id];
41440 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41441 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41442 * @return {Roo.TabPanelItem} The TabPanelItem.
41444 activate : function(id)
41446 //Roo.log('activite:' + id);
41448 var tab = this.items[id];
41452 if(tab == this.active || tab.disabled){
41456 this.fireEvent("beforetabchange", this, e, tab);
41457 if(e.cancel !== true && !tab.disabled){
41459 this.active.hide();
41461 this.active = this.items[id];
41462 this.active.show();
41463 this.fireEvent("tabchange", this, this.active);
41469 * Gets the active {@link Roo.TabPanelItem}.
41470 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41472 getActiveTab : function(){
41473 return this.active;
41477 * Updates the tab body element to fit the height of the container element
41478 * for overflow scrolling
41479 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41481 syncHeight : function(targetHeight){
41482 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41483 var bm = this.bodyEl.getMargins();
41484 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41485 this.bodyEl.setHeight(newHeight);
41489 onResize : function(){
41490 if(this.monitorResize){
41491 this.autoSizeTabs();
41496 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41498 beginUpdate : function(){
41499 this.updating = true;
41503 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41505 endUpdate : function(){
41506 this.updating = false;
41507 this.autoSizeTabs();
41511 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41513 autoSizeTabs : function()
41515 var count = this.items.length;
41516 var vcount = count - this.hiddenCount;
41519 this.stripEl.hide();
41521 this.stripEl.show();
41524 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41529 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41530 var availWidth = Math.floor(w / vcount);
41531 var b = this.stripBody;
41532 if(b.getWidth() > w){
41533 var tabs = this.items;
41534 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41535 if(availWidth < this.minTabWidth){
41536 /*if(!this.sleft){ // incomplete scrolling code
41537 this.createScrollButtons();
41540 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41543 if(this.currentTabWidth < this.preferredTabWidth){
41544 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41550 * Returns the number of tabs in this TabPanel.
41553 getCount : function(){
41554 return this.items.length;
41558 * Resizes all the tabs to the passed width
41559 * @param {Number} The new width
41561 setTabWidth : function(width){
41562 this.currentTabWidth = width;
41563 for(var i = 0, len = this.items.length; i < len; i++) {
41564 if(!this.items[i].isHidden()) {
41565 this.items[i].setWidth(width);
41571 * Destroys this TabPanel
41572 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41574 destroy : function(removeEl){
41575 Roo.EventManager.removeResizeListener(this.onResize, this);
41576 for(var i = 0, len = this.items.length; i < len; i++){
41577 this.items[i].purgeListeners();
41579 if(removeEl === true){
41580 this.el.update("");
41585 createStrip : function(container)
41587 var strip = document.createElement("nav");
41588 strip.className = Roo.bootstrap.version == 4 ?
41589 "navbar-light bg-light" :
41590 "navbar navbar-default"; //"x-tabs-wrap";
41591 container.appendChild(strip);
41595 createStripList : function(strip)
41597 // div wrapper for retard IE
41598 // returns the "tr" element.
41599 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41600 //'<div class="x-tabs-strip-wrap">'+
41601 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41602 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41603 return strip.firstChild; //.firstChild.firstChild.firstChild;
41605 createBody : function(container)
41607 var body = document.createElement("div");
41608 Roo.id(body, "tab-body");
41609 //Roo.fly(body).addClass("x-tabs-body");
41610 Roo.fly(body).addClass("tab-content");
41611 container.appendChild(body);
41614 createItemBody :function(bodyEl, id){
41615 var body = Roo.getDom(id);
41617 body = document.createElement("div");
41620 //Roo.fly(body).addClass("x-tabs-item-body");
41621 Roo.fly(body).addClass("tab-pane");
41622 bodyEl.insertBefore(body, bodyEl.firstChild);
41626 createStripElements : function(stripEl, text, closable, tpl)
41628 var td = document.createElement("li"); // was td..
41629 td.className = 'nav-item';
41631 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41634 stripEl.appendChild(td);
41636 td.className = "x-tabs-closable";
41637 if(!this.closeTpl){
41638 this.closeTpl = new Roo.Template(
41639 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41640 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41641 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41644 var el = this.closeTpl.overwrite(td, {"text": text});
41645 var close = el.getElementsByTagName("div")[0];
41646 var inner = el.getElementsByTagName("em")[0];
41647 return {"el": el, "close": close, "inner": inner};
41650 // not sure what this is..
41651 // if(!this.tabTpl){
41652 //this.tabTpl = new Roo.Template(
41653 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41654 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41656 // this.tabTpl = new Roo.Template(
41657 // '<a href="#">' +
41658 // '<span unselectable="on"' +
41659 // (this.disableTooltips ? '' : ' title="{text}"') +
41660 // ' >{text}</span></a>'
41666 var template = tpl || this.tabTpl || false;
41669 template = new Roo.Template(
41670 Roo.bootstrap.version == 4 ?
41672 '<a class="nav-link" href="#" unselectable="on"' +
41673 (this.disableTooltips ? '' : ' title="{text}"') +
41676 '<a class="nav-link" href="#">' +
41677 '<span unselectable="on"' +
41678 (this.disableTooltips ? '' : ' title="{text}"') +
41679 ' >{text}</span></a>'
41684 switch (typeof(template)) {
41688 template = new Roo.Template(template);
41694 var el = template.overwrite(td, {"text": text});
41696 var inner = el.getElementsByTagName("span")[0];
41698 return {"el": el, "inner": inner};
41706 * @class Roo.TabPanelItem
41707 * @extends Roo.util.Observable
41708 * Represents an individual item (tab plus body) in a TabPanel.
41709 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41710 * @param {String} id The id of this TabPanelItem
41711 * @param {String} text The text for the tab of this TabPanelItem
41712 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41714 Roo.bootstrap.panel.TabItem = function(config){
41716 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41717 * @type Roo.TabPanel
41719 this.tabPanel = config.panel;
41721 * The id for this TabPanelItem
41724 this.id = config.id;
41726 this.disabled = false;
41728 this.text = config.text;
41730 this.loaded = false;
41731 this.closable = config.closable;
41734 * The body element for this TabPanelItem.
41735 * @type Roo.Element
41737 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41738 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41739 this.bodyEl.setStyle("display", "block");
41740 this.bodyEl.setStyle("zoom", "1");
41741 //this.hideAction();
41743 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41745 this.el = Roo.get(els.el);
41746 this.inner = Roo.get(els.inner, true);
41747 this.textEl = Roo.bootstrap.version == 4 ?
41748 this.el : Roo.get(this.el.dom.firstChild, true);
41750 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41751 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41754 // this.el.on("mousedown", this.onTabMouseDown, this);
41755 this.el.on("click", this.onTabClick, this);
41757 if(config.closable){
41758 var c = Roo.get(els.close, true);
41759 c.dom.title = this.closeText;
41760 c.addClassOnOver("close-over");
41761 c.on("click", this.closeClick, this);
41767 * Fires when this tab becomes the active tab.
41768 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41769 * @param {Roo.TabPanelItem} this
41773 * @event beforeclose
41774 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41775 * @param {Roo.TabPanelItem} this
41776 * @param {Object} e Set cancel to true on this object to cancel the close.
41778 "beforeclose": true,
41781 * Fires when this tab is closed.
41782 * @param {Roo.TabPanelItem} this
41786 * @event deactivate
41787 * Fires when this tab is no longer the active tab.
41788 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41789 * @param {Roo.TabPanelItem} this
41791 "deactivate" : true
41793 this.hidden = false;
41795 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41798 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41800 purgeListeners : function(){
41801 Roo.util.Observable.prototype.purgeListeners.call(this);
41802 this.el.removeAllListeners();
41805 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41808 this.status_node.addClass("active");
41811 this.tabPanel.stripWrap.repaint();
41813 this.fireEvent("activate", this.tabPanel, this);
41817 * Returns true if this tab is the active tab.
41818 * @return {Boolean}
41820 isActive : function(){
41821 return this.tabPanel.getActiveTab() == this;
41825 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41828 this.status_node.removeClass("active");
41830 this.fireEvent("deactivate", this.tabPanel, this);
41833 hideAction : function(){
41834 this.bodyEl.hide();
41835 this.bodyEl.setStyle("position", "absolute");
41836 this.bodyEl.setLeft("-20000px");
41837 this.bodyEl.setTop("-20000px");
41840 showAction : function(){
41841 this.bodyEl.setStyle("position", "relative");
41842 this.bodyEl.setTop("");
41843 this.bodyEl.setLeft("");
41844 this.bodyEl.show();
41848 * Set the tooltip for the tab.
41849 * @param {String} tooltip The tab's tooltip
41851 setTooltip : function(text){
41852 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41853 this.textEl.dom.qtip = text;
41854 this.textEl.dom.removeAttribute('title');
41856 this.textEl.dom.title = text;
41860 onTabClick : function(e){
41861 e.preventDefault();
41862 this.tabPanel.activate(this.id);
41865 onTabMouseDown : function(e){
41866 e.preventDefault();
41867 this.tabPanel.activate(this.id);
41870 getWidth : function(){
41871 return this.inner.getWidth();
41874 setWidth : function(width){
41875 var iwidth = width - this.linode.getPadding("lr");
41876 this.inner.setWidth(iwidth);
41877 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41878 this.linode.setWidth(width);
41882 * Show or hide the tab
41883 * @param {Boolean} hidden True to hide or false to show.
41885 setHidden : function(hidden){
41886 this.hidden = hidden;
41887 this.linode.setStyle("display", hidden ? "none" : "");
41891 * Returns true if this tab is "hidden"
41892 * @return {Boolean}
41894 isHidden : function(){
41895 return this.hidden;
41899 * Returns the text for this tab
41902 getText : function(){
41906 autoSize : function(){
41907 //this.el.beginMeasure();
41908 this.textEl.setWidth(1);
41910 * #2804 [new] Tabs in Roojs
41911 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41913 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41914 //this.el.endMeasure();
41918 * Sets the text for the tab (Note: this also sets the tooltip text)
41919 * @param {String} text The tab's text and tooltip
41921 setText : function(text){
41923 this.textEl.update(text);
41924 this.setTooltip(text);
41925 //if(!this.tabPanel.resizeTabs){
41926 // this.autoSize();
41930 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41932 activate : function(){
41933 this.tabPanel.activate(this.id);
41937 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41939 disable : function(){
41940 if(this.tabPanel.active != this){
41941 this.disabled = true;
41942 this.status_node.addClass("disabled");
41947 * Enables this TabPanelItem if it was previously disabled.
41949 enable : function(){
41950 this.disabled = false;
41951 this.status_node.removeClass("disabled");
41955 * Sets the content for this TabPanelItem.
41956 * @param {String} content The content
41957 * @param {Boolean} loadScripts true to look for and load scripts
41959 setContent : function(content, loadScripts){
41960 this.bodyEl.update(content, loadScripts);
41964 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41965 * @return {Roo.UpdateManager} The UpdateManager
41967 getUpdateManager : function(){
41968 return this.bodyEl.getUpdateManager();
41972 * Set a URL to be used to load the content for this TabPanelItem.
41973 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41974 * @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)
41975 * @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)
41976 * @return {Roo.UpdateManager} The UpdateManager
41978 setUrl : function(url, params, loadOnce){
41979 if(this.refreshDelegate){
41980 this.un('activate', this.refreshDelegate);
41982 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41983 this.on("activate", this.refreshDelegate);
41984 return this.bodyEl.getUpdateManager();
41988 _handleRefresh : function(url, params, loadOnce){
41989 if(!loadOnce || !this.loaded){
41990 var updater = this.bodyEl.getUpdateManager();
41991 updater.update(url, params, this._setLoaded.createDelegate(this));
41996 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41997 * Will fail silently if the setUrl method has not been called.
41998 * This does not activate the panel, just updates its content.
42000 refresh : function(){
42001 if(this.refreshDelegate){
42002 this.loaded = false;
42003 this.refreshDelegate();
42008 _setLoaded : function(){
42009 this.loaded = true;
42013 closeClick : function(e){
42016 this.fireEvent("beforeclose", this, o);
42017 if(o.cancel !== true){
42018 this.tabPanel.removeTab(this.id);
42022 * The text displayed in the tooltip for the close icon.
42025 closeText : "Close this tab"
42028 * This script refer to:
42029 * Title: International Telephone Input
42030 * Author: Jack O'Connor
42031 * Code version: v12.1.12
42032 * Availability: https://github.com/jackocnr/intl-tel-input.git
42035 Roo.bootstrap.PhoneInputData = function() {
42038 "Afghanistan (افغانستان)",
42043 "Albania (Shqipëri)",
42048 "Algeria (الجزائر)",
42073 "Antigua and Barbuda",
42083 "Armenia (Հայաստան)",
42099 "Austria (Österreich)",
42104 "Azerbaijan (Azərbaycan)",
42114 "Bahrain (البحرين)",
42119 "Bangladesh (বাংলাদেশ)",
42129 "Belarus (Беларусь)",
42134 "Belgium (België)",
42164 "Bosnia and Herzegovina (Босна и Херцеговина)",
42179 "British Indian Ocean Territory",
42184 "British Virgin Islands",
42194 "Bulgaria (България)",
42204 "Burundi (Uburundi)",
42209 "Cambodia (កម្ពុជា)",
42214 "Cameroon (Cameroun)",
42223 ["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"]
42226 "Cape Verde (Kabu Verdi)",
42231 "Caribbean Netherlands",
42242 "Central African Republic (République centrafricaine)",
42262 "Christmas Island",
42268 "Cocos (Keeling) Islands",
42279 "Comoros (جزر القمر)",
42284 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42289 "Congo (Republic) (Congo-Brazzaville)",
42309 "Croatia (Hrvatska)",
42330 "Czech Republic (Česká republika)",
42335 "Denmark (Danmark)",
42350 "Dominican Republic (República Dominicana)",
42354 ["809", "829", "849"]
42372 "Equatorial Guinea (Guinea Ecuatorial)",
42392 "Falkland Islands (Islas Malvinas)",
42397 "Faroe Islands (Føroyar)",
42418 "French Guiana (Guyane française)",
42423 "French Polynesia (Polynésie française)",
42438 "Georgia (საქართველო)",
42443 "Germany (Deutschland)",
42463 "Greenland (Kalaallit Nunaat)",
42500 "Guinea-Bissau (Guiné Bissau)",
42525 "Hungary (Magyarország)",
42530 "Iceland (Ísland)",
42550 "Iraq (العراق)",
42566 "Israel (ישראל)",
42593 "Jordan (الأردن)",
42598 "Kazakhstan (Казахстан)",
42619 "Kuwait (الكويت)",
42624 "Kyrgyzstan (Кыргызстан)",
42634 "Latvia (Latvija)",
42639 "Lebanon (لبنان)",
42654 "Libya (ليبيا)",
42664 "Lithuania (Lietuva)",
42679 "Macedonia (FYROM) (Македонија)",
42684 "Madagascar (Madagasikara)",
42714 "Marshall Islands",
42724 "Mauritania (موريتانيا)",
42729 "Mauritius (Moris)",
42750 "Moldova (Republica Moldova)",
42760 "Mongolia (Монгол)",
42765 "Montenegro (Crna Gora)",
42775 "Morocco (المغرب)",
42781 "Mozambique (Moçambique)",
42786 "Myanmar (Burma) (မြန်မာ)",
42791 "Namibia (Namibië)",
42806 "Netherlands (Nederland)",
42811 "New Caledonia (Nouvelle-Calédonie)",
42846 "North Korea (조선 민주주의 인민 공화국)",
42851 "Northern Mariana Islands",
42867 "Pakistan (پاکستان)",
42877 "Palestine (فلسطين)",
42887 "Papua New Guinea",
42929 "Réunion (La Réunion)",
42935 "Romania (România)",
42951 "Saint Barthélemy",
42962 "Saint Kitts and Nevis",
42972 "Saint Martin (Saint-Martin (partie française))",
42978 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42983 "Saint Vincent and the Grenadines",
42998 "São Tomé and Príncipe (São Tomé e Príncipe)",
43003 "Saudi Arabia (المملكة العربية السعودية)",
43008 "Senegal (Sénégal)",
43038 "Slovakia (Slovensko)",
43043 "Slovenia (Slovenija)",
43053 "Somalia (Soomaaliya)",
43063 "South Korea (대한민국)",
43068 "South Sudan (جنوب السودان)",
43078 "Sri Lanka (ශ්රී ලංකාව)",
43083 "Sudan (السودان)",
43093 "Svalbard and Jan Mayen",
43104 "Sweden (Sverige)",
43109 "Switzerland (Schweiz)",
43114 "Syria (سوريا)",
43159 "Trinidad and Tobago",
43164 "Tunisia (تونس)",
43169 "Turkey (Türkiye)",
43179 "Turks and Caicos Islands",
43189 "U.S. Virgin Islands",
43199 "Ukraine (Україна)",
43204 "United Arab Emirates (الإمارات العربية المتحدة)",
43226 "Uzbekistan (Oʻzbekiston)",
43236 "Vatican City (Città del Vaticano)",
43247 "Vietnam (Việt Nam)",
43252 "Wallis and Futuna (Wallis-et-Futuna)",
43257 "Western Sahara (الصحراء الغربية)",
43263 "Yemen (اليمن)",
43287 * This script refer to:
43288 * Title: International Telephone Input
43289 * Author: Jack O'Connor
43290 * Code version: v12.1.12
43291 * Availability: https://github.com/jackocnr/intl-tel-input.git
43295 * @class Roo.bootstrap.PhoneInput
43296 * @extends Roo.bootstrap.TriggerField
43297 * An input with International dial-code selection
43299 * @cfg {String} defaultDialCode default '+852'
43300 * @cfg {Array} preferedCountries default []
43303 * Create a new PhoneInput.
43304 * @param {Object} config Configuration options
43307 Roo.bootstrap.PhoneInput = function(config) {
43308 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43311 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43313 listWidth: undefined,
43315 selectedClass: 'active',
43317 invalidClass : "has-warning",
43319 validClass: 'has-success',
43321 allowed: '0123456789',
43326 * @cfg {String} defaultDialCode The default dial code when initializing the input
43328 defaultDialCode: '+852',
43331 * @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
43333 preferedCountries: false,
43335 getAutoCreate : function()
43337 var data = Roo.bootstrap.PhoneInputData();
43338 var align = this.labelAlign || this.parentLabelAlign();
43341 this.allCountries = [];
43342 this.dialCodeMapping = [];
43344 for (var i = 0; i < data.length; i++) {
43346 this.allCountries[i] = {
43350 priority: c[3] || 0,
43351 areaCodes: c[4] || null
43353 this.dialCodeMapping[c[2]] = {
43356 priority: c[3] || 0,
43357 areaCodes: c[4] || null
43369 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43370 maxlength: this.max_length,
43371 cls : 'form-control tel-input',
43372 autocomplete: 'new-password'
43375 var hiddenInput = {
43378 cls: 'hidden-tel-input'
43382 hiddenInput.name = this.name;
43385 if (this.disabled) {
43386 input.disabled = true;
43389 var flag_container = {
43406 cls: this.hasFeedback ? 'has-feedback' : '',
43412 cls: 'dial-code-holder',
43419 cls: 'roo-select2-container input-group',
43426 if (this.fieldLabel.length) {
43429 tooltip: 'This field is required'
43435 cls: 'control-label',
43441 html: this.fieldLabel
43444 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43450 if(this.indicatorpos == 'right') {
43451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43458 if(align == 'left') {
43466 if(this.labelWidth > 12){
43467 label.style = "width: " + this.labelWidth + 'px';
43469 if(this.labelWidth < 13 && this.labelmd == 0){
43470 this.labelmd = this.labelWidth;
43472 if(this.labellg > 0){
43473 label.cls += ' col-lg-' + this.labellg;
43474 input.cls += ' col-lg-' + (12 - this.labellg);
43476 if(this.labelmd > 0){
43477 label.cls += ' col-md-' + this.labelmd;
43478 container.cls += ' col-md-' + (12 - this.labelmd);
43480 if(this.labelsm > 0){
43481 label.cls += ' col-sm-' + this.labelsm;
43482 container.cls += ' col-sm-' + (12 - this.labelsm);
43484 if(this.labelxs > 0){
43485 label.cls += ' col-xs-' + this.labelxs;
43486 container.cls += ' col-xs-' + (12 - this.labelxs);
43496 var settings = this;
43498 ['xs','sm','md','lg'].map(function(size){
43499 if (settings[size]) {
43500 cfg.cls += ' col-' + size + '-' + settings[size];
43504 this.store = new Roo.data.Store({
43505 proxy : new Roo.data.MemoryProxy({}),
43506 reader : new Roo.data.JsonReader({
43517 'name' : 'dialCode',
43521 'name' : 'priority',
43525 'name' : 'areaCodes',
43532 if(!this.preferedCountries) {
43533 this.preferedCountries = [
43540 var p = this.preferedCountries.reverse();
43543 for (var i = 0; i < p.length; i++) {
43544 for (var j = 0; j < this.allCountries.length; j++) {
43545 if(this.allCountries[j].iso2 == p[i]) {
43546 var t = this.allCountries[j];
43547 this.allCountries.splice(j,1);
43548 this.allCountries.unshift(t);
43554 this.store.proxy.data = {
43556 data: this.allCountries
43562 initEvents : function()
43565 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43567 this.indicator = this.indicatorEl();
43568 this.flag = this.flagEl();
43569 this.dialCodeHolder = this.dialCodeHolderEl();
43571 this.trigger = this.el.select('div.flag-box',true).first();
43572 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43577 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43578 _this.list.setWidth(lw);
43581 this.list.on('mouseover', this.onViewOver, this);
43582 this.list.on('mousemove', this.onViewMove, this);
43583 this.inputEl().on("keyup", this.onKeyUp, this);
43584 this.inputEl().on("keypress", this.onKeyPress, this);
43586 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43588 this.view = new Roo.View(this.list, this.tpl, {
43589 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43592 this.view.on('click', this.onViewClick, this);
43593 this.setValue(this.defaultDialCode);
43596 onTriggerClick : function(e)
43598 Roo.log('trigger click');
43603 if(this.isExpanded()){
43605 this.hasFocus = false;
43607 this.store.load({});
43608 this.hasFocus = true;
43613 isExpanded : function()
43615 return this.list.isVisible();
43618 collapse : function()
43620 if(!this.isExpanded()){
43624 Roo.get(document).un('mousedown', this.collapseIf, this);
43625 Roo.get(document).un('mousewheel', this.collapseIf, this);
43626 this.fireEvent('collapse', this);
43630 expand : function()
43634 if(this.isExpanded() || !this.hasFocus){
43638 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43639 this.list.setWidth(lw);
43642 this.restrictHeight();
43644 Roo.get(document).on('mousedown', this.collapseIf, this);
43645 Roo.get(document).on('mousewheel', this.collapseIf, this);
43647 this.fireEvent('expand', this);
43650 restrictHeight : function()
43652 this.list.alignTo(this.inputEl(), this.listAlign);
43653 this.list.alignTo(this.inputEl(), this.listAlign);
43656 onViewOver : function(e, t)
43658 if(this.inKeyMode){
43661 var item = this.view.findItemFromChild(t);
43664 var index = this.view.indexOf(item);
43665 this.select(index, false);
43670 onViewClick : function(view, doFocus, el, e)
43672 var index = this.view.getSelectedIndexes()[0];
43674 var r = this.store.getAt(index);
43677 this.onSelect(r, index);
43679 if(doFocus !== false && !this.blockFocus){
43680 this.inputEl().focus();
43684 onViewMove : function(e, t)
43686 this.inKeyMode = false;
43689 select : function(index, scrollIntoView)
43691 this.selectedIndex = index;
43692 this.view.select(index);
43693 if(scrollIntoView !== false){
43694 var el = this.view.getNode(index);
43696 this.list.scrollChildIntoView(el, false);
43701 createList : function()
43703 this.list = Roo.get(document.body).createChild({
43705 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43706 style: 'display:none'
43709 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43712 collapseIf : function(e)
43714 var in_combo = e.within(this.el);
43715 var in_list = e.within(this.list);
43716 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43718 if (in_combo || in_list || is_list) {
43724 onSelect : function(record, index)
43726 if(this.fireEvent('beforeselect', this, record, index) !== false){
43728 this.setFlagClass(record.data.iso2);
43729 this.setDialCode(record.data.dialCode);
43730 this.hasFocus = false;
43732 this.fireEvent('select', this, record, index);
43736 flagEl : function()
43738 var flag = this.el.select('div.flag',true).first();
43745 dialCodeHolderEl : function()
43747 var d = this.el.select('input.dial-code-holder',true).first();
43754 setDialCode : function(v)
43756 this.dialCodeHolder.dom.value = '+'+v;
43759 setFlagClass : function(n)
43761 this.flag.dom.className = 'flag '+n;
43764 getValue : function()
43766 var v = this.inputEl().getValue();
43767 if(this.dialCodeHolder) {
43768 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43773 setValue : function(v)
43775 var d = this.getDialCode(v);
43777 //invalid dial code
43778 if(v.length == 0 || !d || d.length == 0) {
43780 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43781 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43787 this.setFlagClass(this.dialCodeMapping[d].iso2);
43788 this.setDialCode(d);
43789 this.inputEl().dom.value = v.replace('+'+d,'');
43790 this.hiddenEl().dom.value = this.getValue();
43795 getDialCode : function(v)
43799 if (v.length == 0) {
43800 return this.dialCodeHolder.dom.value;
43804 if (v.charAt(0) != "+") {
43807 var numericChars = "";
43808 for (var i = 1; i < v.length; i++) {
43809 var c = v.charAt(i);
43812 if (this.dialCodeMapping[numericChars]) {
43813 dialCode = v.substr(1, i);
43815 if (numericChars.length == 4) {
43825 this.setValue(this.defaultDialCode);
43829 hiddenEl : function()
43831 return this.el.select('input.hidden-tel-input',true).first();
43834 // after setting val
43835 onKeyUp : function(e){
43836 this.setValue(this.getValue());
43839 onKeyPress : function(e){
43840 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43847 * @class Roo.bootstrap.MoneyField
43848 * @extends Roo.bootstrap.ComboBox
43849 * Bootstrap MoneyField class
43852 * Create a new MoneyField.
43853 * @param {Object} config Configuration options
43856 Roo.bootstrap.MoneyField = function(config) {
43858 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43862 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43865 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43867 allowDecimals : true,
43869 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43871 decimalSeparator : ".",
43873 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43875 decimalPrecision : 0,
43877 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43879 allowNegative : true,
43881 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43885 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43887 minValue : Number.NEGATIVE_INFINITY,
43889 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43891 maxValue : Number.MAX_VALUE,
43893 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43895 minText : "The minimum value for this field is {0}",
43897 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43899 maxText : "The maximum value for this field is {0}",
43901 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43902 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43904 nanText : "{0} is not a valid number",
43906 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43910 * @cfg {String} defaults currency of the MoneyField
43911 * value should be in lkey
43913 defaultCurrency : false,
43915 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43917 thousandsDelimiter : false,
43919 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43930 getAutoCreate : function()
43932 var align = this.labelAlign || this.parentLabelAlign();
43944 cls : 'form-control roo-money-amount-input',
43945 autocomplete: 'new-password'
43948 var hiddenInput = {
43952 cls: 'hidden-number-input'
43955 if(this.max_length) {
43956 input.maxlength = this.max_length;
43960 hiddenInput.name = this.name;
43963 if (this.disabled) {
43964 input.disabled = true;
43967 var clg = 12 - this.inputlg;
43968 var cmd = 12 - this.inputmd;
43969 var csm = 12 - this.inputsm;
43970 var cxs = 12 - this.inputxs;
43974 cls : 'row roo-money-field',
43978 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43982 cls: 'roo-select2-container input-group',
43986 cls : 'form-control roo-money-currency-input',
43987 autocomplete: 'new-password',
43989 name : this.currencyName
43993 cls : 'input-group-addon',
44007 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44011 cls: this.hasFeedback ? 'has-feedback' : '',
44022 if (this.fieldLabel.length) {
44025 tooltip: 'This field is required'
44031 cls: 'control-label',
44037 html: this.fieldLabel
44040 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44046 if(this.indicatorpos == 'right') {
44047 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44054 if(align == 'left') {
44062 if(this.labelWidth > 12){
44063 label.style = "width: " + this.labelWidth + 'px';
44065 if(this.labelWidth < 13 && this.labelmd == 0){
44066 this.labelmd = this.labelWidth;
44068 if(this.labellg > 0){
44069 label.cls += ' col-lg-' + this.labellg;
44070 input.cls += ' col-lg-' + (12 - this.labellg);
44072 if(this.labelmd > 0){
44073 label.cls += ' col-md-' + this.labelmd;
44074 container.cls += ' col-md-' + (12 - this.labelmd);
44076 if(this.labelsm > 0){
44077 label.cls += ' col-sm-' + this.labelsm;
44078 container.cls += ' col-sm-' + (12 - this.labelsm);
44080 if(this.labelxs > 0){
44081 label.cls += ' col-xs-' + this.labelxs;
44082 container.cls += ' col-xs-' + (12 - this.labelxs);
44093 var settings = this;
44095 ['xs','sm','md','lg'].map(function(size){
44096 if (settings[size]) {
44097 cfg.cls += ' col-' + size + '-' + settings[size];
44104 initEvents : function()
44106 this.indicator = this.indicatorEl();
44108 this.initCurrencyEvent();
44110 this.initNumberEvent();
44113 initCurrencyEvent : function()
44116 throw "can not find store for combo";
44119 this.store = Roo.factory(this.store, Roo.data);
44120 this.store.parent = this;
44124 this.triggerEl = this.el.select('.input-group-addon', true).first();
44126 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44131 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44132 _this.list.setWidth(lw);
44135 this.list.on('mouseover', this.onViewOver, this);
44136 this.list.on('mousemove', this.onViewMove, this);
44137 this.list.on('scroll', this.onViewScroll, this);
44140 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44143 this.view = new Roo.View(this.list, this.tpl, {
44144 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44147 this.view.on('click', this.onViewClick, this);
44149 this.store.on('beforeload', this.onBeforeLoad, this);
44150 this.store.on('load', this.onLoad, this);
44151 this.store.on('loadexception', this.onLoadException, this);
44153 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44154 "up" : function(e){
44155 this.inKeyMode = true;
44159 "down" : function(e){
44160 if(!this.isExpanded()){
44161 this.onTriggerClick();
44163 this.inKeyMode = true;
44168 "enter" : function(e){
44171 if(this.fireEvent("specialkey", this, e)){
44172 this.onViewClick(false);
44178 "esc" : function(e){
44182 "tab" : function(e){
44185 if(this.fireEvent("specialkey", this, e)){
44186 this.onViewClick(false);
44194 doRelay : function(foo, bar, hname){
44195 if(hname == 'down' || this.scope.isExpanded()){
44196 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44204 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44208 initNumberEvent : function(e)
44210 this.inputEl().on("keydown" , this.fireKey, this);
44211 this.inputEl().on("focus", this.onFocus, this);
44212 this.inputEl().on("blur", this.onBlur, this);
44214 this.inputEl().relayEvent('keyup', this);
44216 if(this.indicator){
44217 this.indicator.addClass('invisible');
44220 this.originalValue = this.getValue();
44222 if(this.validationEvent == 'keyup'){
44223 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44224 this.inputEl().on('keyup', this.filterValidation, this);
44226 else if(this.validationEvent !== false){
44227 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44230 if(this.selectOnFocus){
44231 this.on("focus", this.preFocus, this);
44234 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44235 this.inputEl().on("keypress", this.filterKeys, this);
44237 this.inputEl().relayEvent('keypress', this);
44240 var allowed = "0123456789";
44242 if(this.allowDecimals){
44243 allowed += this.decimalSeparator;
44246 if(this.allowNegative){
44250 if(this.thousandsDelimiter) {
44254 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44256 var keyPress = function(e){
44258 var k = e.getKey();
44260 var c = e.getCharCode();
44263 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44264 allowed.indexOf(String.fromCharCode(c)) === -1
44270 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44274 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44279 this.inputEl().on("keypress", keyPress, this);
44283 onTriggerClick : function(e)
44290 this.loadNext = false;
44292 if(this.isExpanded()){
44297 this.hasFocus = true;
44299 if(this.triggerAction == 'all') {
44300 this.doQuery(this.allQuery, true);
44304 this.doQuery(this.getRawValue());
44307 getCurrency : function()
44309 var v = this.currencyEl().getValue();
44314 restrictHeight : function()
44316 this.list.alignTo(this.currencyEl(), this.listAlign);
44317 this.list.alignTo(this.currencyEl(), this.listAlign);
44320 onViewClick : function(view, doFocus, el, e)
44322 var index = this.view.getSelectedIndexes()[0];
44324 var r = this.store.getAt(index);
44327 this.onSelect(r, index);
44331 onSelect : function(record, index){
44333 if(this.fireEvent('beforeselect', this, record, index) !== false){
44335 this.setFromCurrencyData(index > -1 ? record.data : false);
44339 this.fireEvent('select', this, record, index);
44343 setFromCurrencyData : function(o)
44347 this.lastCurrency = o;
44349 if (this.currencyField) {
44350 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44352 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44355 this.lastSelectionText = currency;
44357 //setting default currency
44358 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44359 this.setCurrency(this.defaultCurrency);
44363 this.setCurrency(currency);
44366 setFromData : function(o)
44370 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44372 this.setFromCurrencyData(c);
44377 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44379 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44382 this.setValue(value);
44386 setCurrency : function(v)
44388 this.currencyValue = v;
44391 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44396 setValue : function(v)
44398 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44404 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44406 this.inputEl().dom.value = (v == '') ? '' :
44407 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44409 if(!this.allowZero && v === '0') {
44410 this.hiddenEl().dom.value = '';
44411 this.inputEl().dom.value = '';
44418 getRawValue : function()
44420 var v = this.inputEl().getValue();
44425 getValue : function()
44427 return this.fixPrecision(this.parseValue(this.getRawValue()));
44430 parseValue : function(value)
44432 if(this.thousandsDelimiter) {
44434 r = new RegExp(",", "g");
44435 value = value.replace(r, "");
44438 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44439 return isNaN(value) ? '' : value;
44443 fixPrecision : function(value)
44445 if(this.thousandsDelimiter) {
44447 r = new RegExp(",", "g");
44448 value = value.replace(r, "");
44451 var nan = isNaN(value);
44453 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44454 return nan ? '' : value;
44456 return parseFloat(value).toFixed(this.decimalPrecision);
44459 decimalPrecisionFcn : function(v)
44461 return Math.floor(v);
44464 validateValue : function(value)
44466 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44470 var num = this.parseValue(value);
44473 this.markInvalid(String.format(this.nanText, value));
44477 if(num < this.minValue){
44478 this.markInvalid(String.format(this.minText, this.minValue));
44482 if(num > this.maxValue){
44483 this.markInvalid(String.format(this.maxText, this.maxValue));
44490 validate : function()
44492 if(this.disabled || this.allowBlank){
44497 var currency = this.getCurrency();
44499 if(this.validateValue(this.getRawValue()) && currency.length){
44504 this.markInvalid();
44508 getName: function()
44513 beforeBlur : function()
44519 var v = this.parseValue(this.getRawValue());
44526 onBlur : function()
44530 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44531 //this.el.removeClass(this.focusClass);
44534 this.hasFocus = false;
44536 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44540 var v = this.getValue();
44542 if(String(v) !== String(this.startValue)){
44543 this.fireEvent('change', this, v, this.startValue);
44546 this.fireEvent("blur", this);
44549 inputEl : function()
44551 return this.el.select('.roo-money-amount-input', true).first();
44554 currencyEl : function()
44556 return this.el.select('.roo-money-currency-input', true).first();
44559 hiddenEl : function()
44561 return this.el.select('input.hidden-number-input',true).first();
44565 * @class Roo.bootstrap.BezierSignature
44566 * @extends Roo.bootstrap.Component
44567 * Bootstrap BezierSignature class
44568 * This script refer to:
44569 * Title: Signature Pad
44571 * Availability: https://github.com/szimek/signature_pad
44574 * Create a new BezierSignature
44575 * @param {Object} config The config object
44578 Roo.bootstrap.BezierSignature = function(config){
44579 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44585 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44592 mouse_btn_down: true,
44595 * @cfg {int} canvas height
44597 canvas_height: '200px',
44600 * @cfg {float|function} Radius of a single dot.
44605 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44610 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44615 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44620 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44625 * @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.
44627 bg_color: 'rgba(0, 0, 0, 0)',
44630 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44632 dot_color: 'black',
44635 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44637 velocity_filter_weight: 0.7,
44640 * @cfg {function} Callback when stroke begin.
44645 * @cfg {function} Callback when stroke end.
44649 getAutoCreate : function()
44651 var cls = 'roo-signature column';
44654 cls += ' ' + this.cls;
44664 for(var i = 0; i < col_sizes.length; i++) {
44665 if(this[col_sizes[i]]) {
44666 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44676 cls: 'roo-signature-body',
44680 cls: 'roo-signature-body-canvas',
44681 height: this.canvas_height,
44682 width: this.canvas_width
44689 style: 'display: none'
44697 initEvents: function()
44699 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44701 var canvas = this.canvasEl();
44703 // mouse && touch event swapping...
44704 canvas.dom.style.touchAction = 'none';
44705 canvas.dom.style.msTouchAction = 'none';
44707 this.mouse_btn_down = false;
44708 canvas.on('mousedown', this._handleMouseDown, this);
44709 canvas.on('mousemove', this._handleMouseMove, this);
44710 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44712 if (window.PointerEvent) {
44713 canvas.on('pointerdown', this._handleMouseDown, this);
44714 canvas.on('pointermove', this._handleMouseMove, this);
44715 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44718 if ('ontouchstart' in window) {
44719 canvas.on('touchstart', this._handleTouchStart, this);
44720 canvas.on('touchmove', this._handleTouchMove, this);
44721 canvas.on('touchend', this._handleTouchEnd, this);
44724 Roo.EventManager.onWindowResize(this.resize, this, true);
44726 // file input event
44727 this.fileEl().on('change', this.uploadImage, this);
44734 resize: function(){
44736 var canvas = this.canvasEl().dom;
44737 var ctx = this.canvasElCtx();
44738 var img_data = false;
44740 if(canvas.width > 0) {
44741 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44743 // setting canvas width will clean img data
44746 var style = window.getComputedStyle ?
44747 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44749 var padding_left = parseInt(style.paddingLeft) || 0;
44750 var padding_right = parseInt(style.paddingRight) || 0;
44752 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44755 ctx.putImageData(img_data, 0, 0);
44759 _handleMouseDown: function(e)
44761 if (e.browserEvent.which === 1) {
44762 this.mouse_btn_down = true;
44763 this.strokeBegin(e);
44767 _handleMouseMove: function (e)
44769 if (this.mouse_btn_down) {
44770 this.strokeMoveUpdate(e);
44774 _handleMouseUp: function (e)
44776 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44777 this.mouse_btn_down = false;
44782 _handleTouchStart: function (e) {
44784 e.preventDefault();
44785 if (e.browserEvent.targetTouches.length === 1) {
44786 // var touch = e.browserEvent.changedTouches[0];
44787 // this.strokeBegin(touch);
44789 this.strokeBegin(e); // assume e catching the correct xy...
44793 _handleTouchMove: function (e) {
44794 e.preventDefault();
44795 // var touch = event.targetTouches[0];
44796 // _this._strokeMoveUpdate(touch);
44797 this.strokeMoveUpdate(e);
44800 _handleTouchEnd: function (e) {
44801 var wasCanvasTouched = e.target === this.canvasEl().dom;
44802 if (wasCanvasTouched) {
44803 e.preventDefault();
44804 // var touch = event.changedTouches[0];
44805 // _this._strokeEnd(touch);
44810 reset: function () {
44811 this._lastPoints = [];
44812 this._lastVelocity = 0;
44813 this._lastWidth = (this.min_width + this.max_width) / 2;
44814 this.canvasElCtx().fillStyle = this.dot_color;
44817 strokeMoveUpdate: function(e)
44819 this.strokeUpdate(e);
44821 if (this.throttle) {
44822 this.throttleStroke(this.strokeUpdate, this.throttle);
44825 this.strokeUpdate(e);
44829 strokeBegin: function(e)
44831 var newPointGroup = {
44832 color: this.dot_color,
44836 if (typeof this.onBegin === 'function') {
44840 this.curve_data.push(newPointGroup);
44842 this.strokeUpdate(e);
44845 strokeUpdate: function(e)
44847 var rect = this.canvasEl().dom.getBoundingClientRect();
44848 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44849 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44850 var lastPoints = lastPointGroup.points;
44851 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44852 var isLastPointTooClose = lastPoint
44853 ? point.distanceTo(lastPoint) <= this.min_distance
44855 var color = lastPointGroup.color;
44856 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44857 var curve = this.addPoint(point);
44859 this.drawDot({color: color, point: point});
44862 this.drawCurve({color: color, curve: curve});
44872 strokeEnd: function(e)
44874 this.strokeUpdate(e);
44875 if (typeof this.onEnd === 'function') {
44880 addPoint: function (point) {
44881 var _lastPoints = this._lastPoints;
44882 _lastPoints.push(point);
44883 if (_lastPoints.length > 2) {
44884 if (_lastPoints.length === 3) {
44885 _lastPoints.unshift(_lastPoints[0]);
44887 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44888 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44889 _lastPoints.shift();
44895 calculateCurveWidths: function (startPoint, endPoint) {
44896 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44897 (1 - this.velocity_filter_weight) * this._lastVelocity;
44899 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44902 start: this._lastWidth
44905 this._lastVelocity = velocity;
44906 this._lastWidth = newWidth;
44910 drawDot: function (_a) {
44911 var color = _a.color, point = _a.point;
44912 var ctx = this.canvasElCtx();
44913 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44915 this.drawCurveSegment(point.x, point.y, width);
44917 ctx.fillStyle = color;
44921 drawCurve: function (_a) {
44922 var color = _a.color, curve = _a.curve;
44923 var ctx = this.canvasElCtx();
44924 var widthDelta = curve.endWidth - curve.startWidth;
44925 var drawSteps = Math.floor(curve.length()) * 2;
44927 ctx.fillStyle = color;
44928 for (var i = 0; i < drawSteps; i += 1) {
44929 var t = i / drawSteps;
44935 var x = uuu * curve.startPoint.x;
44936 x += 3 * uu * t * curve.control1.x;
44937 x += 3 * u * tt * curve.control2.x;
44938 x += ttt * curve.endPoint.x;
44939 var y = uuu * curve.startPoint.y;
44940 y += 3 * uu * t * curve.control1.y;
44941 y += 3 * u * tt * curve.control2.y;
44942 y += ttt * curve.endPoint.y;
44943 var width = curve.startWidth + ttt * widthDelta;
44944 this.drawCurveSegment(x, y, width);
44950 drawCurveSegment: function (x, y, width) {
44951 var ctx = this.canvasElCtx();
44953 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44954 this.is_empty = false;
44959 var ctx = this.canvasElCtx();
44960 var canvas = this.canvasEl().dom;
44961 ctx.fillStyle = this.bg_color;
44962 ctx.clearRect(0, 0, canvas.width, canvas.height);
44963 ctx.fillRect(0, 0, canvas.width, canvas.height);
44964 this.curve_data = [];
44966 this.is_empty = true;
44971 return this.el.select('input',true).first();
44974 canvasEl: function()
44976 return this.el.select('canvas',true).first();
44979 canvasElCtx: function()
44981 return this.el.select('canvas',true).first().dom.getContext('2d');
44984 getImage: function(type)
44986 if(this.is_empty) {
44991 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44994 drawFromImage: function(img_src)
44996 var img = new Image();
44998 img.onload = function(){
44999 this.canvasElCtx().drawImage(img, 0, 0);
45004 this.is_empty = false;
45007 selectImage: function()
45009 this.fileEl().dom.click();
45012 uploadImage: function(e)
45014 var reader = new FileReader();
45016 reader.onload = function(e){
45017 var img = new Image();
45018 img.onload = function(){
45020 this.canvasElCtx().drawImage(img, 0, 0);
45022 img.src = e.target.result;
45025 reader.readAsDataURL(e.target.files[0]);
45028 // Bezier Point Constructor
45029 Point: (function () {
45030 function Point(x, y, time) {
45033 this.time = time || Date.now();
45035 Point.prototype.distanceTo = function (start) {
45036 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45038 Point.prototype.equals = function (other) {
45039 return this.x === other.x && this.y === other.y && this.time === other.time;
45041 Point.prototype.velocityFrom = function (start) {
45042 return this.time !== start.time
45043 ? this.distanceTo(start) / (this.time - start.time)
45050 // Bezier Constructor
45051 Bezier: (function () {
45052 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45053 this.startPoint = startPoint;
45054 this.control2 = control2;
45055 this.control1 = control1;
45056 this.endPoint = endPoint;
45057 this.startWidth = startWidth;
45058 this.endWidth = endWidth;
45060 Bezier.fromPoints = function (points, widths, scope) {
45061 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45062 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45063 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45065 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45066 var dx1 = s1.x - s2.x;
45067 var dy1 = s1.y - s2.y;
45068 var dx2 = s2.x - s3.x;
45069 var dy2 = s2.y - s3.y;
45070 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45071 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45072 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45073 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45074 var dxm = m1.x - m2.x;
45075 var dym = m1.y - m2.y;
45076 var k = l2 / (l1 + l2);
45077 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45078 var tx = s2.x - cm.x;
45079 var ty = s2.y - cm.y;
45081 c1: new scope.Point(m1.x + tx, m1.y + ty),
45082 c2: new scope.Point(m2.x + tx, m2.y + ty)
45085 Bezier.prototype.length = function () {
45090 for (var i = 0; i <= steps; i += 1) {
45092 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45093 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45095 var xdiff = cx - px;
45096 var ydiff = cy - py;
45097 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45104 Bezier.prototype.point = function (t, start, c1, c2, end) {
45105 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45106 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45107 + (3.0 * c2 * (1.0 - t) * t * t)
45108 + (end * t * t * t);
45113 throttleStroke: function(fn, wait) {
45114 if (wait === void 0) { wait = 250; }
45116 var timeout = null;
45120 var later = function () {
45121 previous = Date.now();
45123 result = fn.apply(storedContext, storedArgs);
45125 storedContext = null;
45129 return function wrapper() {
45131 for (var _i = 0; _i < arguments.length; _i++) {
45132 args[_i] = arguments[_i];
45134 var now = Date.now();
45135 var remaining = wait - (now - previous);
45136 storedContext = this;
45138 if (remaining <= 0 || remaining > wait) {
45140 clearTimeout(timeout);
45144 result = fn.apply(storedContext, storedArgs);
45146 storedContext = null;
45150 else if (!timeout) {
45151 timeout = window.setTimeout(later, remaining);