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);
9859 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
9860 new Roo.grid.SplitDragZone(this.grid, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9865 onHiddenChange : function(colModel, colIndex, hidden)
9867 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9868 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9870 //this.CSS.updateRule(thSelector, "display", "");
9871 var cols = this.headEl.select('th', true).elements;
9872 if (typeof(cols[colIndex]) != 'undefined') {
9873 cols[colIndex].removeClass(['d-none', 'd-block']);
9874 cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
9876 this.CSS.updateRule(tdSelector, "display", "");
9879 // this.CSS.updateRule(thSelector, "display", "none");
9880 this.CSS.updateRule(tdSelector, "display", "none");
9883 this.onHeaderChange();
9887 setColumnWidth: function(col_index, width)
9889 // width = "md-2 xs-2..."
9890 if(!this.colModel.config[col_index]) {
9894 var w = width.split(" ");
9896 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9898 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9901 for(var j = 0; j < w.length; j++) {
9907 var size_cls = w[j].split("-");
9909 if(!Number.isInteger(size_cls[1] * 1)) {
9913 if(!this.colModel.config[col_index][size_cls[0]]) {
9917 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9921 h_row[0].classList.replace(
9922 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9923 "col-"+size_cls[0]+"-"+size_cls[1]
9926 for(var i = 0; i < rows.length; i++) {
9928 var size_cls = w[j].split("-");
9930 if(!Number.isInteger(size_cls[1] * 1)) {
9934 if(!this.colModel.config[col_index][size_cls[0]]) {
9938 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9942 rows[i].classList.replace(
9943 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9944 "col-"+size_cls[0]+"-"+size_cls[1]
9948 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9958 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9959 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9967 * @class Roo.bootstrap.TableCell
9968 * @extends Roo.bootstrap.Component
9969 * Bootstrap TableCell class
9970 * @cfg {String} html cell contain text
9971 * @cfg {String} cls cell class
9972 * @cfg {String} tag cell tag (td|th) default td
9973 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9974 * @cfg {String} align Aligns the content in a cell
9975 * @cfg {String} axis Categorizes cells
9976 * @cfg {String} bgcolor Specifies the background color of a cell
9977 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9978 * @cfg {Number} colspan Specifies the number of columns a cell should span
9979 * @cfg {String} headers Specifies one or more header cells a cell is related to
9980 * @cfg {Number} height Sets the height of a cell
9981 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9982 * @cfg {Number} rowspan Sets the number of rows a cell should span
9983 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9984 * @cfg {String} valign Vertical aligns the content in a cell
9985 * @cfg {Number} width Specifies the width of a cell
9988 * Create a new TableCell
9989 * @param {Object} config The config object
9992 Roo.bootstrap.TableCell = function(config){
9993 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9996 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10016 getAutoCreate : function(){
10017 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10024 cfg.tag = this.tag;
10037 cfg.align=this.align
10042 if (this.bgcolor) {
10043 cfg.bgcolor=this.bgcolor
10045 if (this.charoff) {
10046 cfg.charoff=this.charoff
10048 if (this.colspan) {
10049 cfg.colspan=this.colspan
10051 if (this.headers) {
10052 cfg.headers=this.headers
10055 cfg.height=this.height
10058 cfg.nowrap=this.nowrap
10060 if (this.rowspan) {
10061 cfg.rowspan=this.rowspan
10064 cfg.scope=this.scope
10067 cfg.valign=this.valign
10070 cfg.width=this.width
10089 * @class Roo.bootstrap.TableRow
10090 * @extends Roo.bootstrap.Component
10091 * Bootstrap TableRow class
10092 * @cfg {String} cls row class
10093 * @cfg {String} align Aligns the content in a table row
10094 * @cfg {String} bgcolor Specifies a background color for a table row
10095 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10096 * @cfg {String} valign Vertical aligns the content in a table row
10099 * Create a new TableRow
10100 * @param {Object} config The config object
10103 Roo.bootstrap.TableRow = function(config){
10104 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10107 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10115 getAutoCreate : function(){
10116 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10123 cfg.cls = this.cls;
10126 cfg.align = this.align;
10129 cfg.bgcolor = this.bgcolor;
10132 cfg.charoff = this.charoff;
10135 cfg.valign = this.valign;
10153 * @class Roo.bootstrap.TableBody
10154 * @extends Roo.bootstrap.Component
10155 * Bootstrap TableBody class
10156 * @cfg {String} cls element class
10157 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10158 * @cfg {String} align Aligns the content inside the element
10159 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10160 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10163 * Create a new TableBody
10164 * @param {Object} config The config object
10167 Roo.bootstrap.TableBody = function(config){
10168 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10171 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10179 getAutoCreate : function(){
10180 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10190 cfg.tag = this.tag;
10194 cfg.align = this.align;
10197 cfg.charoff = this.charoff;
10200 cfg.valign = this.valign;
10207 // initEvents : function()
10210 // if(!this.store){
10214 // this.store = Roo.factory(this.store, Roo.data);
10215 // this.store.on('load', this.onLoad, this);
10217 // this.store.load();
10221 // onLoad: function ()
10223 // this.fireEvent('load', this);
10233 * Ext JS Library 1.1.1
10234 * Copyright(c) 2006-2007, Ext JS, LLC.
10236 * Originally Released Under LGPL - original licence link has changed is not relivant.
10239 * <script type="text/javascript">
10242 // as we use this in bootstrap.
10243 Roo.namespace('Roo.form');
10245 * @class Roo.form.Action
10246 * Internal Class used to handle form actions
10248 * @param {Roo.form.BasicForm} el The form element or its id
10249 * @param {Object} config Configuration options
10254 // define the action interface
10255 Roo.form.Action = function(form, options){
10257 this.options = options || {};
10260 * Client Validation Failed
10263 Roo.form.Action.CLIENT_INVALID = 'client';
10265 * Server Validation Failed
10268 Roo.form.Action.SERVER_INVALID = 'server';
10270 * Connect to Server Failed
10273 Roo.form.Action.CONNECT_FAILURE = 'connect';
10275 * Reading Data from Server Failed
10278 Roo.form.Action.LOAD_FAILURE = 'load';
10280 Roo.form.Action.prototype = {
10282 failureType : undefined,
10283 response : undefined,
10284 result : undefined,
10286 // interface method
10287 run : function(options){
10291 // interface method
10292 success : function(response){
10296 // interface method
10297 handleResponse : function(response){
10301 // default connection failure
10302 failure : function(response){
10304 this.response = response;
10305 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10306 this.form.afterAction(this, false);
10309 processResponse : function(response){
10310 this.response = response;
10311 if(!response.responseText){
10314 this.result = this.handleResponse(response);
10315 return this.result;
10318 // utility functions used internally
10319 getUrl : function(appendParams){
10320 var url = this.options.url || this.form.url || this.form.el.dom.action;
10322 var p = this.getParams();
10324 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10330 getMethod : function(){
10331 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10334 getParams : function(){
10335 var bp = this.form.baseParams;
10336 var p = this.options.params;
10338 if(typeof p == "object"){
10339 p = Roo.urlEncode(Roo.applyIf(p, bp));
10340 }else if(typeof p == 'string' && bp){
10341 p += '&' + Roo.urlEncode(bp);
10344 p = Roo.urlEncode(bp);
10349 createCallback : function(){
10351 success: this.success,
10352 failure: this.failure,
10354 timeout: (this.form.timeout*1000),
10355 upload: this.form.fileUpload ? this.success : undefined
10360 Roo.form.Action.Submit = function(form, options){
10361 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10364 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10367 haveProgress : false,
10368 uploadComplete : false,
10370 // uploadProgress indicator.
10371 uploadProgress : function()
10373 if (!this.form.progressUrl) {
10377 if (!this.haveProgress) {
10378 Roo.MessageBox.progress("Uploading", "Uploading");
10380 if (this.uploadComplete) {
10381 Roo.MessageBox.hide();
10385 this.haveProgress = true;
10387 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10389 var c = new Roo.data.Connection();
10391 url : this.form.progressUrl,
10396 success : function(req){
10397 //console.log(data);
10401 rdata = Roo.decode(req.responseText)
10403 Roo.log("Invalid data from server..");
10407 if (!rdata || !rdata.success) {
10409 Roo.MessageBox.alert(Roo.encode(rdata));
10412 var data = rdata.data;
10414 if (this.uploadComplete) {
10415 Roo.MessageBox.hide();
10420 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10421 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10424 this.uploadProgress.defer(2000,this);
10427 failure: function(data) {
10428 Roo.log('progress url failed ');
10439 // run get Values on the form, so it syncs any secondary forms.
10440 this.form.getValues();
10442 var o = this.options;
10443 var method = this.getMethod();
10444 var isPost = method == 'POST';
10445 if(o.clientValidation === false || this.form.isValid()){
10447 if (this.form.progressUrl) {
10448 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10449 (new Date() * 1) + '' + Math.random());
10454 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10455 form:this.form.el.dom,
10456 url:this.getUrl(!isPost),
10458 params:isPost ? this.getParams() : null,
10459 isUpload: this.form.fileUpload,
10460 formData : this.form.formData
10463 this.uploadProgress();
10465 }else if (o.clientValidation !== false){ // client validation failed
10466 this.failureType = Roo.form.Action.CLIENT_INVALID;
10467 this.form.afterAction(this, false);
10471 success : function(response)
10473 this.uploadComplete= true;
10474 if (this.haveProgress) {
10475 Roo.MessageBox.hide();
10479 var result = this.processResponse(response);
10480 if(result === true || result.success){
10481 this.form.afterAction(this, true);
10485 this.form.markInvalid(result.errors);
10486 this.failureType = Roo.form.Action.SERVER_INVALID;
10488 this.form.afterAction(this, false);
10490 failure : function(response)
10492 this.uploadComplete= true;
10493 if (this.haveProgress) {
10494 Roo.MessageBox.hide();
10497 this.response = response;
10498 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10499 this.form.afterAction(this, false);
10502 handleResponse : function(response){
10503 if(this.form.errorReader){
10504 var rs = this.form.errorReader.read(response);
10507 for(var i = 0, len = rs.records.length; i < len; i++) {
10508 var r = rs.records[i];
10509 errors[i] = r.data;
10512 if(errors.length < 1){
10516 success : rs.success,
10522 ret = Roo.decode(response.responseText);
10526 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10536 Roo.form.Action.Load = function(form, options){
10537 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10538 this.reader = this.form.reader;
10541 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10546 Roo.Ajax.request(Roo.apply(
10547 this.createCallback(), {
10548 method:this.getMethod(),
10549 url:this.getUrl(false),
10550 params:this.getParams()
10554 success : function(response){
10556 var result = this.processResponse(response);
10557 if(result === true || !result.success || !result.data){
10558 this.failureType = Roo.form.Action.LOAD_FAILURE;
10559 this.form.afterAction(this, false);
10562 this.form.clearInvalid();
10563 this.form.setValues(result.data);
10564 this.form.afterAction(this, true);
10567 handleResponse : function(response){
10568 if(this.form.reader){
10569 var rs = this.form.reader.read(response);
10570 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10572 success : rs.success,
10576 return Roo.decode(response.responseText);
10580 Roo.form.Action.ACTION_TYPES = {
10581 'load' : Roo.form.Action.Load,
10582 'submit' : Roo.form.Action.Submit
10591 * @class Roo.bootstrap.Form
10592 * @extends Roo.bootstrap.Component
10593 * Bootstrap Form class
10594 * @cfg {String} method GET | POST (default POST)
10595 * @cfg {String} labelAlign top | left (default top)
10596 * @cfg {String} align left | right - for navbars
10597 * @cfg {Boolean} loadMask load mask when submit (default true)
10601 * Create a new Form
10602 * @param {Object} config The config object
10606 Roo.bootstrap.Form = function(config){
10608 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10610 Roo.bootstrap.Form.popover.apply();
10614 * @event clientvalidation
10615 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10616 * @param {Form} this
10617 * @param {Boolean} valid true if the form has passed client-side validation
10619 clientvalidation: true,
10621 * @event beforeaction
10622 * Fires before any action is performed. Return false to cancel the action.
10623 * @param {Form} this
10624 * @param {Action} action The action to be performed
10626 beforeaction: true,
10628 * @event actionfailed
10629 * Fires when an action fails.
10630 * @param {Form} this
10631 * @param {Action} action The action that failed
10633 actionfailed : true,
10635 * @event actioncomplete
10636 * Fires when an action is completed.
10637 * @param {Form} this
10638 * @param {Action} action The action that completed
10640 actioncomplete : true
10644 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10647 * @cfg {String} method
10648 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10652 * @cfg {String} url
10653 * The URL to use for form actions if one isn't supplied in the action options.
10656 * @cfg {Boolean} fileUpload
10657 * Set to true if this form is a file upload.
10661 * @cfg {Object} baseParams
10662 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10666 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10670 * @cfg {Sting} align (left|right) for navbar forms
10675 activeAction : null,
10678 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10679 * element by passing it or its id or mask the form itself by passing in true.
10682 waitMsgTarget : false,
10687 * @cfg {Boolean} errorMask (true|false) default false
10692 * @cfg {Number} maskOffset Default 100
10697 * @cfg {Boolean} maskBody
10701 getAutoCreate : function(){
10705 method : this.method || 'POST',
10706 id : this.id || Roo.id(),
10709 if (this.parent().xtype.match(/^Nav/)) {
10710 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10714 if (this.labelAlign == 'left' ) {
10715 cfg.cls += ' form-horizontal';
10721 initEvents : function()
10723 this.el.on('submit', this.onSubmit, this);
10724 // this was added as random key presses on the form where triggering form submit.
10725 this.el.on('keypress', function(e) {
10726 if (e.getCharCode() != 13) {
10729 // we might need to allow it for textareas.. and some other items.
10730 // check e.getTarget().
10732 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10736 Roo.log("keypress blocked");
10738 e.preventDefault();
10744 onSubmit : function(e){
10749 * Returns true if client-side validation on the form is successful.
10752 isValid : function(){
10753 var items = this.getItems();
10755 var target = false;
10757 items.each(function(f){
10763 Roo.log('invalid field: ' + f.name);
10767 if(!target && f.el.isVisible(true)){
10773 if(this.errorMask && !valid){
10774 Roo.bootstrap.Form.popover.mask(this, target);
10781 * Returns true if any fields in this form have changed since their original load.
10784 isDirty : function(){
10786 var items = this.getItems();
10787 items.each(function(f){
10797 * Performs a predefined action (submit or load) or custom actions you define on this form.
10798 * @param {String} actionName The name of the action type
10799 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10800 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10801 * accept other config options):
10803 Property Type Description
10804 ---------------- --------------- ----------------------------------------------------------------------------------
10805 url String The url for the action (defaults to the form's url)
10806 method String The form method to use (defaults to the form's method, or POST if not defined)
10807 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10808 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10809 validate the form on the client (defaults to false)
10811 * @return {BasicForm} this
10813 doAction : function(action, options){
10814 if(typeof action == 'string'){
10815 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10817 if(this.fireEvent('beforeaction', this, action) !== false){
10818 this.beforeAction(action);
10819 action.run.defer(100, action);
10825 beforeAction : function(action){
10826 var o = action.options;
10831 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10833 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10836 // not really supported yet.. ??
10838 //if(this.waitMsgTarget === true){
10839 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10840 //}else if(this.waitMsgTarget){
10841 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10842 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10844 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10850 afterAction : function(action, success){
10851 this.activeAction = null;
10852 var o = action.options;
10857 Roo.get(document.body).unmask();
10863 //if(this.waitMsgTarget === true){
10864 // this.el.unmask();
10865 //}else if(this.waitMsgTarget){
10866 // this.waitMsgTarget.unmask();
10868 // Roo.MessageBox.updateProgress(1);
10869 // Roo.MessageBox.hide();
10876 Roo.callback(o.success, o.scope, [this, action]);
10877 this.fireEvent('actioncomplete', this, action);
10881 // failure condition..
10882 // we have a scenario where updates need confirming.
10883 // eg. if a locking scenario exists..
10884 // we look for { errors : { needs_confirm : true }} in the response.
10886 (typeof(action.result) != 'undefined') &&
10887 (typeof(action.result.errors) != 'undefined') &&
10888 (typeof(action.result.errors.needs_confirm) != 'undefined')
10891 Roo.log("not supported yet");
10894 Roo.MessageBox.confirm(
10895 "Change requires confirmation",
10896 action.result.errorMsg,
10901 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10911 Roo.callback(o.failure, o.scope, [this, action]);
10912 // show an error message if no failed handler is set..
10913 if (!this.hasListener('actionfailed')) {
10914 Roo.log("need to add dialog support");
10916 Roo.MessageBox.alert("Error",
10917 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10918 action.result.errorMsg :
10919 "Saving Failed, please check your entries or try again"
10924 this.fireEvent('actionfailed', this, action);
10929 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10930 * @param {String} id The value to search for
10933 findField : function(id){
10934 var items = this.getItems();
10935 var field = items.get(id);
10937 items.each(function(f){
10938 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10945 return field || null;
10948 * Mark fields in this form invalid in bulk.
10949 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10950 * @return {BasicForm} this
10952 markInvalid : function(errors){
10953 if(errors instanceof Array){
10954 for(var i = 0, len = errors.length; i < len; i++){
10955 var fieldError = errors[i];
10956 var f = this.findField(fieldError.id);
10958 f.markInvalid(fieldError.msg);
10964 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10965 field.markInvalid(errors[id]);
10969 //Roo.each(this.childForms || [], function (f) {
10970 // f.markInvalid(errors);
10977 * Set values for fields in this form in bulk.
10978 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10979 * @return {BasicForm} this
10981 setValues : function(values){
10982 if(values instanceof Array){ // array of objects
10983 for(var i = 0, len = values.length; i < len; i++){
10985 var f = this.findField(v.id);
10987 f.setValue(v.value);
10988 if(this.trackResetOnLoad){
10989 f.originalValue = f.getValue();
10993 }else{ // object hash
10996 if(typeof values[id] != 'function' && (field = this.findField(id))){
10998 if (field.setFromData &&
10999 field.valueField &&
11000 field.displayField &&
11001 // combos' with local stores can
11002 // be queried via setValue()
11003 // to set their value..
11004 (field.store && !field.store.isLocal)
11008 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11009 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11010 field.setFromData(sd);
11012 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11014 field.setFromData(values);
11017 field.setValue(values[id]);
11021 if(this.trackResetOnLoad){
11022 field.originalValue = field.getValue();
11028 //Roo.each(this.childForms || [], function (f) {
11029 // f.setValues(values);
11036 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11037 * they are returned as an array.
11038 * @param {Boolean} asString
11041 getValues : function(asString){
11042 //if (this.childForms) {
11043 // copy values from the child forms
11044 // Roo.each(this.childForms, function (f) {
11045 // this.setValues(f.getValues());
11051 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11052 if(asString === true){
11055 return Roo.urlDecode(fs);
11059 * Returns the fields in this form as an object with key/value pairs.
11060 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11063 getFieldValues : function(with_hidden)
11065 var items = this.getItems();
11067 items.each(function(f){
11069 if (!f.getName()) {
11073 var v = f.getValue();
11075 if (f.inputType =='radio') {
11076 if (typeof(ret[f.getName()]) == 'undefined') {
11077 ret[f.getName()] = ''; // empty..
11080 if (!f.el.dom.checked) {
11084 v = f.el.dom.value;
11088 if(f.xtype == 'MoneyField'){
11089 ret[f.currencyName] = f.getCurrency();
11092 // not sure if this supported any more..
11093 if ((typeof(v) == 'object') && f.getRawValue) {
11094 v = f.getRawValue() ; // dates..
11096 // combo boxes where name != hiddenName...
11097 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11098 ret[f.name] = f.getRawValue();
11100 ret[f.getName()] = v;
11107 * Clears all invalid messages in this form.
11108 * @return {BasicForm} this
11110 clearInvalid : function(){
11111 var items = this.getItems();
11113 items.each(function(f){
11121 * Resets this form.
11122 * @return {BasicForm} this
11124 reset : function(){
11125 var items = this.getItems();
11126 items.each(function(f){
11130 Roo.each(this.childForms || [], function (f) {
11138 getItems : function()
11140 var r=new Roo.util.MixedCollection(false, function(o){
11141 return o.id || (o.id = Roo.id());
11143 var iter = function(el) {
11150 Roo.each(el.items,function(e) {
11159 hideFields : function(items)
11161 Roo.each(items, function(i){
11163 var f = this.findField(i);
11174 showFields : function(items)
11176 Roo.each(items, function(i){
11178 var f = this.findField(i);
11191 Roo.apply(Roo.bootstrap.Form, {
11207 intervalID : false,
11213 if(this.isApplied){
11218 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11219 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11220 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11221 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11224 this.maskEl.top.enableDisplayMode("block");
11225 this.maskEl.left.enableDisplayMode("block");
11226 this.maskEl.bottom.enableDisplayMode("block");
11227 this.maskEl.right.enableDisplayMode("block");
11229 this.toolTip = new Roo.bootstrap.Tooltip({
11230 cls : 'roo-form-error-popover',
11232 'left' : ['r-l', [-2,0], 'right'],
11233 'right' : ['l-r', [2,0], 'left'],
11234 'bottom' : ['tl-bl', [0,2], 'top'],
11235 'top' : [ 'bl-tl', [0,-2], 'bottom']
11239 this.toolTip.render(Roo.get(document.body));
11241 this.toolTip.el.enableDisplayMode("block");
11243 Roo.get(document.body).on('click', function(){
11247 Roo.get(document.body).on('touchstart', function(){
11251 this.isApplied = true
11254 mask : function(form, target)
11258 this.target = target;
11260 if(!this.form.errorMask || !target.el){
11264 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11266 Roo.log(scrollable);
11268 var ot = this.target.el.calcOffsetsTo(scrollable);
11270 var scrollTo = ot[1] - this.form.maskOffset;
11272 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11274 scrollable.scrollTo('top', scrollTo);
11276 var box = this.target.el.getBox();
11278 var zIndex = Roo.bootstrap.Modal.zIndex++;
11281 this.maskEl.top.setStyle('position', 'absolute');
11282 this.maskEl.top.setStyle('z-index', zIndex);
11283 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11284 this.maskEl.top.setLeft(0);
11285 this.maskEl.top.setTop(0);
11286 this.maskEl.top.show();
11288 this.maskEl.left.setStyle('position', 'absolute');
11289 this.maskEl.left.setStyle('z-index', zIndex);
11290 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11291 this.maskEl.left.setLeft(0);
11292 this.maskEl.left.setTop(box.y - this.padding);
11293 this.maskEl.left.show();
11295 this.maskEl.bottom.setStyle('position', 'absolute');
11296 this.maskEl.bottom.setStyle('z-index', zIndex);
11297 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11298 this.maskEl.bottom.setLeft(0);
11299 this.maskEl.bottom.setTop(box.bottom + this.padding);
11300 this.maskEl.bottom.show();
11302 this.maskEl.right.setStyle('position', 'absolute');
11303 this.maskEl.right.setStyle('z-index', zIndex);
11304 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11305 this.maskEl.right.setLeft(box.right + this.padding);
11306 this.maskEl.right.setTop(box.y - this.padding);
11307 this.maskEl.right.show();
11309 this.toolTip.bindEl = this.target.el;
11311 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11313 var tip = this.target.blankText;
11315 if(this.target.getValue() !== '' ) {
11317 if (this.target.invalidText.length) {
11318 tip = this.target.invalidText;
11319 } else if (this.target.regexText.length){
11320 tip = this.target.regexText;
11324 this.toolTip.show(tip);
11326 this.intervalID = window.setInterval(function() {
11327 Roo.bootstrap.Form.popover.unmask();
11330 window.onwheel = function(){ return false;};
11332 (function(){ this.isMasked = true; }).defer(500, this);
11336 unmask : function()
11338 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11342 this.maskEl.top.setStyle('position', 'absolute');
11343 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11344 this.maskEl.top.hide();
11346 this.maskEl.left.setStyle('position', 'absolute');
11347 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11348 this.maskEl.left.hide();
11350 this.maskEl.bottom.setStyle('position', 'absolute');
11351 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11352 this.maskEl.bottom.hide();
11354 this.maskEl.right.setStyle('position', 'absolute');
11355 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11356 this.maskEl.right.hide();
11358 this.toolTip.hide();
11360 this.toolTip.el.hide();
11362 window.onwheel = function(){ return true;};
11364 if(this.intervalID){
11365 window.clearInterval(this.intervalID);
11366 this.intervalID = false;
11369 this.isMasked = false;
11379 * Ext JS Library 1.1.1
11380 * Copyright(c) 2006-2007, Ext JS, LLC.
11382 * Originally Released Under LGPL - original licence link has changed is not relivant.
11385 * <script type="text/javascript">
11388 * @class Roo.form.VTypes
11389 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11392 Roo.form.VTypes = function(){
11393 // closure these in so they are only created once.
11394 var alpha = /^[a-zA-Z_]+$/;
11395 var alphanum = /^[a-zA-Z0-9_]+$/;
11396 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11397 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11399 // All these messages and functions are configurable
11402 * The function used to validate email addresses
11403 * @param {String} value The email address
11405 'email' : function(v){
11406 return email.test(v);
11409 * The error text to display when the email validation function returns false
11412 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11414 * The keystroke filter mask to be applied on email input
11417 'emailMask' : /[a-z0-9_\.\-@]/i,
11420 * The function used to validate URLs
11421 * @param {String} value The URL
11423 'url' : function(v){
11424 return url.test(v);
11427 * The error text to display when the url validation function returns false
11430 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11433 * The function used to validate alpha values
11434 * @param {String} value The value
11436 'alpha' : function(v){
11437 return alpha.test(v);
11440 * The error text to display when the alpha validation function returns false
11443 'alphaText' : 'This field should only contain letters and _',
11445 * The keystroke filter mask to be applied on alpha input
11448 'alphaMask' : /[a-z_]/i,
11451 * The function used to validate alphanumeric values
11452 * @param {String} value The value
11454 'alphanum' : function(v){
11455 return alphanum.test(v);
11458 * The error text to display when the alphanumeric validation function returns false
11461 'alphanumText' : 'This field should only contain letters, numbers and _',
11463 * The keystroke filter mask to be applied on alphanumeric input
11466 'alphanumMask' : /[a-z0-9_]/i
11476 * @class Roo.bootstrap.Input
11477 * @extends Roo.bootstrap.Component
11478 * Bootstrap Input class
11479 * @cfg {Boolean} disabled is it disabled
11480 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11481 * @cfg {String} name name of the input
11482 * @cfg {string} fieldLabel - the label associated
11483 * @cfg {string} placeholder - placeholder to put in text.
11484 * @cfg {string} before - input group add on before
11485 * @cfg {string} after - input group add on after
11486 * @cfg {string} size - (lg|sm) or leave empty..
11487 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11488 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11489 * @cfg {Number} md colspan out of 12 for computer-sized screens
11490 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11491 * @cfg {string} value default value of the input
11492 * @cfg {Number} labelWidth set the width of label
11493 * @cfg {Number} labellg set the width of label (1-12)
11494 * @cfg {Number} labelmd set the width of label (1-12)
11495 * @cfg {Number} labelsm set the width of label (1-12)
11496 * @cfg {Number} labelxs set the width of label (1-12)
11497 * @cfg {String} labelAlign (top|left)
11498 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11499 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11500 * @cfg {String} indicatorpos (left|right) default left
11501 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11502 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11503 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11505 * @cfg {String} align (left|center|right) Default left
11506 * @cfg {Boolean} forceFeedback (true|false) Default false
11509 * Create a new Input
11510 * @param {Object} config The config object
11513 Roo.bootstrap.Input = function(config){
11515 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11520 * Fires when this field receives input focus.
11521 * @param {Roo.form.Field} this
11526 * Fires when this field loses input focus.
11527 * @param {Roo.form.Field} this
11531 * @event specialkey
11532 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11533 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11534 * @param {Roo.form.Field} this
11535 * @param {Roo.EventObject} e The event object
11540 * Fires just before the field blurs if the field value has changed.
11541 * @param {Roo.form.Field} this
11542 * @param {Mixed} newValue The new value
11543 * @param {Mixed} oldValue The original value
11548 * Fires after the field has been marked as invalid.
11549 * @param {Roo.form.Field} this
11550 * @param {String} msg The validation message
11555 * Fires after the field has been validated with no errors.
11556 * @param {Roo.form.Field} this
11561 * Fires after the key up
11562 * @param {Roo.form.Field} this
11563 * @param {Roo.EventObject} e The event Object
11568 * Fires after the user pastes into input
11569 * @param {Roo.form.Field} this
11570 * @param {Roo.EventObject} e The event Object
11576 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11578 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11579 automatic validation (defaults to "keyup").
11581 validationEvent : "keyup",
11583 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11585 validateOnBlur : true,
11587 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11589 validationDelay : 250,
11591 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11593 focusClass : "x-form-focus", // not needed???
11597 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11599 invalidClass : "has-warning",
11602 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11604 validClass : "has-success",
11607 * @cfg {Boolean} hasFeedback (true|false) default true
11609 hasFeedback : true,
11612 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11614 invalidFeedbackClass : "glyphicon-warning-sign",
11617 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11619 validFeedbackClass : "glyphicon-ok",
11622 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11624 selectOnFocus : false,
11627 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11631 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11636 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11638 disableKeyFilter : false,
11641 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11645 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11649 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11651 blankText : "Please complete this mandatory field",
11654 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11658 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11660 maxLength : Number.MAX_VALUE,
11662 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11664 minLengthText : "The minimum length for this field is {0}",
11666 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11668 maxLengthText : "The maximum length for this field is {0}",
11672 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11673 * If available, this function will be called only after the basic validators all return true, and will be passed the
11674 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11678 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11679 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11680 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11684 * @cfg {String} regexText -- Depricated - use Invalid Text
11689 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11695 autocomplete: false,
11699 inputType : 'text',
11702 placeholder: false,
11707 preventMark: false,
11708 isFormField : true,
11711 labelAlign : false,
11714 formatedValue : false,
11715 forceFeedback : false,
11717 indicatorpos : 'left',
11727 parentLabelAlign : function()
11730 while (parent.parent()) {
11731 parent = parent.parent();
11732 if (typeof(parent.labelAlign) !='undefined') {
11733 return parent.labelAlign;
11740 getAutoCreate : function()
11742 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11748 if(this.inputType != 'hidden'){
11749 cfg.cls = 'form-group' //input-group
11755 type : this.inputType,
11756 value : this.value,
11757 cls : 'form-control',
11758 placeholder : this.placeholder || '',
11759 autocomplete : this.autocomplete || 'new-password'
11761 if (this.inputType == 'file') {
11762 input.style = 'overflow:hidden'; // why not in CSS?
11765 if(this.capture.length){
11766 input.capture = this.capture;
11769 if(this.accept.length){
11770 input.accept = this.accept + "/*";
11774 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11777 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11778 input.maxLength = this.maxLength;
11781 if (this.disabled) {
11782 input.disabled=true;
11785 if (this.readOnly) {
11786 input.readonly=true;
11790 input.name = this.name;
11794 input.cls += ' input-' + this.size;
11798 ['xs','sm','md','lg'].map(function(size){
11799 if (settings[size]) {
11800 cfg.cls += ' col-' + size + '-' + settings[size];
11804 var inputblock = input;
11808 cls: 'glyphicon form-control-feedback'
11811 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11814 cls : 'has-feedback',
11822 if (this.before || this.after) {
11825 cls : 'input-group',
11829 if (this.before && typeof(this.before) == 'string') {
11831 inputblock.cn.push({
11833 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11837 if (this.before && typeof(this.before) == 'object') {
11838 this.before = Roo.factory(this.before);
11840 inputblock.cn.push({
11842 cls : 'roo-input-before input-group-prepend input-group-' +
11843 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11847 inputblock.cn.push(input);
11849 if (this.after && typeof(this.after) == 'string') {
11850 inputblock.cn.push({
11852 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11856 if (this.after && typeof(this.after) == 'object') {
11857 this.after = Roo.factory(this.after);
11859 inputblock.cn.push({
11861 cls : 'roo-input-after input-group-append input-group-' +
11862 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11866 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11867 inputblock.cls += ' has-feedback';
11868 inputblock.cn.push(feedback);
11873 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11874 tooltip : 'This field is required'
11876 if (this.allowBlank ) {
11877 indicator.style = this.allowBlank ? ' display:none' : '';
11879 if (align ==='left' && this.fieldLabel.length) {
11881 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11888 cls : 'control-label col-form-label',
11889 html : this.fieldLabel
11900 var labelCfg = cfg.cn[1];
11901 var contentCfg = cfg.cn[2];
11903 if(this.indicatorpos == 'right'){
11908 cls : 'control-label col-form-label',
11912 html : this.fieldLabel
11926 labelCfg = cfg.cn[0];
11927 contentCfg = cfg.cn[1];
11931 if(this.labelWidth > 12){
11932 labelCfg.style = "width: " + this.labelWidth + 'px';
11935 if(this.labelWidth < 13 && this.labelmd == 0){
11936 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11939 if(this.labellg > 0){
11940 labelCfg.cls += ' col-lg-' + this.labellg;
11941 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11944 if(this.labelmd > 0){
11945 labelCfg.cls += ' col-md-' + this.labelmd;
11946 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11949 if(this.labelsm > 0){
11950 labelCfg.cls += ' col-sm-' + this.labelsm;
11951 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11954 if(this.labelxs > 0){
11955 labelCfg.cls += ' col-xs-' + this.labelxs;
11956 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11960 } else if ( this.fieldLabel.length) {
11967 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11968 tooltip : 'This field is required',
11969 style : this.allowBlank ? ' display:none' : ''
11973 //cls : 'input-group-addon',
11974 html : this.fieldLabel
11982 if(this.indicatorpos == 'right'){
11987 //cls : 'input-group-addon',
11988 html : this.fieldLabel
11993 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11994 tooltip : 'This field is required',
11995 style : this.allowBlank ? ' display:none' : ''
12015 if (this.parentType === 'Navbar' && this.parent().bar) {
12016 cfg.cls += ' navbar-form';
12019 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12020 // on BS4 we do this only if not form
12021 cfg.cls += ' navbar-form';
12029 * return the real input element.
12031 inputEl: function ()
12033 return this.el.select('input.form-control',true).first();
12036 tooltipEl : function()
12038 return this.inputEl();
12041 indicatorEl : function()
12043 if (Roo.bootstrap.version == 4) {
12044 return false; // not enabled in v4 yet.
12047 var indicator = this.el.select('i.roo-required-indicator',true).first();
12057 setDisabled : function(v)
12059 var i = this.inputEl().dom;
12061 i.removeAttribute('disabled');
12065 i.setAttribute('disabled','true');
12067 initEvents : function()
12070 this.inputEl().on("keydown" , this.fireKey, this);
12071 this.inputEl().on("focus", this.onFocus, this);
12072 this.inputEl().on("blur", this.onBlur, this);
12074 this.inputEl().relayEvent('keyup', this);
12075 this.inputEl().relayEvent('paste', this);
12077 this.indicator = this.indicatorEl();
12079 if(this.indicator){
12080 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12083 // reference to original value for reset
12084 this.originalValue = this.getValue();
12085 //Roo.form.TextField.superclass.initEvents.call(this);
12086 if(this.validationEvent == 'keyup'){
12087 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12088 this.inputEl().on('keyup', this.filterValidation, this);
12090 else if(this.validationEvent !== false){
12091 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12094 if(this.selectOnFocus){
12095 this.on("focus", this.preFocus, this);
12098 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12099 this.inputEl().on("keypress", this.filterKeys, this);
12101 this.inputEl().relayEvent('keypress', this);
12104 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12105 this.el.on("click", this.autoSize, this);
12108 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12109 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12112 if (typeof(this.before) == 'object') {
12113 this.before.render(this.el.select('.roo-input-before',true).first());
12115 if (typeof(this.after) == 'object') {
12116 this.after.render(this.el.select('.roo-input-after',true).first());
12119 this.inputEl().on('change', this.onChange, this);
12122 filterValidation : function(e){
12123 if(!e.isNavKeyPress()){
12124 this.validationTask.delay(this.validationDelay);
12128 * Validates the field value
12129 * @return {Boolean} True if the value is valid, else false
12131 validate : function(){
12132 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12133 if(this.disabled || this.validateValue(this.getRawValue())){
12138 this.markInvalid();
12144 * Validates a value according to the field's validation rules and marks the field as invalid
12145 * if the validation fails
12146 * @param {Mixed} value The value to validate
12147 * @return {Boolean} True if the value is valid, else false
12149 validateValue : function(value)
12151 if(this.getVisibilityEl().hasClass('hidden')){
12155 if(value.length < 1) { // if it's blank
12156 if(this.allowBlank){
12162 if(value.length < this.minLength){
12165 if(value.length > this.maxLength){
12169 var vt = Roo.form.VTypes;
12170 if(!vt[this.vtype](value, this)){
12174 if(typeof this.validator == "function"){
12175 var msg = this.validator(value);
12179 if (typeof(msg) == 'string') {
12180 this.invalidText = msg;
12184 if(this.regex && !this.regex.test(value)){
12192 fireKey : function(e){
12193 //Roo.log('field ' + e.getKey());
12194 if(e.isNavKeyPress()){
12195 this.fireEvent("specialkey", this, e);
12198 focus : function (selectText){
12200 this.inputEl().focus();
12201 if(selectText === true){
12202 this.inputEl().dom.select();
12208 onFocus : function(){
12209 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12210 // this.el.addClass(this.focusClass);
12212 if(!this.hasFocus){
12213 this.hasFocus = true;
12214 this.startValue = this.getValue();
12215 this.fireEvent("focus", this);
12219 beforeBlur : Roo.emptyFn,
12223 onBlur : function(){
12225 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12226 //this.el.removeClass(this.focusClass);
12228 this.hasFocus = false;
12229 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12232 var v = this.getValue();
12233 if(String(v) !== String(this.startValue)){
12234 this.fireEvent('change', this, v, this.startValue);
12236 this.fireEvent("blur", this);
12239 onChange : function(e)
12241 var v = this.getValue();
12242 if(String(v) !== String(this.startValue)){
12243 this.fireEvent('change', this, v, this.startValue);
12249 * Resets the current field value to the originally loaded value and clears any validation messages
12251 reset : function(){
12252 this.setValue(this.originalValue);
12256 * Returns the name of the field
12257 * @return {Mixed} name The name field
12259 getName: function(){
12263 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12264 * @return {Mixed} value The field value
12266 getValue : function(){
12268 var v = this.inputEl().getValue();
12273 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12274 * @return {Mixed} value The field value
12276 getRawValue : function(){
12277 var v = this.inputEl().getValue();
12283 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12284 * @param {Mixed} value The value to set
12286 setRawValue : function(v){
12287 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12290 selectText : function(start, end){
12291 var v = this.getRawValue();
12293 start = start === undefined ? 0 : start;
12294 end = end === undefined ? v.length : end;
12295 var d = this.inputEl().dom;
12296 if(d.setSelectionRange){
12297 d.setSelectionRange(start, end);
12298 }else if(d.createTextRange){
12299 var range = d.createTextRange();
12300 range.moveStart("character", start);
12301 range.moveEnd("character", v.length-end);
12308 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12309 * @param {Mixed} value The value to set
12311 setValue : function(v){
12314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12320 processValue : function(value){
12321 if(this.stripCharsRe){
12322 var newValue = value.replace(this.stripCharsRe, '');
12323 if(newValue !== value){
12324 this.setRawValue(newValue);
12331 preFocus : function(){
12333 if(this.selectOnFocus){
12334 this.inputEl().dom.select();
12337 filterKeys : function(e){
12338 var k = e.getKey();
12339 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12342 var c = e.getCharCode(), cc = String.fromCharCode(c);
12343 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12346 if(!this.maskRe.test(cc)){
12351 * Clear any invalid styles/messages for this field
12353 clearInvalid : function(){
12355 if(!this.el || this.preventMark){ // not rendered
12360 this.el.removeClass([this.invalidClass, 'is-invalid']);
12362 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12364 var feedback = this.el.select('.form-control-feedback', true).first();
12367 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12372 if(this.indicator){
12373 this.indicator.removeClass('visible');
12374 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12377 this.fireEvent('valid', this);
12381 * Mark this field as valid
12383 markValid : function()
12385 if(!this.el || this.preventMark){ // not rendered...
12389 this.el.removeClass([this.invalidClass, this.validClass]);
12390 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12392 var feedback = this.el.select('.form-control-feedback', true).first();
12395 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12398 if(this.indicator){
12399 this.indicator.removeClass('visible');
12400 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12408 if(this.allowBlank && !this.getRawValue().length){
12411 if (Roo.bootstrap.version == 3) {
12412 this.el.addClass(this.validClass);
12414 this.inputEl().addClass('is-valid');
12417 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12419 var feedback = this.el.select('.form-control-feedback', true).first();
12422 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12423 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12428 this.fireEvent('valid', this);
12432 * Mark this field as invalid
12433 * @param {String} msg The validation message
12435 markInvalid : function(msg)
12437 if(!this.el || this.preventMark){ // not rendered
12441 this.el.removeClass([this.invalidClass, this.validClass]);
12442 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12444 var feedback = this.el.select('.form-control-feedback', true).first();
12447 this.el.select('.form-control-feedback', true).first().removeClass(
12448 [this.invalidFeedbackClass, this.validFeedbackClass]);
12455 if(this.allowBlank && !this.getRawValue().length){
12459 if(this.indicator){
12460 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12461 this.indicator.addClass('visible');
12463 if (Roo.bootstrap.version == 3) {
12464 this.el.addClass(this.invalidClass);
12466 this.inputEl().addClass('is-invalid');
12471 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12473 var feedback = this.el.select('.form-control-feedback', true).first();
12476 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12478 if(this.getValue().length || this.forceFeedback){
12479 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12486 this.fireEvent('invalid', this, msg);
12489 SafariOnKeyDown : function(event)
12491 // this is a workaround for a password hang bug on chrome/ webkit.
12492 if (this.inputEl().dom.type != 'password') {
12496 var isSelectAll = false;
12498 if(this.inputEl().dom.selectionEnd > 0){
12499 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12501 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12502 event.preventDefault();
12507 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12509 event.preventDefault();
12510 // this is very hacky as keydown always get's upper case.
12512 var cc = String.fromCharCode(event.getCharCode());
12513 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12517 adjustWidth : function(tag, w){
12518 tag = tag.toLowerCase();
12519 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12520 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12521 if(tag == 'input'){
12524 if(tag == 'textarea'){
12527 }else if(Roo.isOpera){
12528 if(tag == 'input'){
12531 if(tag == 'textarea'){
12539 setFieldLabel : function(v)
12541 if(!this.rendered){
12545 if(this.indicatorEl()){
12546 var ar = this.el.select('label > span',true);
12548 if (ar.elements.length) {
12549 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12550 this.fieldLabel = v;
12554 var br = this.el.select('label',true);
12556 if(br.elements.length) {
12557 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12558 this.fieldLabel = v;
12562 Roo.log('Cannot Found any of label > span || label in input');
12566 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12567 this.fieldLabel = v;
12582 * @class Roo.bootstrap.TextArea
12583 * @extends Roo.bootstrap.Input
12584 * Bootstrap TextArea class
12585 * @cfg {Number} cols Specifies the visible width of a text area
12586 * @cfg {Number} rows Specifies the visible number of lines in a text area
12587 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12588 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12589 * @cfg {string} html text
12592 * Create a new TextArea
12593 * @param {Object} config The config object
12596 Roo.bootstrap.TextArea = function(config){
12597 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12601 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12611 getAutoCreate : function(){
12613 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12619 if(this.inputType != 'hidden'){
12620 cfg.cls = 'form-group' //input-group
12628 value : this.value || '',
12629 html: this.html || '',
12630 cls : 'form-control',
12631 placeholder : this.placeholder || ''
12635 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12636 input.maxLength = this.maxLength;
12640 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12644 input.cols = this.cols;
12647 if (this.readOnly) {
12648 input.readonly = true;
12652 input.name = this.name;
12656 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12660 ['xs','sm','md','lg'].map(function(size){
12661 if (settings[size]) {
12662 cfg.cls += ' col-' + size + '-' + settings[size];
12666 var inputblock = input;
12668 if(this.hasFeedback && !this.allowBlank){
12672 cls: 'glyphicon form-control-feedback'
12676 cls : 'has-feedback',
12685 if (this.before || this.after) {
12688 cls : 'input-group',
12692 inputblock.cn.push({
12694 cls : 'input-group-addon',
12699 inputblock.cn.push(input);
12701 if(this.hasFeedback && !this.allowBlank){
12702 inputblock.cls += ' has-feedback';
12703 inputblock.cn.push(feedback);
12707 inputblock.cn.push({
12709 cls : 'input-group-addon',
12716 if (align ==='left' && this.fieldLabel.length) {
12721 cls : 'control-label',
12722 html : this.fieldLabel
12733 if(this.labelWidth > 12){
12734 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12737 if(this.labelWidth < 13 && this.labelmd == 0){
12738 this.labelmd = this.labelWidth;
12741 if(this.labellg > 0){
12742 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12743 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12746 if(this.labelmd > 0){
12747 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12748 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12751 if(this.labelsm > 0){
12752 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12753 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12756 if(this.labelxs > 0){
12757 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12758 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12761 } else if ( this.fieldLabel.length) {
12766 //cls : 'input-group-addon',
12767 html : this.fieldLabel
12785 if (this.disabled) {
12786 input.disabled=true;
12793 * return the real textarea element.
12795 inputEl: function ()
12797 return this.el.select('textarea.form-control',true).first();
12801 * Clear any invalid styles/messages for this field
12803 clearInvalid : function()
12806 if(!this.el || this.preventMark){ // not rendered
12810 var label = this.el.select('label', true).first();
12811 var icon = this.el.select('i.fa-star', true).first();
12816 this.el.removeClass( this.validClass);
12817 this.inputEl().removeClass('is-invalid');
12819 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12821 var feedback = this.el.select('.form-control-feedback', true).first();
12824 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12829 this.fireEvent('valid', this);
12833 * Mark this field as valid
12835 markValid : function()
12837 if(!this.el || this.preventMark){ // not rendered
12841 this.el.removeClass([this.invalidClass, this.validClass]);
12842 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12844 var feedback = this.el.select('.form-control-feedback', true).first();
12847 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12850 if(this.disabled || this.allowBlank){
12854 var label = this.el.select('label', true).first();
12855 var icon = this.el.select('i.fa-star', true).first();
12860 if (Roo.bootstrap.version == 3) {
12861 this.el.addClass(this.validClass);
12863 this.inputEl().addClass('is-valid');
12867 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12869 var feedback = this.el.select('.form-control-feedback', true).first();
12872 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12873 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12878 this.fireEvent('valid', this);
12882 * Mark this field as invalid
12883 * @param {String} msg The validation message
12885 markInvalid : function(msg)
12887 if(!this.el || this.preventMark){ // not rendered
12891 this.el.removeClass([this.invalidClass, this.validClass]);
12892 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12894 var feedback = this.el.select('.form-control-feedback', true).first();
12897 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12900 if(this.disabled || this.allowBlank){
12904 var label = this.el.select('label', true).first();
12905 var icon = this.el.select('i.fa-star', true).first();
12907 if(!this.getValue().length && label && !icon){
12908 this.el.createChild({
12910 cls : 'text-danger fa fa-lg fa-star',
12911 tooltip : 'This field is required',
12912 style : 'margin-right:5px;'
12916 if (Roo.bootstrap.version == 3) {
12917 this.el.addClass(this.invalidClass);
12919 this.inputEl().addClass('is-invalid');
12922 // fixme ... this may be depricated need to test..
12923 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12925 var feedback = this.el.select('.form-control-feedback', true).first();
12928 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12930 if(this.getValue().length || this.forceFeedback){
12931 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12938 this.fireEvent('invalid', this, msg);
12946 * trigger field - base class for combo..
12951 * @class Roo.bootstrap.TriggerField
12952 * @extends Roo.bootstrap.Input
12953 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12954 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12955 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12956 * for which you can provide a custom implementation. For example:
12958 var trigger = new Roo.bootstrap.TriggerField();
12959 trigger.onTriggerClick = myTriggerFn;
12960 trigger.applyTo('my-field');
12963 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12964 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12965 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12966 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12967 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12970 * Create a new TriggerField.
12971 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12972 * to the base TextField)
12974 Roo.bootstrap.TriggerField = function(config){
12975 this.mimicing = false;
12976 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12979 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12981 * @cfg {String} triggerClass A CSS class to apply to the trigger
12984 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12989 * @cfg {Boolean} removable (true|false) special filter default false
12993 /** @cfg {Boolean} grow @hide */
12994 /** @cfg {Number} growMin @hide */
12995 /** @cfg {Number} growMax @hide */
13001 autoSize: Roo.emptyFn,
13005 deferHeight : true,
13008 actionMode : 'wrap',
13013 getAutoCreate : function(){
13015 var align = this.labelAlign || this.parentLabelAlign();
13020 cls: 'form-group' //input-group
13027 type : this.inputType,
13028 cls : 'form-control',
13029 autocomplete: 'new-password',
13030 placeholder : this.placeholder || ''
13034 input.name = this.name;
13037 input.cls += ' input-' + this.size;
13040 if (this.disabled) {
13041 input.disabled=true;
13044 var inputblock = input;
13046 if(this.hasFeedback && !this.allowBlank){
13050 cls: 'glyphicon form-control-feedback'
13053 if(this.removable && !this.editable ){
13055 cls : 'has-feedback',
13061 cls : 'roo-combo-removable-btn close'
13068 cls : 'has-feedback',
13077 if(this.removable && !this.editable ){
13079 cls : 'roo-removable',
13085 cls : 'roo-combo-removable-btn close'
13092 if (this.before || this.after) {
13095 cls : 'input-group',
13099 inputblock.cn.push({
13101 cls : 'input-group-addon input-group-prepend input-group-text',
13106 inputblock.cn.push(input);
13108 if(this.hasFeedback && !this.allowBlank){
13109 inputblock.cls += ' has-feedback';
13110 inputblock.cn.push(feedback);
13114 inputblock.cn.push({
13116 cls : 'input-group-addon input-group-append input-group-text',
13125 var ibwrap = inputblock;
13130 cls: 'roo-select2-choices',
13134 cls: 'roo-select2-search-field',
13146 cls: 'roo-select2-container input-group',
13151 cls: 'form-hidden-field'
13157 if(!this.multiple && this.showToggleBtn){
13163 if (this.caret != false) {
13166 cls: 'fa fa-' + this.caret
13173 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13175 Roo.bootstrap.version == 3 ? caret : '',
13178 cls: 'combobox-clear',
13192 combobox.cls += ' roo-select2-container-multi';
13196 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13197 tooltip : 'This field is required'
13199 if (Roo.bootstrap.version == 4) {
13202 style : 'display:none'
13207 if (align ==='left' && this.fieldLabel.length) {
13209 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13216 cls : 'control-label',
13217 html : this.fieldLabel
13229 var labelCfg = cfg.cn[1];
13230 var contentCfg = cfg.cn[2];
13232 if(this.indicatorpos == 'right'){
13237 cls : 'control-label',
13241 html : this.fieldLabel
13255 labelCfg = cfg.cn[0];
13256 contentCfg = cfg.cn[1];
13259 if(this.labelWidth > 12){
13260 labelCfg.style = "width: " + this.labelWidth + 'px';
13263 if(this.labelWidth < 13 && this.labelmd == 0){
13264 this.labelmd = this.labelWidth;
13267 if(this.labellg > 0){
13268 labelCfg.cls += ' col-lg-' + this.labellg;
13269 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13272 if(this.labelmd > 0){
13273 labelCfg.cls += ' col-md-' + this.labelmd;
13274 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13277 if(this.labelsm > 0){
13278 labelCfg.cls += ' col-sm-' + this.labelsm;
13279 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13282 if(this.labelxs > 0){
13283 labelCfg.cls += ' col-xs-' + this.labelxs;
13284 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13287 } else if ( this.fieldLabel.length) {
13288 // Roo.log(" label");
13293 //cls : 'input-group-addon',
13294 html : this.fieldLabel
13302 if(this.indicatorpos == 'right'){
13310 html : this.fieldLabel
13324 // Roo.log(" no label && no align");
13331 ['xs','sm','md','lg'].map(function(size){
13332 if (settings[size]) {
13333 cfg.cls += ' col-' + size + '-' + settings[size];
13344 onResize : function(w, h){
13345 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13346 // if(typeof w == 'number'){
13347 // var x = w - this.trigger.getWidth();
13348 // this.inputEl().setWidth(this.adjustWidth('input', x));
13349 // this.trigger.setStyle('left', x+'px');
13354 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13357 getResizeEl : function(){
13358 return this.inputEl();
13362 getPositionEl : function(){
13363 return this.inputEl();
13367 alignErrorIcon : function(){
13368 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13372 initEvents : function(){
13376 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13377 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13378 if(!this.multiple && this.showToggleBtn){
13379 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13380 if(this.hideTrigger){
13381 this.trigger.setDisplayed(false);
13383 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13387 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13390 if(this.removable && !this.editable && !this.tickable){
13391 var close = this.closeTriggerEl();
13394 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13395 close.on('click', this.removeBtnClick, this, close);
13399 //this.trigger.addClassOnOver('x-form-trigger-over');
13400 //this.trigger.addClassOnClick('x-form-trigger-click');
13403 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13407 closeTriggerEl : function()
13409 var close = this.el.select('.roo-combo-removable-btn', true).first();
13410 return close ? close : false;
13413 removeBtnClick : function(e, h, el)
13415 e.preventDefault();
13417 if(this.fireEvent("remove", this) !== false){
13419 this.fireEvent("afterremove", this)
13423 createList : function()
13425 this.list = Roo.get(document.body).createChild({
13426 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13427 cls: 'typeahead typeahead-long dropdown-menu shadow',
13428 style: 'display:none'
13431 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13436 initTrigger : function(){
13441 onDestroy : function(){
13443 this.trigger.removeAllListeners();
13444 // this.trigger.remove();
13447 // this.wrap.remove();
13449 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13453 onFocus : function(){
13454 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13456 if(!this.mimicing){
13457 this.wrap.addClass('x-trigger-wrap-focus');
13458 this.mimicing = true;
13459 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13460 if(this.monitorTab){
13461 this.el.on("keydown", this.checkTab, this);
13468 checkTab : function(e){
13469 if(e.getKey() == e.TAB){
13470 this.triggerBlur();
13475 onBlur : function(){
13480 mimicBlur : function(e, t){
13482 if(!this.wrap.contains(t) && this.validateBlur()){
13483 this.triggerBlur();
13489 triggerBlur : function(){
13490 this.mimicing = false;
13491 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13492 if(this.monitorTab){
13493 this.el.un("keydown", this.checkTab, this);
13495 //this.wrap.removeClass('x-trigger-wrap-focus');
13496 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13500 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13501 validateBlur : function(e, t){
13506 onDisable : function(){
13507 this.inputEl().dom.disabled = true;
13508 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13510 // this.wrap.addClass('x-item-disabled');
13515 onEnable : function(){
13516 this.inputEl().dom.disabled = false;
13517 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13519 // this.el.removeClass('x-item-disabled');
13524 onShow : function(){
13525 var ae = this.getActionEl();
13528 ae.dom.style.display = '';
13529 ae.dom.style.visibility = 'visible';
13535 onHide : function(){
13536 var ae = this.getActionEl();
13537 ae.dom.style.display = 'none';
13541 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13542 * by an implementing function.
13544 * @param {EventObject} e
13546 onTriggerClick : Roo.emptyFn
13554 * @class Roo.bootstrap.CardUploader
13555 * @extends Roo.bootstrap.Button
13556 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13557 * @cfg {Number} errorTimeout default 3000
13558 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13559 * @cfg {Array} html The button text.
13563 * Create a new CardUploader
13564 * @param {Object} config The config object
13567 Roo.bootstrap.CardUploader = function(config){
13571 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13574 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13582 * When a image is clicked on - and needs to display a slideshow or similar..
13583 * @param {Roo.bootstrap.Card} this
13584 * @param {Object} The image information data
13590 * When a the download link is clicked
13591 * @param {Roo.bootstrap.Card} this
13592 * @param {Object} The image information data contains
13599 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13602 errorTimeout : 3000,
13606 fileCollection : false,
13609 getAutoCreate : function()
13613 cls :'form-group' ,
13618 //cls : 'input-group-addon',
13619 html : this.fieldLabel
13627 value : this.value,
13628 cls : 'd-none form-control'
13633 multiple : 'multiple',
13635 cls : 'd-none roo-card-upload-selector'
13639 cls : 'roo-card-uploader-button-container w-100 mb-2'
13642 cls : 'card-columns roo-card-uploader-container'
13652 getChildContainer : function() /// what children are added to.
13654 return this.containerEl;
13657 getButtonContainer : function() /// what children are added to.
13659 return this.el.select(".roo-card-uploader-button-container").first();
13662 initEvents : function()
13665 Roo.bootstrap.Input.prototype.initEvents.call(this);
13669 xns: Roo.bootstrap,
13672 container_method : 'getButtonContainer' ,
13673 html : this.html, // fix changable?
13676 'click' : function(btn, e) {
13685 this.urlAPI = (window.createObjectURL && window) ||
13686 (window.URL && URL.revokeObjectURL && URL) ||
13687 (window.webkitURL && webkitURL);
13692 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13694 this.selectorEl.on('change', this.onFileSelected, this);
13697 this.images.forEach(function(img) {
13700 this.images = false;
13702 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13708 onClick : function(e)
13710 e.preventDefault();
13712 this.selectorEl.dom.click();
13716 onFileSelected : function(e)
13718 e.preventDefault();
13720 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13724 Roo.each(this.selectorEl.dom.files, function(file){
13725 this.addFile(file);
13734 addFile : function(file)
13737 if(typeof(file) === 'string'){
13738 throw "Add file by name?"; // should not happen
13742 if(!file || !this.urlAPI){
13752 var url = _this.urlAPI.createObjectURL( file);
13755 id : Roo.bootstrap.CardUploader.ID--,
13756 is_uploaded : false,
13760 mimetype : file.type,
13768 * addCard - add an Attachment to the uploader
13769 * @param data - the data about the image to upload
13773 title : "Title of file",
13774 is_uploaded : false,
13775 src : "http://.....",
13776 srcfile : { the File upload object },
13777 mimetype : file.type,
13780 .. any other data...
13786 addCard : function (data)
13788 // hidden input element?
13789 // if the file is not an image...
13790 //then we need to use something other that and header_image
13795 xns : Roo.bootstrap,
13796 xtype : 'CardFooter',
13799 xns : Roo.bootstrap,
13805 xns : Roo.bootstrap,
13807 html : String.format("<small>{0}</small>", data.title),
13808 cls : 'col-10 text-left',
13813 click : function() {
13815 t.fireEvent( "download", t, data );
13821 xns : Roo.bootstrap,
13823 style: 'max-height: 28px; ',
13829 click : function() {
13830 t.removeCard(data.id)
13842 var cn = this.addxtype(
13845 xns : Roo.bootstrap,
13848 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13849 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13850 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13855 initEvents : function() {
13856 Roo.bootstrap.Card.prototype.initEvents.call(this);
13858 this.imgEl = this.el.select('.card-img-top').first();
13860 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13861 this.imgEl.set({ 'pointer' : 'cursor' });
13864 this.getCardFooter().addClass('p-1');
13871 // dont' really need ot update items.
13872 // this.items.push(cn);
13873 this.fileCollection.add(cn);
13875 if (!data.srcfile) {
13876 this.updateInput();
13881 var reader = new FileReader();
13882 reader.addEventListener("load", function() {
13883 data.srcdata = reader.result;
13886 reader.readAsDataURL(data.srcfile);
13891 removeCard : function(id)
13894 var card = this.fileCollection.get(id);
13895 card.data.is_deleted = 1;
13896 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13897 //this.fileCollection.remove(card);
13898 //this.items = this.items.filter(function(e) { return e != card });
13899 // dont' really need ot update items.
13900 card.el.dom.parentNode.removeChild(card.el.dom);
13901 this.updateInput();
13907 this.fileCollection.each(function(card) {
13908 if (card.el.dom && card.el.dom.parentNode) {
13909 card.el.dom.parentNode.removeChild(card.el.dom);
13912 this.fileCollection.clear();
13913 this.updateInput();
13916 updateInput : function()
13919 this.fileCollection.each(function(e) {
13923 this.inputEl().dom.value = JSON.stringify(data);
13933 Roo.bootstrap.CardUploader.ID = -1;/*
13935 * Ext JS Library 1.1.1
13936 * Copyright(c) 2006-2007, Ext JS, LLC.
13938 * Originally Released Under LGPL - original licence link has changed is not relivant.
13941 * <script type="text/javascript">
13946 * @class Roo.data.SortTypes
13948 * Defines the default sorting (casting?) comparison functions used when sorting data.
13950 Roo.data.SortTypes = {
13952 * Default sort that does nothing
13953 * @param {Mixed} s The value being converted
13954 * @return {Mixed} The comparison value
13956 none : function(s){
13961 * The regular expression used to strip tags
13965 stripTagsRE : /<\/?[^>]+>/gi,
13968 * Strips all HTML tags to sort on text only
13969 * @param {Mixed} s The value being converted
13970 * @return {String} The comparison value
13972 asText : function(s){
13973 return String(s).replace(this.stripTagsRE, "");
13977 * Strips all HTML tags to sort on text only - Case insensitive
13978 * @param {Mixed} s The value being converted
13979 * @return {String} The comparison value
13981 asUCText : function(s){
13982 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13986 * Case insensitive string
13987 * @param {Mixed} s The value being converted
13988 * @return {String} The comparison value
13990 asUCString : function(s) {
13991 return String(s).toUpperCase();
13996 * @param {Mixed} s The value being converted
13997 * @return {Number} The comparison value
13999 asDate : function(s) {
14003 if(s instanceof Date){
14004 return s.getTime();
14006 return Date.parse(String(s));
14011 * @param {Mixed} s The value being converted
14012 * @return {Float} The comparison value
14014 asFloat : function(s) {
14015 var val = parseFloat(String(s).replace(/,/g, ""));
14024 * @param {Mixed} s The value being converted
14025 * @return {Number} The comparison value
14027 asInt : function(s) {
14028 var val = parseInt(String(s).replace(/,/g, ""));
14036 * Ext JS Library 1.1.1
14037 * Copyright(c) 2006-2007, Ext JS, LLC.
14039 * Originally Released Under LGPL - original licence link has changed is not relivant.
14042 * <script type="text/javascript">
14046 * @class Roo.data.Record
14047 * Instances of this class encapsulate both record <em>definition</em> information, and record
14048 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14049 * to access Records cached in an {@link Roo.data.Store} object.<br>
14051 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14052 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14055 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14057 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14058 * {@link #create}. The parameters are the same.
14059 * @param {Array} data An associative Array of data values keyed by the field name.
14060 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14061 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14062 * not specified an integer id is generated.
14064 Roo.data.Record = function(data, id){
14065 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14070 * Generate a constructor for a specific record layout.
14071 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14072 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14073 * Each field definition object may contain the following properties: <ul>
14074 * <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,
14075 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14076 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14077 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14078 * is being used, then this is a string containing the javascript expression to reference the data relative to
14079 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14080 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14081 * this may be omitted.</p></li>
14082 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14083 * <ul><li>auto (Default, implies no conversion)</li>
14088 * <li>date</li></ul></p></li>
14089 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14090 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14091 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14092 * by the Reader into an object that will be stored in the Record. It is passed the
14093 * following parameters:<ul>
14094 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14096 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14098 * <br>usage:<br><pre><code>
14099 var TopicRecord = Roo.data.Record.create(
14100 {name: 'title', mapping: 'topic_title'},
14101 {name: 'author', mapping: 'username'},
14102 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14103 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14104 {name: 'lastPoster', mapping: 'user2'},
14105 {name: 'excerpt', mapping: 'post_text'}
14108 var myNewRecord = new TopicRecord({
14109 title: 'Do my job please',
14112 lastPost: new Date(),
14113 lastPoster: 'Animal',
14114 excerpt: 'No way dude!'
14116 myStore.add(myNewRecord);
14121 Roo.data.Record.create = function(o){
14122 var f = function(){
14123 f.superclass.constructor.apply(this, arguments);
14125 Roo.extend(f, Roo.data.Record);
14126 var p = f.prototype;
14127 p.fields = new Roo.util.MixedCollection(false, function(field){
14130 for(var i = 0, len = o.length; i < len; i++){
14131 p.fields.add(new Roo.data.Field(o[i]));
14133 f.getField = function(name){
14134 return p.fields.get(name);
14139 Roo.data.Record.AUTO_ID = 1000;
14140 Roo.data.Record.EDIT = 'edit';
14141 Roo.data.Record.REJECT = 'reject';
14142 Roo.data.Record.COMMIT = 'commit';
14144 Roo.data.Record.prototype = {
14146 * Readonly flag - true if this record has been modified.
14155 join : function(store){
14156 this.store = store;
14160 * Set the named field to the specified value.
14161 * @param {String} name The name of the field to set.
14162 * @param {Object} value The value to set the field to.
14164 set : function(name, value){
14165 if(this.data[name] == value){
14169 if(!this.modified){
14170 this.modified = {};
14172 if(typeof this.modified[name] == 'undefined'){
14173 this.modified[name] = this.data[name];
14175 this.data[name] = value;
14176 if(!this.editing && this.store){
14177 this.store.afterEdit(this);
14182 * Get the value of the named field.
14183 * @param {String} name The name of the field to get the value of.
14184 * @return {Object} The value of the field.
14186 get : function(name){
14187 return this.data[name];
14191 beginEdit : function(){
14192 this.editing = true;
14193 this.modified = {};
14197 cancelEdit : function(){
14198 this.editing = false;
14199 delete this.modified;
14203 endEdit : function(){
14204 this.editing = false;
14205 if(this.dirty && this.store){
14206 this.store.afterEdit(this);
14211 * Usually called by the {@link Roo.data.Store} which owns the Record.
14212 * Rejects all changes made to the Record since either creation, or the last commit operation.
14213 * Modified fields are reverted to their original values.
14215 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14216 * of reject operations.
14218 reject : function(){
14219 var m = this.modified;
14221 if(typeof m[n] != "function"){
14222 this.data[n] = m[n];
14225 this.dirty = false;
14226 delete this.modified;
14227 this.editing = false;
14229 this.store.afterReject(this);
14234 * Usually called by the {@link Roo.data.Store} which owns the Record.
14235 * Commits all changes made to the Record since either creation, or the last commit operation.
14237 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14238 * of commit operations.
14240 commit : function(){
14241 this.dirty = false;
14242 delete this.modified;
14243 this.editing = false;
14245 this.store.afterCommit(this);
14250 hasError : function(){
14251 return this.error != null;
14255 clearError : function(){
14260 * Creates a copy of this record.
14261 * @param {String} id (optional) A new record id if you don't want to use this record's id
14264 copy : function(newId) {
14265 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14269 * Ext JS Library 1.1.1
14270 * Copyright(c) 2006-2007, Ext JS, LLC.
14272 * Originally Released Under LGPL - original licence link has changed is not relivant.
14275 * <script type="text/javascript">
14281 * @class Roo.data.Store
14282 * @extends Roo.util.Observable
14283 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14284 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14286 * 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
14287 * has no knowledge of the format of the data returned by the Proxy.<br>
14289 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14290 * instances from the data object. These records are cached and made available through accessor functions.
14292 * Creates a new Store.
14293 * @param {Object} config A config object containing the objects needed for the Store to access data,
14294 * and read the data into Records.
14296 Roo.data.Store = function(config){
14297 this.data = new Roo.util.MixedCollection(false);
14298 this.data.getKey = function(o){
14301 this.baseParams = {};
14303 this.paramNames = {
14308 "multisort" : "_multisort"
14311 if(config && config.data){
14312 this.inlineData = config.data;
14313 delete config.data;
14316 Roo.apply(this, config);
14318 if(this.reader){ // reader passed
14319 this.reader = Roo.factory(this.reader, Roo.data);
14320 this.reader.xmodule = this.xmodule || false;
14321 if(!this.recordType){
14322 this.recordType = this.reader.recordType;
14324 if(this.reader.onMetaChange){
14325 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14329 if(this.recordType){
14330 this.fields = this.recordType.prototype.fields;
14332 this.modified = [];
14336 * @event datachanged
14337 * Fires when the data cache has changed, and a widget which is using this Store
14338 * as a Record cache should refresh its view.
14339 * @param {Store} this
14341 datachanged : true,
14343 * @event metachange
14344 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14345 * @param {Store} this
14346 * @param {Object} meta The JSON metadata
14351 * Fires when Records have been added to the Store
14352 * @param {Store} this
14353 * @param {Roo.data.Record[]} records The array of Records added
14354 * @param {Number} index The index at which the record(s) were added
14359 * Fires when a Record has been removed from the Store
14360 * @param {Store} this
14361 * @param {Roo.data.Record} record The Record that was removed
14362 * @param {Number} index The index at which the record was removed
14367 * Fires when a Record has been updated
14368 * @param {Store} this
14369 * @param {Roo.data.Record} record The Record that was updated
14370 * @param {String} operation The update operation being performed. Value may be one of:
14372 Roo.data.Record.EDIT
14373 Roo.data.Record.REJECT
14374 Roo.data.Record.COMMIT
14380 * Fires when the data cache has been cleared.
14381 * @param {Store} this
14385 * @event beforeload
14386 * Fires before a request is made for a new data object. If the beforeload handler returns false
14387 * the load action will be canceled.
14388 * @param {Store} this
14389 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14393 * @event beforeloadadd
14394 * Fires after a new set of Records has been loaded.
14395 * @param {Store} this
14396 * @param {Roo.data.Record[]} records The Records that were loaded
14397 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14399 beforeloadadd : true,
14402 * Fires after a new set of Records has been loaded, before they are added to the store.
14403 * @param {Store} this
14404 * @param {Roo.data.Record[]} records The Records that were loaded
14405 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14406 * @params {Object} return from reader
14410 * @event loadexception
14411 * Fires if an exception occurs in the Proxy during loading.
14412 * Called with the signature of the Proxy's "loadexception" event.
14413 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14416 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14417 * @param {Object} load options
14418 * @param {Object} jsonData from your request (normally this contains the Exception)
14420 loadexception : true
14424 this.proxy = Roo.factory(this.proxy, Roo.data);
14425 this.proxy.xmodule = this.xmodule || false;
14426 this.relayEvents(this.proxy, ["loadexception"]);
14428 this.sortToggle = {};
14429 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14431 Roo.data.Store.superclass.constructor.call(this);
14433 if(this.inlineData){
14434 this.loadData(this.inlineData);
14435 delete this.inlineData;
14439 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14441 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14442 * without a remote query - used by combo/forms at present.
14446 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14449 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14452 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14453 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14456 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14457 * on any HTTP request
14460 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14463 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14467 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14468 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14470 remoteSort : false,
14473 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14474 * loaded or when a record is removed. (defaults to false).
14476 pruneModifiedRecords : false,
14479 lastOptions : null,
14482 * Add Records to the Store and fires the add event.
14483 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14485 add : function(records){
14486 records = [].concat(records);
14487 for(var i = 0, len = records.length; i < len; i++){
14488 records[i].join(this);
14490 var index = this.data.length;
14491 this.data.addAll(records);
14492 this.fireEvent("add", this, records, index);
14496 * Remove a Record from the Store and fires the remove event.
14497 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14499 remove : function(record){
14500 var index = this.data.indexOf(record);
14501 this.data.removeAt(index);
14503 if(this.pruneModifiedRecords){
14504 this.modified.remove(record);
14506 this.fireEvent("remove", this, record, index);
14510 * Remove all Records from the Store and fires the clear event.
14512 removeAll : function(){
14514 if(this.pruneModifiedRecords){
14515 this.modified = [];
14517 this.fireEvent("clear", this);
14521 * Inserts Records to the Store at the given index and fires the add event.
14522 * @param {Number} index The start index at which to insert the passed Records.
14523 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14525 insert : function(index, records){
14526 records = [].concat(records);
14527 for(var i = 0, len = records.length; i < len; i++){
14528 this.data.insert(index, records[i]);
14529 records[i].join(this);
14531 this.fireEvent("add", this, records, index);
14535 * Get the index within the cache of the passed Record.
14536 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14537 * @return {Number} The index of the passed Record. Returns -1 if not found.
14539 indexOf : function(record){
14540 return this.data.indexOf(record);
14544 * Get the index within the cache of the Record with the passed id.
14545 * @param {String} id The id of the Record to find.
14546 * @return {Number} The index of the Record. Returns -1 if not found.
14548 indexOfId : function(id){
14549 return this.data.indexOfKey(id);
14553 * Get the Record with the specified id.
14554 * @param {String} id The id of the Record to find.
14555 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14557 getById : function(id){
14558 return this.data.key(id);
14562 * Get the Record at the specified index.
14563 * @param {Number} index The index of the Record to find.
14564 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14566 getAt : function(index){
14567 return this.data.itemAt(index);
14571 * Returns a range of Records between specified indices.
14572 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14573 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14574 * @return {Roo.data.Record[]} An array of Records
14576 getRange : function(start, end){
14577 return this.data.getRange(start, end);
14581 storeOptions : function(o){
14582 o = Roo.apply({}, o);
14585 this.lastOptions = o;
14589 * Loads the Record cache from the configured Proxy using the configured Reader.
14591 * If using remote paging, then the first load call must specify the <em>start</em>
14592 * and <em>limit</em> properties in the options.params property to establish the initial
14593 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14595 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14596 * and this call will return before the new data has been loaded. Perform any post-processing
14597 * in a callback function, or in a "load" event handler.</strong>
14599 * @param {Object} options An object containing properties which control loading options:<ul>
14600 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14601 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14602 * passed the following arguments:<ul>
14603 * <li>r : Roo.data.Record[]</li>
14604 * <li>options: Options object from the load call</li>
14605 * <li>success: Boolean success indicator</li></ul></li>
14606 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14607 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14610 load : function(options){
14611 options = options || {};
14612 if(this.fireEvent("beforeload", this, options) !== false){
14613 this.storeOptions(options);
14614 var p = Roo.apply(options.params || {}, this.baseParams);
14615 // if meta was not loaded from remote source.. try requesting it.
14616 if (!this.reader.metaFromRemote) {
14617 p._requestMeta = 1;
14619 if(this.sortInfo && this.remoteSort){
14620 var pn = this.paramNames;
14621 p[pn["sort"]] = this.sortInfo.field;
14622 p[pn["dir"]] = this.sortInfo.direction;
14624 if (this.multiSort) {
14625 var pn = this.paramNames;
14626 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14629 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14634 * Reloads the Record cache from the configured Proxy using the configured Reader and
14635 * the options from the last load operation performed.
14636 * @param {Object} options (optional) An object containing properties which may override the options
14637 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14638 * the most recently used options are reused).
14640 reload : function(options){
14641 this.load(Roo.applyIf(options||{}, this.lastOptions));
14645 // Called as a callback by the Reader during a load operation.
14646 loadRecords : function(o, options, success){
14647 if(!o || success === false){
14648 if(success !== false){
14649 this.fireEvent("load", this, [], options, o);
14651 if(options.callback){
14652 options.callback.call(options.scope || this, [], options, false);
14656 // if data returned failure - throw an exception.
14657 if (o.success === false) {
14658 // show a message if no listener is registered.
14659 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14660 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14662 // loadmask wil be hooked into this..
14663 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14666 var r = o.records, t = o.totalRecords || r.length;
14668 this.fireEvent("beforeloadadd", this, r, options, o);
14670 if(!options || options.add !== true){
14671 if(this.pruneModifiedRecords){
14672 this.modified = [];
14674 for(var i = 0, len = r.length; i < len; i++){
14678 this.data = this.snapshot;
14679 delete this.snapshot;
14682 this.data.addAll(r);
14683 this.totalLength = t;
14685 this.fireEvent("datachanged", this);
14687 this.totalLength = Math.max(t, this.data.length+r.length);
14691 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14693 var e = new Roo.data.Record({});
14695 e.set(this.parent.displayField, this.parent.emptyTitle);
14696 e.set(this.parent.valueField, '');
14701 this.fireEvent("load", this, r, options, o);
14702 if(options.callback){
14703 options.callback.call(options.scope || this, r, options, true);
14709 * Loads data from a passed data block. A Reader which understands the format of the data
14710 * must have been configured in the constructor.
14711 * @param {Object} data The data block from which to read the Records. The format of the data expected
14712 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14713 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14715 loadData : function(o, append){
14716 var r = this.reader.readRecords(o);
14717 this.loadRecords(r, {add: append}, true);
14721 * using 'cn' the nested child reader read the child array into it's child stores.
14722 * @param {Object} rec The record with a 'children array
14724 loadDataFromChildren : function(rec)
14726 this.loadData(this.reader.toLoadData(rec));
14731 * Gets the number of cached records.
14733 * <em>If using paging, this may not be the total size of the dataset. If the data object
14734 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14735 * the data set size</em>
14737 getCount : function(){
14738 return this.data.length || 0;
14742 * Gets the total number of records in the dataset as returned by the server.
14744 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14745 * the dataset size</em>
14747 getTotalCount : function(){
14748 return this.totalLength || 0;
14752 * Returns the sort state of the Store as an object with two properties:
14754 field {String} The name of the field by which the Records are sorted
14755 direction {String} The sort order, "ASC" or "DESC"
14758 getSortState : function(){
14759 return this.sortInfo;
14763 applySort : function(){
14764 if(this.sortInfo && !this.remoteSort){
14765 var s = this.sortInfo, f = s.field;
14766 var st = this.fields.get(f).sortType;
14767 var fn = function(r1, r2){
14768 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14769 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14771 this.data.sort(s.direction, fn);
14772 if(this.snapshot && this.snapshot != this.data){
14773 this.snapshot.sort(s.direction, fn);
14779 * Sets the default sort column and order to be used by the next load operation.
14780 * @param {String} fieldName The name of the field to sort by.
14781 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14783 setDefaultSort : function(field, dir){
14784 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14788 * Sort the Records.
14789 * If remote sorting is used, the sort is performed on the server, and the cache is
14790 * reloaded. If local sorting is used, the cache is sorted internally.
14791 * @param {String} fieldName The name of the field to sort by.
14792 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14794 sort : function(fieldName, dir){
14795 var f = this.fields.get(fieldName);
14797 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14799 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14800 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14805 this.sortToggle[f.name] = dir;
14806 this.sortInfo = {field: f.name, direction: dir};
14807 if(!this.remoteSort){
14809 this.fireEvent("datachanged", this);
14811 this.load(this.lastOptions);
14816 * Calls the specified function for each of the Records in the cache.
14817 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14818 * Returning <em>false</em> aborts and exits the iteration.
14819 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14821 each : function(fn, scope){
14822 this.data.each(fn, scope);
14826 * Gets all records modified since the last commit. Modified records are persisted across load operations
14827 * (e.g., during paging).
14828 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14830 getModifiedRecords : function(){
14831 return this.modified;
14835 createFilterFn : function(property, value, anyMatch){
14836 if(!value.exec){ // not a regex
14837 value = String(value);
14838 if(value.length == 0){
14841 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14843 return function(r){
14844 return value.test(r.data[property]);
14849 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14850 * @param {String} property A field on your records
14851 * @param {Number} start The record index to start at (defaults to 0)
14852 * @param {Number} end The last record index to include (defaults to length - 1)
14853 * @return {Number} The sum
14855 sum : function(property, start, end){
14856 var rs = this.data.items, v = 0;
14857 start = start || 0;
14858 end = (end || end === 0) ? end : rs.length-1;
14860 for(var i = start; i <= end; i++){
14861 v += (rs[i].data[property] || 0);
14867 * Filter the records by a specified property.
14868 * @param {String} field A field on your records
14869 * @param {String/RegExp} value Either a string that the field
14870 * should start with or a RegExp to test against the field
14871 * @param {Boolean} anyMatch True to match any part not just the beginning
14873 filter : function(property, value, anyMatch){
14874 var fn = this.createFilterFn(property, value, anyMatch);
14875 return fn ? this.filterBy(fn) : this.clearFilter();
14879 * Filter by a function. The specified function will be called with each
14880 * record in this data source. If the function returns true the record is included,
14881 * otherwise it is filtered.
14882 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14883 * @param {Object} scope (optional) The scope of the function (defaults to this)
14885 filterBy : function(fn, scope){
14886 this.snapshot = this.snapshot || this.data;
14887 this.data = this.queryBy(fn, scope||this);
14888 this.fireEvent("datachanged", this);
14892 * Query the records by a specified property.
14893 * @param {String} field A field on your records
14894 * @param {String/RegExp} value Either a string that the field
14895 * should start with or a RegExp to test against the field
14896 * @param {Boolean} anyMatch True to match any part not just the beginning
14897 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14899 query : function(property, value, anyMatch){
14900 var fn = this.createFilterFn(property, value, anyMatch);
14901 return fn ? this.queryBy(fn) : this.data.clone();
14905 * Query by a function. The specified function will be called with each
14906 * record in this data source. If the function returns true the record is included
14908 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14909 * @param {Object} scope (optional) The scope of the function (defaults to this)
14910 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14912 queryBy : function(fn, scope){
14913 var data = this.snapshot || this.data;
14914 return data.filterBy(fn, scope||this);
14918 * Collects unique values for a particular dataIndex from this store.
14919 * @param {String} dataIndex The property to collect
14920 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14921 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14922 * @return {Array} An array of the unique values
14924 collect : function(dataIndex, allowNull, bypassFilter){
14925 var d = (bypassFilter === true && this.snapshot) ?
14926 this.snapshot.items : this.data.items;
14927 var v, sv, r = [], l = {};
14928 for(var i = 0, len = d.length; i < len; i++){
14929 v = d[i].data[dataIndex];
14931 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14940 * Revert to a view of the Record cache with no filtering applied.
14941 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14943 clearFilter : function(suppressEvent){
14944 if(this.snapshot && this.snapshot != this.data){
14945 this.data = this.snapshot;
14946 delete this.snapshot;
14947 if(suppressEvent !== true){
14948 this.fireEvent("datachanged", this);
14954 afterEdit : function(record){
14955 if(this.modified.indexOf(record) == -1){
14956 this.modified.push(record);
14958 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14962 afterReject : function(record){
14963 this.modified.remove(record);
14964 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14968 afterCommit : function(record){
14969 this.modified.remove(record);
14970 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14974 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14975 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14977 commitChanges : function(){
14978 var m = this.modified.slice(0);
14979 this.modified = [];
14980 for(var i = 0, len = m.length; i < len; i++){
14986 * Cancel outstanding changes on all changed records.
14988 rejectChanges : function(){
14989 var m = this.modified.slice(0);
14990 this.modified = [];
14991 for(var i = 0, len = m.length; i < len; i++){
14996 onMetaChange : function(meta, rtype, o){
14997 this.recordType = rtype;
14998 this.fields = rtype.prototype.fields;
14999 delete this.snapshot;
15000 this.sortInfo = meta.sortInfo || this.sortInfo;
15001 this.modified = [];
15002 this.fireEvent('metachange', this, this.reader.meta);
15005 moveIndex : function(data, type)
15007 var index = this.indexOf(data);
15009 var newIndex = index + type;
15013 this.insert(newIndex, data);
15018 * Ext JS Library 1.1.1
15019 * Copyright(c) 2006-2007, Ext JS, LLC.
15021 * Originally Released Under LGPL - original licence link has changed is not relivant.
15024 * <script type="text/javascript">
15028 * @class Roo.data.SimpleStore
15029 * @extends Roo.data.Store
15030 * Small helper class to make creating Stores from Array data easier.
15031 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15032 * @cfg {Array} fields An array of field definition objects, or field name strings.
15033 * @cfg {Object} an existing reader (eg. copied from another store)
15034 * @cfg {Array} data The multi-dimensional array of data
15036 * @param {Object} config
15038 Roo.data.SimpleStore = function(config)
15040 Roo.data.SimpleStore.superclass.constructor.call(this, {
15042 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15045 Roo.data.Record.create(config.fields)
15047 proxy : new Roo.data.MemoryProxy(config.data)
15051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15053 * Ext JS Library 1.1.1
15054 * Copyright(c) 2006-2007, Ext JS, LLC.
15056 * Originally Released Under LGPL - original licence link has changed is not relivant.
15059 * <script type="text/javascript">
15064 * @extends Roo.data.Store
15065 * @class Roo.data.JsonStore
15066 * Small helper class to make creating Stores for JSON data easier. <br/>
15068 var store = new Roo.data.JsonStore({
15069 url: 'get-images.php',
15071 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15074 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15075 * JsonReader and HttpProxy (unless inline data is provided).</b>
15076 * @cfg {Array} fields An array of field definition objects, or field name strings.
15078 * @param {Object} config
15080 Roo.data.JsonStore = function(c){
15081 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15082 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15083 reader: new Roo.data.JsonReader(c, c.fields)
15086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15088 * Ext JS Library 1.1.1
15089 * Copyright(c) 2006-2007, Ext JS, LLC.
15091 * Originally Released Under LGPL - original licence link has changed is not relivant.
15094 * <script type="text/javascript">
15098 Roo.data.Field = function(config){
15099 if(typeof config == "string"){
15100 config = {name: config};
15102 Roo.apply(this, config);
15105 this.type = "auto";
15108 var st = Roo.data.SortTypes;
15109 // named sortTypes are supported, here we look them up
15110 if(typeof this.sortType == "string"){
15111 this.sortType = st[this.sortType];
15114 // set default sortType for strings and dates
15115 if(!this.sortType){
15118 this.sortType = st.asUCString;
15121 this.sortType = st.asDate;
15124 this.sortType = st.none;
15129 var stripRe = /[\$,%]/g;
15131 // prebuilt conversion function for this field, instead of
15132 // switching every time we're reading a value
15134 var cv, dateFormat = this.dateFormat;
15139 cv = function(v){ return v; };
15142 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15146 return v !== undefined && v !== null && v !== '' ?
15147 parseInt(String(v).replace(stripRe, ""), 10) : '';
15152 return v !== undefined && v !== null && v !== '' ?
15153 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15158 cv = function(v){ return v === true || v === "true" || v == 1; };
15165 if(v instanceof Date){
15169 if(dateFormat == "timestamp"){
15170 return new Date(v*1000);
15172 return Date.parseDate(v, dateFormat);
15174 var parsed = Date.parse(v);
15175 return parsed ? new Date(parsed) : null;
15184 Roo.data.Field.prototype = {
15192 * Ext JS Library 1.1.1
15193 * Copyright(c) 2006-2007, Ext JS, LLC.
15195 * Originally Released Under LGPL - original licence link has changed is not relivant.
15198 * <script type="text/javascript">
15201 // Base class for reading structured data from a data source. This class is intended to be
15202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15205 * @class Roo.data.DataReader
15206 * Base class for reading structured data from a data source. This class is intended to be
15207 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15210 Roo.data.DataReader = function(meta, recordType){
15214 this.recordType = recordType instanceof Array ?
15215 Roo.data.Record.create(recordType) : recordType;
15218 Roo.data.DataReader.prototype = {
15221 readerType : 'Data',
15223 * Create an empty record
15224 * @param {Object} data (optional) - overlay some values
15225 * @return {Roo.data.Record} record created.
15227 newRow : function(d) {
15229 this.recordType.prototype.fields.each(function(c) {
15231 case 'int' : da[c.name] = 0; break;
15232 case 'date' : da[c.name] = new Date(); break;
15233 case 'float' : da[c.name] = 0.0; break;
15234 case 'boolean' : da[c.name] = false; break;
15235 default : da[c.name] = ""; break;
15239 return new this.recordType(Roo.apply(da, d));
15245 * Ext JS Library 1.1.1
15246 * Copyright(c) 2006-2007, Ext JS, LLC.
15248 * Originally Released Under LGPL - original licence link has changed is not relivant.
15251 * <script type="text/javascript">
15255 * @class Roo.data.DataProxy
15256 * @extends Roo.data.Observable
15257 * This class is an abstract base class for implementations which provide retrieval of
15258 * unformatted data objects.<br>
15260 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15261 * (of the appropriate type which knows how to parse the data object) to provide a block of
15262 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15264 * Custom implementations must implement the load method as described in
15265 * {@link Roo.data.HttpProxy#load}.
15267 Roo.data.DataProxy = function(){
15270 * @event beforeload
15271 * Fires before a network request is made to retrieve a data object.
15272 * @param {Object} This DataProxy object.
15273 * @param {Object} params The params parameter to the load function.
15278 * Fires before the load method's callback is called.
15279 * @param {Object} This DataProxy object.
15280 * @param {Object} o The data object.
15281 * @param {Object} arg The callback argument object passed to the load function.
15285 * @event loadexception
15286 * Fires if an Exception occurs during data retrieval.
15287 * @param {Object} This DataProxy object.
15288 * @param {Object} o The data object.
15289 * @param {Object} arg The callback argument object passed to the load function.
15290 * @param {Object} e The Exception.
15292 loadexception : true
15294 Roo.data.DataProxy.superclass.constructor.call(this);
15297 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15300 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15304 * Ext JS Library 1.1.1
15305 * Copyright(c) 2006-2007, Ext JS, LLC.
15307 * Originally Released Under LGPL - original licence link has changed is not relivant.
15310 * <script type="text/javascript">
15313 * @class Roo.data.MemoryProxy
15314 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15315 * to the Reader when its load method is called.
15317 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15319 Roo.data.MemoryProxy = function(data){
15323 Roo.data.MemoryProxy.superclass.constructor.call(this);
15327 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15330 * Load data from the requested source (in this case an in-memory
15331 * data object passed to the constructor), read the data object into
15332 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15333 * process that block using the passed callback.
15334 * @param {Object} params This parameter is not used by the MemoryProxy class.
15335 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15336 * object into a block of Roo.data.Records.
15337 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15338 * The function must be passed <ul>
15339 * <li>The Record block object</li>
15340 * <li>The "arg" argument from the load function</li>
15341 * <li>A boolean success indicator</li>
15343 * @param {Object} scope The scope in which to call the callback
15344 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15346 load : function(params, reader, callback, scope, arg){
15347 params = params || {};
15350 result = reader.readRecords(params.data ? params.data :this.data);
15352 this.fireEvent("loadexception", this, arg, null, e);
15353 callback.call(scope, null, arg, false);
15356 callback.call(scope, result, arg, true);
15360 update : function(params, records){
15365 * Ext JS Library 1.1.1
15366 * Copyright(c) 2006-2007, Ext JS, LLC.
15368 * Originally Released Under LGPL - original licence link has changed is not relivant.
15371 * <script type="text/javascript">
15374 * @class Roo.data.HttpProxy
15375 * @extends Roo.data.DataProxy
15376 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15377 * configured to reference a certain URL.<br><br>
15379 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15380 * from which the running page was served.<br><br>
15382 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15384 * Be aware that to enable the browser to parse an XML document, the server must set
15385 * the Content-Type header in the HTTP response to "text/xml".
15387 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15388 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15389 * will be used to make the request.
15391 Roo.data.HttpProxy = function(conn){
15392 Roo.data.HttpProxy.superclass.constructor.call(this);
15393 // is conn a conn config or a real conn?
15395 this.useAjax = !conn || !conn.events;
15399 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15400 // thse are take from connection...
15403 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15406 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15407 * extra parameters to each request made by this object. (defaults to undefined)
15410 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15411 * to each request made by this object. (defaults to undefined)
15414 * @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)
15417 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15420 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15426 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15430 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15431 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15432 * a finer-grained basis than the DataProxy events.
15434 getConnection : function(){
15435 return this.useAjax ? Roo.Ajax : this.conn;
15439 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15440 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15441 * process that block using the passed callback.
15442 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15443 * for the request to the remote server.
15444 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15445 * object into a block of Roo.data.Records.
15446 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15447 * The function must be passed <ul>
15448 * <li>The Record block object</li>
15449 * <li>The "arg" argument from the load function</li>
15450 * <li>A boolean success indicator</li>
15452 * @param {Object} scope The scope in which to call the callback
15453 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15455 load : function(params, reader, callback, scope, arg){
15456 if(this.fireEvent("beforeload", this, params) !== false){
15458 params : params || {},
15460 callback : callback,
15465 callback : this.loadResponse,
15469 Roo.applyIf(o, this.conn);
15470 if(this.activeRequest){
15471 Roo.Ajax.abort(this.activeRequest);
15473 this.activeRequest = Roo.Ajax.request(o);
15475 this.conn.request(o);
15478 callback.call(scope||this, null, arg, false);
15483 loadResponse : function(o, success, response){
15484 delete this.activeRequest;
15486 this.fireEvent("loadexception", this, o, response);
15487 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15492 result = o.reader.read(response);
15494 this.fireEvent("loadexception", this, o, response, e);
15495 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15499 this.fireEvent("load", this, o, o.request.arg);
15500 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15504 update : function(dataSet){
15509 updateResponse : function(dataSet){
15514 * Ext JS Library 1.1.1
15515 * Copyright(c) 2006-2007, Ext JS, LLC.
15517 * Originally Released Under LGPL - original licence link has changed is not relivant.
15520 * <script type="text/javascript">
15524 * @class Roo.data.ScriptTagProxy
15525 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15526 * other than the originating domain of the running page.<br><br>
15528 * <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
15529 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15531 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15532 * source code that is used as the source inside a <script> tag.<br><br>
15534 * In order for the browser to process the returned data, the server must wrap the data object
15535 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15536 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15537 * depending on whether the callback name was passed:
15540 boolean scriptTag = false;
15541 String cb = request.getParameter("callback");
15544 response.setContentType("text/javascript");
15546 response.setContentType("application/x-json");
15548 Writer out = response.getWriter();
15550 out.write(cb + "(");
15552 out.print(dataBlock.toJsonString());
15559 * @param {Object} config A configuration object.
15561 Roo.data.ScriptTagProxy = function(config){
15562 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15563 Roo.apply(this, config);
15564 this.head = document.getElementsByTagName("head")[0];
15567 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15569 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15571 * @cfg {String} url The URL from which to request the data object.
15574 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15578 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15579 * the server the name of the callback function set up by the load call to process the returned data object.
15580 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15581 * javascript output which calls this named function passing the data object as its only parameter.
15583 callbackParam : "callback",
15585 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15586 * name to the request.
15591 * Load data from the configured URL, read the data object into
15592 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15593 * process that block using the passed callback.
15594 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15595 * for the request to the remote server.
15596 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15597 * object into a block of Roo.data.Records.
15598 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15599 * The function must be passed <ul>
15600 * <li>The Record block object</li>
15601 * <li>The "arg" argument from the load function</li>
15602 * <li>A boolean success indicator</li>
15604 * @param {Object} scope The scope in which to call the callback
15605 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15607 load : function(params, reader, callback, scope, arg){
15608 if(this.fireEvent("beforeload", this, params) !== false){
15610 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15612 var url = this.url;
15613 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15615 url += "&_dc=" + (new Date().getTime());
15617 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15620 cb : "stcCallback"+transId,
15621 scriptId : "stcScript"+transId,
15625 callback : callback,
15631 window[trans.cb] = function(o){
15632 conn.handleResponse(o, trans);
15635 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15637 if(this.autoAbort !== false){
15641 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15643 var script = document.createElement("script");
15644 script.setAttribute("src", url);
15645 script.setAttribute("type", "text/javascript");
15646 script.setAttribute("id", trans.scriptId);
15647 this.head.appendChild(script);
15649 this.trans = trans;
15651 callback.call(scope||this, null, arg, false);
15656 isLoading : function(){
15657 return this.trans ? true : false;
15661 * Abort the current server request.
15663 abort : function(){
15664 if(this.isLoading()){
15665 this.destroyTrans(this.trans);
15670 destroyTrans : function(trans, isLoaded){
15671 this.head.removeChild(document.getElementById(trans.scriptId));
15672 clearTimeout(trans.timeoutId);
15674 window[trans.cb] = undefined;
15676 delete window[trans.cb];
15679 // if hasn't been loaded, wait for load to remove it to prevent script error
15680 window[trans.cb] = function(){
15681 window[trans.cb] = undefined;
15683 delete window[trans.cb];
15690 handleResponse : function(o, trans){
15691 this.trans = false;
15692 this.destroyTrans(trans, true);
15695 result = trans.reader.readRecords(o);
15697 this.fireEvent("loadexception", this, o, trans.arg, e);
15698 trans.callback.call(trans.scope||window, null, trans.arg, false);
15701 this.fireEvent("load", this, o, trans.arg);
15702 trans.callback.call(trans.scope||window, result, trans.arg, true);
15706 handleFailure : function(trans){
15707 this.trans = false;
15708 this.destroyTrans(trans, false);
15709 this.fireEvent("loadexception", this, null, trans.arg);
15710 trans.callback.call(trans.scope||window, null, trans.arg, false);
15714 * Ext JS Library 1.1.1
15715 * Copyright(c) 2006-2007, Ext JS, LLC.
15717 * Originally Released Under LGPL - original licence link has changed is not relivant.
15720 * <script type="text/javascript">
15724 * @class Roo.data.JsonReader
15725 * @extends Roo.data.DataReader
15726 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15727 * based on mappings in a provided Roo.data.Record constructor.
15729 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15730 * in the reply previously.
15735 var RecordDef = Roo.data.Record.create([
15736 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15737 {name: 'occupation'} // This field will use "occupation" as the mapping.
15739 var myReader = new Roo.data.JsonReader({
15740 totalProperty: "results", // The property which contains the total dataset size (optional)
15741 root: "rows", // The property which contains an Array of row objects
15742 id: "id" // The property within each row object that provides an ID for the record (optional)
15746 * This would consume a JSON file like this:
15748 { 'results': 2, 'rows': [
15749 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15750 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15753 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15754 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15755 * paged from the remote server.
15756 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15757 * @cfg {String} root name of the property which contains the Array of row objects.
15758 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15759 * @cfg {Array} fields Array of field definition objects
15761 * Create a new JsonReader
15762 * @param {Object} meta Metadata configuration options
15763 * @param {Object} recordType Either an Array of field definition objects,
15764 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15766 Roo.data.JsonReader = function(meta, recordType){
15769 // set some defaults:
15770 Roo.applyIf(meta, {
15771 totalProperty: 'total',
15772 successProperty : 'success',
15777 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15779 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15781 readerType : 'Json',
15784 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15785 * Used by Store query builder to append _requestMeta to params.
15788 metaFromRemote : false,
15790 * This method is only used by a DataProxy which has retrieved data from a remote server.
15791 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15792 * @return {Object} data A data block which is used by an Roo.data.Store object as
15793 * a cache of Roo.data.Records.
15795 read : function(response){
15796 var json = response.responseText;
15798 var o = /* eval:var:o */ eval("("+json+")");
15800 throw {message: "JsonReader.read: Json object not found"};
15806 this.metaFromRemote = true;
15807 this.meta = o.metaData;
15808 this.recordType = Roo.data.Record.create(o.metaData.fields);
15809 this.onMetaChange(this.meta, this.recordType, o);
15811 return this.readRecords(o);
15814 // private function a store will implement
15815 onMetaChange : function(meta, recordType, o){
15822 simpleAccess: function(obj, subsc) {
15829 getJsonAccessor: function(){
15831 return function(expr) {
15833 return(re.test(expr))
15834 ? new Function("obj", "return obj." + expr)
15839 return Roo.emptyFn;
15844 * Create a data block containing Roo.data.Records from an XML document.
15845 * @param {Object} o An object which contains an Array of row objects in the property specified
15846 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15847 * which contains the total size of the dataset.
15848 * @return {Object} data A data block which is used by an Roo.data.Store object as
15849 * a cache of Roo.data.Records.
15851 readRecords : function(o){
15853 * After any data loads, the raw JSON data is available for further custom processing.
15857 var s = this.meta, Record = this.recordType,
15858 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15860 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15862 if(s.totalProperty) {
15863 this.getTotal = this.getJsonAccessor(s.totalProperty);
15865 if(s.successProperty) {
15866 this.getSuccess = this.getJsonAccessor(s.successProperty);
15868 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15870 var g = this.getJsonAccessor(s.id);
15871 this.getId = function(rec) {
15873 return (r === undefined || r === "") ? null : r;
15876 this.getId = function(){return null;};
15879 for(var jj = 0; jj < fl; jj++){
15881 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15882 this.ef[jj] = this.getJsonAccessor(map);
15886 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15887 if(s.totalProperty){
15888 var vt = parseInt(this.getTotal(o), 10);
15893 if(s.successProperty){
15894 var vs = this.getSuccess(o);
15895 if(vs === false || vs === 'false'){
15900 for(var i = 0; i < c; i++){
15903 var id = this.getId(n);
15904 for(var j = 0; j < fl; j++){
15906 var v = this.ef[j](n);
15908 Roo.log('missing convert for ' + f.name);
15912 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15914 var record = new Record(values, id);
15916 records[i] = record;
15922 totalRecords : totalRecords
15925 // used when loading children.. @see loadDataFromChildren
15926 toLoadData: function(rec)
15928 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15929 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15930 return { data : data, total : data.length };
15935 * Ext JS Library 1.1.1
15936 * Copyright(c) 2006-2007, Ext JS, LLC.
15938 * Originally Released Under LGPL - original licence link has changed is not relivant.
15941 * <script type="text/javascript">
15945 * @class Roo.data.ArrayReader
15946 * @extends Roo.data.DataReader
15947 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15948 * Each element of that Array represents a row of data fields. The
15949 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15950 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15954 var RecordDef = Roo.data.Record.create([
15955 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15956 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15958 var myReader = new Roo.data.ArrayReader({
15959 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15963 * This would consume an Array like this:
15965 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15969 * Create a new JsonReader
15970 * @param {Object} meta Metadata configuration options.
15971 * @param {Object|Array} recordType Either an Array of field definition objects
15973 * @cfg {Array} fields Array of field definition objects
15974 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15975 * as specified to {@link Roo.data.Record#create},
15976 * or an {@link Roo.data.Record} object
15979 * created using {@link Roo.data.Record#create}.
15981 Roo.data.ArrayReader = function(meta, recordType)
15983 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15986 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15989 * Create a data block containing Roo.data.Records from an XML document.
15990 * @param {Object} o An Array of row objects which represents the dataset.
15991 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15992 * a cache of Roo.data.Records.
15994 readRecords : function(o)
15996 var sid = this.meta ? this.meta.id : null;
15997 var recordType = this.recordType, fields = recordType.prototype.fields;
16000 for(var i = 0; i < root.length; i++){
16003 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16004 for(var j = 0, jlen = fields.length; j < jlen; j++){
16005 var f = fields.items[j];
16006 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16007 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16009 values[f.name] = v;
16011 var record = new recordType(values, id);
16013 records[records.length] = record;
16017 totalRecords : records.length
16020 // used when loading children.. @see loadDataFromChildren
16021 toLoadData: function(rec)
16023 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16024 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16035 * @class Roo.bootstrap.ComboBox
16036 * @extends Roo.bootstrap.TriggerField
16037 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16038 * @cfg {Boolean} append (true|false) default false
16039 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16040 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16041 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16042 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16043 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16044 * @cfg {Boolean} animate default true
16045 * @cfg {Boolean} emptyResultText only for touch device
16046 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16047 * @cfg {String} emptyTitle default ''
16048 * @cfg {Number} width fixed with? experimental
16050 * Create a new ComboBox.
16051 * @param {Object} config Configuration options
16053 Roo.bootstrap.ComboBox = function(config){
16054 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16058 * Fires when the dropdown list is expanded
16059 * @param {Roo.bootstrap.ComboBox} combo This combo box
16064 * Fires when the dropdown list is collapsed
16065 * @param {Roo.bootstrap.ComboBox} combo This combo box
16069 * @event beforeselect
16070 * Fires before a list item is selected. Return false to cancel the selection.
16071 * @param {Roo.bootstrap.ComboBox} combo This combo box
16072 * @param {Roo.data.Record} record The data record returned from the underlying store
16073 * @param {Number} index The index of the selected item in the dropdown list
16075 'beforeselect' : true,
16078 * Fires when a list item is selected
16079 * @param {Roo.bootstrap.ComboBox} combo This combo box
16080 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16081 * @param {Number} index The index of the selected item in the dropdown list
16085 * @event beforequery
16086 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16087 * The event object passed has these properties:
16088 * @param {Roo.bootstrap.ComboBox} combo This combo box
16089 * @param {String} query The query
16090 * @param {Boolean} forceAll true to force "all" query
16091 * @param {Boolean} cancel true to cancel the query
16092 * @param {Object} e The query event object
16094 'beforequery': true,
16097 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16098 * @param {Roo.bootstrap.ComboBox} combo This combo box
16103 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16104 * @param {Roo.bootstrap.ComboBox} combo This combo box
16105 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16110 * Fires when the remove value from the combobox array
16111 * @param {Roo.bootstrap.ComboBox} combo This combo box
16115 * @event afterremove
16116 * Fires when the remove value from the combobox array
16117 * @param {Roo.bootstrap.ComboBox} combo This combo box
16119 'afterremove' : true,
16121 * @event specialfilter
16122 * Fires when specialfilter
16123 * @param {Roo.bootstrap.ComboBox} combo This combo box
16125 'specialfilter' : true,
16128 * Fires when tick the element
16129 * @param {Roo.bootstrap.ComboBox} combo This combo box
16133 * @event touchviewdisplay
16134 * Fires when touch view require special display (default is using displayField)
16135 * @param {Roo.bootstrap.ComboBox} combo This combo box
16136 * @param {Object} cfg set html .
16138 'touchviewdisplay' : true
16143 this.tickItems = [];
16145 this.selectedIndex = -1;
16146 if(this.mode == 'local'){
16147 if(config.queryDelay === undefined){
16148 this.queryDelay = 10;
16150 if(config.minChars === undefined){
16156 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16159 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16160 * rendering into an Roo.Editor, defaults to false)
16163 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16164 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16167 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16170 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16171 * the dropdown list (defaults to undefined, with no header element)
16175 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16179 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16181 listWidth: undefined,
16183 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16184 * mode = 'remote' or 'text' if mode = 'local')
16186 displayField: undefined,
16189 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16190 * mode = 'remote' or 'value' if mode = 'local').
16191 * Note: use of a valueField requires the user make a selection
16192 * in order for a value to be mapped.
16194 valueField: undefined,
16196 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16201 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16202 * field's data value (defaults to the underlying DOM element's name)
16204 hiddenName: undefined,
16206 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16210 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16212 selectedClass: 'active',
16215 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16219 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16220 * anchor positions (defaults to 'tl-bl')
16222 listAlign: 'tl-bl?',
16224 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16228 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16229 * query specified by the allQuery config option (defaults to 'query')
16231 triggerAction: 'query',
16233 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16234 * (defaults to 4, does not apply if editable = false)
16238 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16239 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16243 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16244 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16248 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16249 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16253 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16254 * when editable = true (defaults to false)
16256 selectOnFocus:false,
16258 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16260 queryParam: 'query',
16262 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16263 * when mode = 'remote' (defaults to 'Loading...')
16265 loadingText: 'Loading...',
16267 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16271 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16275 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16276 * traditional select (defaults to true)
16280 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16284 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16288 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16289 * listWidth has a higher value)
16293 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16294 * allow the user to set arbitrary text into the field (defaults to false)
16296 forceSelection:false,
16298 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16299 * if typeAhead = true (defaults to 250)
16301 typeAheadDelay : 250,
16303 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16304 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16306 valueNotFoundText : undefined,
16308 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16310 blockFocus : false,
16313 * @cfg {Boolean} disableClear Disable showing of clear button.
16315 disableClear : false,
16317 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16319 alwaysQuery : false,
16322 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16327 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16329 invalidClass : "has-warning",
16332 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16334 validClass : "has-success",
16337 * @cfg {Boolean} specialFilter (true|false) special filter default false
16339 specialFilter : false,
16342 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16344 mobileTouchView : true,
16347 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16349 useNativeIOS : false,
16352 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16354 mobile_restrict_height : false,
16356 ios_options : false,
16368 btnPosition : 'right',
16369 triggerList : true,
16370 showToggleBtn : true,
16372 emptyResultText: 'Empty',
16373 triggerText : 'Select',
16377 // element that contains real text value.. (when hidden is used..)
16379 getAutoCreate : function()
16384 * Render classic select for iso
16387 if(Roo.isIOS && this.useNativeIOS){
16388 cfg = this.getAutoCreateNativeIOS();
16396 if(Roo.isTouch && this.mobileTouchView){
16397 cfg = this.getAutoCreateTouchView();
16404 if(!this.tickable){
16405 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16410 * ComboBox with tickable selections
16413 var align = this.labelAlign || this.parentLabelAlign();
16416 cls : 'form-group roo-combobox-tickable' //input-group
16419 var btn_text_select = '';
16420 var btn_text_done = '';
16421 var btn_text_cancel = '';
16423 if (this.btn_text_show) {
16424 btn_text_select = 'Select';
16425 btn_text_done = 'Done';
16426 btn_text_cancel = 'Cancel';
16431 cls : 'tickable-buttons',
16436 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16437 //html : this.triggerText
16438 html: btn_text_select
16444 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16446 html: btn_text_done
16452 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16454 html: btn_text_cancel
16460 buttons.cn.unshift({
16462 cls: 'roo-select2-search-field-input'
16468 Roo.each(buttons.cn, function(c){
16470 c.cls += ' btn-' + _this.size;
16473 if (_this.disabled) {
16480 style : 'display: contents',
16485 cls: 'form-hidden-field'
16489 cls: 'roo-select2-choices',
16493 cls: 'roo-select2-search-field',
16504 cls: 'roo-select2-container input-group roo-select2-container-multi',
16510 // cls: 'typeahead typeahead-long dropdown-menu',
16511 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16516 if(this.hasFeedback && !this.allowBlank){
16520 cls: 'glyphicon form-control-feedback'
16523 combobox.cn.push(feedback);
16530 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16531 tooltip : 'This field is required'
16533 if (Roo.bootstrap.version == 4) {
16536 style : 'display:none'
16539 if (align ==='left' && this.fieldLabel.length) {
16541 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16548 cls : 'control-label col-form-label',
16549 html : this.fieldLabel
16561 var labelCfg = cfg.cn[1];
16562 var contentCfg = cfg.cn[2];
16565 if(this.indicatorpos == 'right'){
16571 cls : 'control-label col-form-label',
16575 html : this.fieldLabel
16591 labelCfg = cfg.cn[0];
16592 contentCfg = cfg.cn[1];
16596 if(this.labelWidth > 12){
16597 labelCfg.style = "width: " + this.labelWidth + 'px';
16599 if(this.width * 1 > 0){
16600 contentCfg.style = "width: " + this.width + 'px';
16602 if(this.labelWidth < 13 && this.labelmd == 0){
16603 this.labelmd = this.labelWidth;
16606 if(this.labellg > 0){
16607 labelCfg.cls += ' col-lg-' + this.labellg;
16608 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16611 if(this.labelmd > 0){
16612 labelCfg.cls += ' col-md-' + this.labelmd;
16613 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16616 if(this.labelsm > 0){
16617 labelCfg.cls += ' col-sm-' + this.labelsm;
16618 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16621 if(this.labelxs > 0){
16622 labelCfg.cls += ' col-xs-' + this.labelxs;
16623 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16627 } else if ( this.fieldLabel.length) {
16628 // Roo.log(" label");
16633 //cls : 'input-group-addon',
16634 html : this.fieldLabel
16639 if(this.indicatorpos == 'right'){
16643 //cls : 'input-group-addon',
16644 html : this.fieldLabel
16654 // Roo.log(" no label && no align");
16661 ['xs','sm','md','lg'].map(function(size){
16662 if (settings[size]) {
16663 cfg.cls += ' col-' + size + '-' + settings[size];
16671 _initEventsCalled : false,
16674 initEvents: function()
16676 if (this._initEventsCalled) { // as we call render... prevent looping...
16679 this._initEventsCalled = true;
16682 throw "can not find store for combo";
16685 this.indicator = this.indicatorEl();
16687 this.store = Roo.factory(this.store, Roo.data);
16688 this.store.parent = this;
16690 // if we are building from html. then this element is so complex, that we can not really
16691 // use the rendered HTML.
16692 // so we have to trash and replace the previous code.
16693 if (Roo.XComponent.build_from_html) {
16694 // remove this element....
16695 var e = this.el.dom, k=0;
16696 while (e ) { e = e.previousSibling; ++k;}
16701 this.rendered = false;
16703 this.render(this.parent().getChildContainer(true), k);
16706 if(Roo.isIOS && this.useNativeIOS){
16707 this.initIOSView();
16715 if(Roo.isTouch && this.mobileTouchView){
16716 this.initTouchView();
16721 this.initTickableEvents();
16725 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16727 if(this.hiddenName){
16729 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16731 this.hiddenField.dom.value =
16732 this.hiddenValue !== undefined ? this.hiddenValue :
16733 this.value !== undefined ? this.value : '';
16735 // prevent input submission
16736 this.el.dom.removeAttribute('name');
16737 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16742 // this.el.dom.setAttribute('autocomplete', 'off');
16745 var cls = 'x-combo-list';
16747 //this.list = new Roo.Layer({
16748 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16754 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16755 _this.list.setWidth(lw);
16758 this.list.on('mouseover', this.onViewOver, this);
16759 this.list.on('mousemove', this.onViewMove, this);
16760 this.list.on('scroll', this.onViewScroll, this);
16763 this.list.swallowEvent('mousewheel');
16764 this.assetHeight = 0;
16767 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16768 this.assetHeight += this.header.getHeight();
16771 this.innerList = this.list.createChild({cls:cls+'-inner'});
16772 this.innerList.on('mouseover', this.onViewOver, this);
16773 this.innerList.on('mousemove', this.onViewMove, this);
16774 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16776 if(this.allowBlank && !this.pageSize && !this.disableClear){
16777 this.footer = this.list.createChild({cls:cls+'-ft'});
16778 this.pageTb = new Roo.Toolbar(this.footer);
16782 this.footer = this.list.createChild({cls:cls+'-ft'});
16783 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16784 {pageSize: this.pageSize});
16788 if (this.pageTb && this.allowBlank && !this.disableClear) {
16790 this.pageTb.add(new Roo.Toolbar.Fill(), {
16791 cls: 'x-btn-icon x-btn-clear',
16793 handler: function()
16796 _this.clearValue();
16797 _this.onSelect(false, -1);
16802 this.assetHeight += this.footer.getHeight();
16807 this.tpl = Roo.bootstrap.version == 4 ?
16808 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16809 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16812 this.view = new Roo.View(this.list, this.tpl, {
16813 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16815 //this.view.wrapEl.setDisplayed(false);
16816 this.view.on('click', this.onViewClick, this);
16819 this.store.on('beforeload', this.onBeforeLoad, this);
16820 this.store.on('load', this.onLoad, this);
16821 this.store.on('loadexception', this.onLoadException, this);
16823 if(this.resizable){
16824 this.resizer = new Roo.Resizable(this.list, {
16825 pinned:true, handles:'se'
16827 this.resizer.on('resize', function(r, w, h){
16828 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16829 this.listWidth = w;
16830 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16831 this.restrictHeight();
16833 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16836 if(!this.editable){
16837 this.editable = true;
16838 this.setEditable(false);
16843 if (typeof(this.events.add.listeners) != 'undefined') {
16845 this.addicon = this.wrap.createChild(
16846 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16848 this.addicon.on('click', function(e) {
16849 this.fireEvent('add', this);
16852 if (typeof(this.events.edit.listeners) != 'undefined') {
16854 this.editicon = this.wrap.createChild(
16855 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16856 if (this.addicon) {
16857 this.editicon.setStyle('margin-left', '40px');
16859 this.editicon.on('click', function(e) {
16861 // we fire even if inothing is selected..
16862 this.fireEvent('edit', this, this.lastData );
16868 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16869 "up" : function(e){
16870 this.inKeyMode = true;
16874 "down" : function(e){
16875 if(!this.isExpanded()){
16876 this.onTriggerClick();
16878 this.inKeyMode = true;
16883 "enter" : function(e){
16884 // this.onViewClick();
16888 if(this.fireEvent("specialkey", this, e)){
16889 this.onViewClick(false);
16895 "esc" : function(e){
16899 "tab" : function(e){
16902 if(this.fireEvent("specialkey", this, e)){
16903 this.onViewClick(false);
16911 doRelay : function(foo, bar, hname){
16912 if(hname == 'down' || this.scope.isExpanded()){
16913 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16922 this.queryDelay = Math.max(this.queryDelay || 10,
16923 this.mode == 'local' ? 10 : 250);
16926 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16928 if(this.typeAhead){
16929 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16931 if(this.editable !== false){
16932 this.inputEl().on("keyup", this.onKeyUp, this);
16934 if(this.forceSelection){
16935 this.inputEl().on('blur', this.doForce, this);
16939 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16940 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16944 initTickableEvents: function()
16948 if(this.hiddenName){
16950 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16952 this.hiddenField.dom.value =
16953 this.hiddenValue !== undefined ? this.hiddenValue :
16954 this.value !== undefined ? this.value : '';
16956 // prevent input submission
16957 this.el.dom.removeAttribute('name');
16958 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16963 // this.list = this.el.select('ul.dropdown-menu',true).first();
16965 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16966 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16967 if(this.triggerList){
16968 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16971 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16972 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16974 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16975 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16977 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16978 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16980 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16981 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16982 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16985 this.cancelBtn.hide();
16990 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16991 _this.list.setWidth(lw);
16994 this.list.on('mouseover', this.onViewOver, this);
16995 this.list.on('mousemove', this.onViewMove, this);
16997 this.list.on('scroll', this.onViewScroll, this);
17000 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17001 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17004 this.view = new Roo.View(this.list, this.tpl, {
17009 selectedClass: this.selectedClass
17012 //this.view.wrapEl.setDisplayed(false);
17013 this.view.on('click', this.onViewClick, this);
17017 this.store.on('beforeload', this.onBeforeLoad, this);
17018 this.store.on('load', this.onLoad, this);
17019 this.store.on('loadexception', this.onLoadException, this);
17022 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17023 "up" : function(e){
17024 this.inKeyMode = true;
17028 "down" : function(e){
17029 this.inKeyMode = true;
17033 "enter" : function(e){
17034 if(this.fireEvent("specialkey", this, e)){
17035 this.onViewClick(false);
17041 "esc" : function(e){
17042 this.onTickableFooterButtonClick(e, false, false);
17045 "tab" : function(e){
17046 this.fireEvent("specialkey", this, e);
17048 this.onTickableFooterButtonClick(e, false, false);
17055 doRelay : function(e, fn, key){
17056 if(this.scope.isExpanded()){
17057 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17066 this.queryDelay = Math.max(this.queryDelay || 10,
17067 this.mode == 'local' ? 10 : 250);
17070 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17072 if(this.typeAhead){
17073 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17076 if(this.editable !== false){
17077 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17080 this.indicator = this.indicatorEl();
17082 if(this.indicator){
17083 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17084 this.indicator.hide();
17089 onDestroy : function(){
17091 this.view.setStore(null);
17092 this.view.el.removeAllListeners();
17093 this.view.el.remove();
17094 this.view.purgeListeners();
17097 this.list.dom.innerHTML = '';
17101 this.store.un('beforeload', this.onBeforeLoad, this);
17102 this.store.un('load', this.onLoad, this);
17103 this.store.un('loadexception', this.onLoadException, this);
17105 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17109 fireKey : function(e){
17110 if(e.isNavKeyPress() && !this.list.isVisible()){
17111 this.fireEvent("specialkey", this, e);
17116 onResize: function(w, h)
17120 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17122 // if(typeof w != 'number'){
17123 // // we do not handle it!?!?
17126 // var tw = this.trigger.getWidth();
17127 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17128 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17130 // this.inputEl().setWidth( this.adjustWidth('input', x));
17132 // //this.trigger.setStyle('left', x+'px');
17134 // if(this.list && this.listWidth === undefined){
17135 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17136 // this.list.setWidth(lw);
17137 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17145 * Allow or prevent the user from directly editing the field text. If false is passed,
17146 * the user will only be able to select from the items defined in the dropdown list. This method
17147 * is the runtime equivalent of setting the 'editable' config option at config time.
17148 * @param {Boolean} value True to allow the user to directly edit the field text
17150 setEditable : function(value){
17151 if(value == this.editable){
17154 this.editable = value;
17156 this.inputEl().dom.setAttribute('readOnly', true);
17157 this.inputEl().on('mousedown', this.onTriggerClick, this);
17158 this.inputEl().addClass('x-combo-noedit');
17160 this.inputEl().dom.removeAttribute('readOnly');
17161 this.inputEl().un('mousedown', this.onTriggerClick, this);
17162 this.inputEl().removeClass('x-combo-noedit');
17168 onBeforeLoad : function(combo,opts){
17169 if(!this.hasFocus){
17173 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17175 this.restrictHeight();
17176 this.selectedIndex = -1;
17180 onLoad : function(){
17182 this.hasQuery = false;
17184 if(!this.hasFocus){
17188 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17189 this.loading.hide();
17192 if(this.store.getCount() > 0){
17195 this.restrictHeight();
17196 if(this.lastQuery == this.allQuery){
17197 if(this.editable && !this.tickable){
17198 this.inputEl().dom.select();
17202 !this.selectByValue(this.value, true) &&
17205 !this.store.lastOptions ||
17206 typeof(this.store.lastOptions.add) == 'undefined' ||
17207 this.store.lastOptions.add != true
17210 this.select(0, true);
17213 if(this.autoFocus){
17216 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17217 this.taTask.delay(this.typeAheadDelay);
17221 this.onEmptyResults();
17227 onLoadException : function()
17229 this.hasQuery = false;
17231 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17232 this.loading.hide();
17235 if(this.tickable && this.editable){
17240 // only causes errors at present
17241 //Roo.log(this.store.reader.jsonData);
17242 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17244 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17250 onTypeAhead : function(){
17251 if(this.store.getCount() > 0){
17252 var r = this.store.getAt(0);
17253 var newValue = r.data[this.displayField];
17254 var len = newValue.length;
17255 var selStart = this.getRawValue().length;
17257 if(selStart != len){
17258 this.setRawValue(newValue);
17259 this.selectText(selStart, newValue.length);
17265 onSelect : function(record, index){
17267 if(this.fireEvent('beforeselect', this, record, index) !== false){
17269 this.setFromData(index > -1 ? record.data : false);
17272 this.fireEvent('select', this, record, index);
17277 * Returns the currently selected field value or empty string if no value is set.
17278 * @return {String} value The selected value
17280 getValue : function()
17282 if(Roo.isIOS && this.useNativeIOS){
17283 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17287 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17290 if(this.valueField){
17291 return typeof this.value != 'undefined' ? this.value : '';
17293 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17297 getRawValue : function()
17299 if(Roo.isIOS && this.useNativeIOS){
17300 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17303 var v = this.inputEl().getValue();
17309 * Clears any text/value currently set in the field
17311 clearValue : function(){
17313 if(this.hiddenField){
17314 this.hiddenField.dom.value = '';
17317 this.setRawValue('');
17318 this.lastSelectionText = '';
17319 this.lastData = false;
17321 var close = this.closeTriggerEl();
17332 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17333 * will be displayed in the field. If the value does not match the data value of an existing item,
17334 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17335 * Otherwise the field will be blank (although the value will still be set).
17336 * @param {String} value The value to match
17338 setValue : function(v)
17340 if(Roo.isIOS && this.useNativeIOS){
17341 this.setIOSValue(v);
17351 if(this.valueField){
17352 var r = this.findRecord(this.valueField, v);
17354 text = r.data[this.displayField];
17355 }else if(this.valueNotFoundText !== undefined){
17356 text = this.valueNotFoundText;
17359 this.lastSelectionText = text;
17360 if(this.hiddenField){
17361 this.hiddenField.dom.value = v;
17363 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17366 var close = this.closeTriggerEl();
17369 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17375 * @property {Object} the last set data for the element
17380 * Sets the value of the field based on a object which is related to the record format for the store.
17381 * @param {Object} value the value to set as. or false on reset?
17383 setFromData : function(o){
17390 var dv = ''; // display value
17391 var vv = ''; // value value..
17393 if (this.displayField) {
17394 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17396 // this is an error condition!!!
17397 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17400 if(this.valueField){
17401 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17404 var close = this.closeTriggerEl();
17407 if(dv.length || vv * 1 > 0){
17409 this.blockFocus=true;
17415 if(this.hiddenField){
17416 this.hiddenField.dom.value = vv;
17418 this.lastSelectionText = dv;
17419 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17423 // no hidden field.. - we store the value in 'value', but still display
17424 // display field!!!!
17425 this.lastSelectionText = dv;
17426 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17433 reset : function(){
17434 // overridden so that last data is reset..
17441 this.setValue(this.originalValue);
17442 //this.clearInvalid();
17443 this.lastData = false;
17445 this.view.clearSelections();
17451 findRecord : function(prop, value){
17453 if(this.store.getCount() > 0){
17454 this.store.each(function(r){
17455 if(r.data[prop] == value){
17465 getName: function()
17467 // returns hidden if it's set..
17468 if (!this.rendered) {return ''};
17469 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17473 onViewMove : function(e, t){
17474 this.inKeyMode = false;
17478 onViewOver : function(e, t){
17479 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17482 var item = this.view.findItemFromChild(t);
17485 var index = this.view.indexOf(item);
17486 this.select(index, false);
17491 onViewClick : function(view, doFocus, el, e)
17493 var index = this.view.getSelectedIndexes()[0];
17495 var r = this.store.getAt(index);
17499 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17506 Roo.each(this.tickItems, function(v,k){
17508 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17510 _this.tickItems.splice(k, 1);
17512 if(typeof(e) == 'undefined' && view == false){
17513 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17525 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17526 this.tickItems.push(r.data);
17529 if(typeof(e) == 'undefined' && view == false){
17530 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17537 this.onSelect(r, index);
17539 if(doFocus !== false && !this.blockFocus){
17540 this.inputEl().focus();
17545 restrictHeight : function(){
17546 //this.innerList.dom.style.height = '';
17547 //var inner = this.innerList.dom;
17548 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17549 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17550 //this.list.beginUpdate();
17551 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17552 this.list.alignTo(this.inputEl(), this.listAlign);
17553 this.list.alignTo(this.inputEl(), this.listAlign);
17554 //this.list.endUpdate();
17558 onEmptyResults : function(){
17560 if(this.tickable && this.editable){
17561 this.hasFocus = false;
17562 this.restrictHeight();
17570 * Returns true if the dropdown list is expanded, else false.
17572 isExpanded : function(){
17573 return this.list.isVisible();
17577 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17578 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17579 * @param {String} value The data value of the item to select
17580 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17581 * selected item if it is not currently in view (defaults to true)
17582 * @return {Boolean} True if the value matched an item in the list, else false
17584 selectByValue : function(v, scrollIntoView){
17585 if(v !== undefined && v !== null){
17586 var r = this.findRecord(this.valueField || this.displayField, v);
17588 this.select(this.store.indexOf(r), scrollIntoView);
17596 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17597 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17598 * @param {Number} index The zero-based index of the list item to select
17599 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17600 * selected item if it is not currently in view (defaults to true)
17602 select : function(index, scrollIntoView){
17603 this.selectedIndex = index;
17604 this.view.select(index);
17605 if(scrollIntoView !== false){
17606 var el = this.view.getNode(index);
17608 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17611 this.list.scrollChildIntoView(el, false);
17617 selectNext : function(){
17618 var ct = this.store.getCount();
17620 if(this.selectedIndex == -1){
17622 }else if(this.selectedIndex < ct-1){
17623 this.select(this.selectedIndex+1);
17629 selectPrev : function(){
17630 var ct = this.store.getCount();
17632 if(this.selectedIndex == -1){
17634 }else if(this.selectedIndex != 0){
17635 this.select(this.selectedIndex-1);
17641 onKeyUp : function(e){
17642 if(this.editable !== false && !e.isSpecialKey()){
17643 this.lastKey = e.getKey();
17644 this.dqTask.delay(this.queryDelay);
17649 validateBlur : function(){
17650 return !this.list || !this.list.isVisible();
17654 initQuery : function(){
17656 var v = this.getRawValue();
17658 if(this.tickable && this.editable){
17659 v = this.tickableInputEl().getValue();
17666 doForce : function(){
17667 if(this.inputEl().dom.value.length > 0){
17668 this.inputEl().dom.value =
17669 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17675 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17676 * query allowing the query action to be canceled if needed.
17677 * @param {String} query The SQL query to execute
17678 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17679 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17680 * saved in the current store (defaults to false)
17682 doQuery : function(q, forceAll){
17684 if(q === undefined || q === null){
17689 forceAll: forceAll,
17693 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17698 forceAll = qe.forceAll;
17699 if(forceAll === true || (q.length >= this.minChars)){
17701 this.hasQuery = true;
17703 if(this.lastQuery != q || this.alwaysQuery){
17704 this.lastQuery = q;
17705 if(this.mode == 'local'){
17706 this.selectedIndex = -1;
17708 this.store.clearFilter();
17711 if(this.specialFilter){
17712 this.fireEvent('specialfilter', this);
17717 this.store.filter(this.displayField, q);
17720 this.store.fireEvent("datachanged", this.store);
17727 this.store.baseParams[this.queryParam] = q;
17729 var options = {params : this.getParams(q)};
17732 options.add = true;
17733 options.params.start = this.page * this.pageSize;
17736 this.store.load(options);
17739 * this code will make the page width larger, at the beginning, the list not align correctly,
17740 * we should expand the list on onLoad
17741 * so command out it
17746 this.selectedIndex = -1;
17751 this.loadNext = false;
17755 getParams : function(q){
17757 //p[this.queryParam] = q;
17761 p.limit = this.pageSize;
17767 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17769 collapse : function(){
17770 if(!this.isExpanded()){
17776 this.hasFocus = false;
17780 this.cancelBtn.hide();
17781 this.trigger.show();
17784 this.tickableInputEl().dom.value = '';
17785 this.tickableInputEl().blur();
17790 Roo.get(document).un('mousedown', this.collapseIf, this);
17791 Roo.get(document).un('mousewheel', this.collapseIf, this);
17792 if (!this.editable) {
17793 Roo.get(document).un('keydown', this.listKeyPress, this);
17795 this.fireEvent('collapse', this);
17801 collapseIf : function(e){
17802 var in_combo = e.within(this.el);
17803 var in_list = e.within(this.list);
17804 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17806 if (in_combo || in_list || is_list) {
17807 //e.stopPropagation();
17812 this.onTickableFooterButtonClick(e, false, false);
17820 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17822 expand : function(){
17824 if(this.isExpanded() || !this.hasFocus){
17828 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17829 this.list.setWidth(lw);
17835 this.restrictHeight();
17839 this.tickItems = Roo.apply([], this.item);
17842 this.cancelBtn.show();
17843 this.trigger.hide();
17846 this.tickableInputEl().focus();
17851 Roo.get(document).on('mousedown', this.collapseIf, this);
17852 Roo.get(document).on('mousewheel', this.collapseIf, this);
17853 if (!this.editable) {
17854 Roo.get(document).on('keydown', this.listKeyPress, this);
17857 this.fireEvent('expand', this);
17861 // Implements the default empty TriggerField.onTriggerClick function
17862 onTriggerClick : function(e)
17864 Roo.log('trigger click');
17866 if(this.disabled || !this.triggerList){
17871 this.loadNext = false;
17873 if(this.isExpanded()){
17875 if (!this.blockFocus) {
17876 this.inputEl().focus();
17880 this.hasFocus = true;
17881 if(this.triggerAction == 'all') {
17882 this.doQuery(this.allQuery, true);
17884 this.doQuery(this.getRawValue());
17886 if (!this.blockFocus) {
17887 this.inputEl().focus();
17892 onTickableTriggerClick : function(e)
17899 this.loadNext = false;
17900 this.hasFocus = true;
17902 if(this.triggerAction == 'all') {
17903 this.doQuery(this.allQuery, true);
17905 this.doQuery(this.getRawValue());
17909 onSearchFieldClick : function(e)
17911 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17912 this.onTickableFooterButtonClick(e, false, false);
17916 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17921 this.loadNext = false;
17922 this.hasFocus = true;
17924 if(this.triggerAction == 'all') {
17925 this.doQuery(this.allQuery, true);
17927 this.doQuery(this.getRawValue());
17931 listKeyPress : function(e)
17933 //Roo.log('listkeypress');
17934 // scroll to first matching element based on key pres..
17935 if (e.isSpecialKey()) {
17938 var k = String.fromCharCode(e.getKey()).toUpperCase();
17941 var csel = this.view.getSelectedNodes();
17942 var cselitem = false;
17944 var ix = this.view.indexOf(csel[0]);
17945 cselitem = this.store.getAt(ix);
17946 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17952 this.store.each(function(v) {
17954 // start at existing selection.
17955 if (cselitem.id == v.id) {
17961 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17962 match = this.store.indexOf(v);
17968 if (match === false) {
17969 return true; // no more action?
17972 this.view.select(match);
17973 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17974 sn.scrollIntoView(sn.dom.parentNode, false);
17977 onViewScroll : function(e, t){
17979 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){
17983 this.hasQuery = true;
17985 this.loading = this.list.select('.loading', true).first();
17987 if(this.loading === null){
17988 this.list.createChild({
17990 cls: 'loading roo-select2-more-results roo-select2-active',
17991 html: 'Loading more results...'
17994 this.loading = this.list.select('.loading', true).first();
17996 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17998 this.loading.hide();
18001 this.loading.show();
18006 this.loadNext = true;
18008 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18013 addItem : function(o)
18015 var dv = ''; // display value
18017 if (this.displayField) {
18018 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18020 // this is an error condition!!!
18021 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18028 var choice = this.choices.createChild({
18030 cls: 'roo-select2-search-choice',
18039 cls: 'roo-select2-search-choice-close fa fa-times',
18044 }, this.searchField);
18046 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18048 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18056 this.inputEl().dom.value = '';
18061 onRemoveItem : function(e, _self, o)
18063 e.preventDefault();
18065 this.lastItem = Roo.apply([], this.item);
18067 var index = this.item.indexOf(o.data) * 1;
18070 Roo.log('not this item?!');
18074 this.item.splice(index, 1);
18079 this.fireEvent('remove', this, e);
18085 syncValue : function()
18087 if(!this.item.length){
18094 Roo.each(this.item, function(i){
18095 if(_this.valueField){
18096 value.push(i[_this.valueField]);
18103 this.value = value.join(',');
18105 if(this.hiddenField){
18106 this.hiddenField.dom.value = this.value;
18109 this.store.fireEvent("datachanged", this.store);
18114 clearItem : function()
18116 if(!this.multiple){
18122 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18130 if(this.tickable && !Roo.isTouch){
18131 this.view.refresh();
18135 inputEl: function ()
18137 if(Roo.isIOS && this.useNativeIOS){
18138 return this.el.select('select.roo-ios-select', true).first();
18141 if(Roo.isTouch && this.mobileTouchView){
18142 return this.el.select('input.form-control',true).first();
18146 return this.searchField;
18149 return this.el.select('input.form-control',true).first();
18152 onTickableFooterButtonClick : function(e, btn, el)
18154 e.preventDefault();
18156 this.lastItem = Roo.apply([], this.item);
18158 if(btn && btn.name == 'cancel'){
18159 this.tickItems = Roo.apply([], this.item);
18168 Roo.each(this.tickItems, function(o){
18176 validate : function()
18178 if(this.getVisibilityEl().hasClass('hidden')){
18182 var v = this.getRawValue();
18185 v = this.getValue();
18188 if(this.disabled || this.allowBlank || v.length){
18193 this.markInvalid();
18197 tickableInputEl : function()
18199 if(!this.tickable || !this.editable){
18200 return this.inputEl();
18203 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18207 getAutoCreateTouchView : function()
18212 cls: 'form-group' //input-group
18218 type : this.inputType,
18219 cls : 'form-control x-combo-noedit',
18220 autocomplete: 'new-password',
18221 placeholder : this.placeholder || '',
18226 input.name = this.name;
18230 input.cls += ' input-' + this.size;
18233 if (this.disabled) {
18234 input.disabled = true;
18238 cls : 'roo-combobox-wrap',
18245 inputblock.cls += ' input-group';
18247 inputblock.cn.unshift({
18249 cls : 'input-group-addon input-group-prepend input-group-text',
18254 if(this.removable && !this.multiple){
18255 inputblock.cls += ' roo-removable';
18257 inputblock.cn.push({
18260 cls : 'roo-combo-removable-btn close'
18264 if(this.hasFeedback && !this.allowBlank){
18266 inputblock.cls += ' has-feedback';
18268 inputblock.cn.push({
18270 cls: 'glyphicon form-control-feedback'
18277 inputblock.cls += (this.before) ? '' : ' input-group';
18279 inputblock.cn.push({
18281 cls : 'input-group-addon input-group-append input-group-text',
18287 var ibwrap = inputblock;
18292 cls: 'roo-select2-choices',
18296 cls: 'roo-select2-search-field',
18309 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18314 cls: 'form-hidden-field'
18320 if(!this.multiple && this.showToggleBtn){
18326 if (this.caret != false) {
18329 cls: 'fa fa-' + this.caret
18336 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18338 Roo.bootstrap.version == 3 ? caret : '',
18341 cls: 'combobox-clear',
18355 combobox.cls += ' roo-select2-container-multi';
18358 var required = this.allowBlank ? {
18360 style: 'display: none'
18363 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18364 tooltip : 'This field is required'
18367 var align = this.labelAlign || this.parentLabelAlign();
18369 if (align ==='left' && this.fieldLabel.length) {
18375 cls : 'control-label col-form-label',
18376 html : this.fieldLabel
18380 cls : 'roo-combobox-wrap ',
18387 var labelCfg = cfg.cn[1];
18388 var contentCfg = cfg.cn[2];
18391 if(this.indicatorpos == 'right'){
18396 cls : 'control-label col-form-label',
18400 html : this.fieldLabel
18406 cls : "roo-combobox-wrap ",
18414 labelCfg = cfg.cn[0];
18415 contentCfg = cfg.cn[1];
18420 if(this.labelWidth > 12){
18421 labelCfg.style = "width: " + this.labelWidth + 'px';
18424 if(this.labelWidth < 13 && this.labelmd == 0){
18425 this.labelmd = this.labelWidth;
18428 if(this.labellg > 0){
18429 labelCfg.cls += ' col-lg-' + this.labellg;
18430 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18433 if(this.labelmd > 0){
18434 labelCfg.cls += ' col-md-' + this.labelmd;
18435 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18438 if(this.labelsm > 0){
18439 labelCfg.cls += ' col-sm-' + this.labelsm;
18440 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18443 if(this.labelxs > 0){
18444 labelCfg.cls += ' col-xs-' + this.labelxs;
18445 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18449 } else if ( this.fieldLabel.length) {
18454 cls : 'control-label',
18455 html : this.fieldLabel
18466 if(this.indicatorpos == 'right'){
18470 cls : 'control-label',
18471 html : this.fieldLabel,
18489 var settings = this;
18491 ['xs','sm','md','lg'].map(function(size){
18492 if (settings[size]) {
18493 cfg.cls += ' col-' + size + '-' + settings[size];
18500 initTouchView : function()
18502 this.renderTouchView();
18504 this.touchViewEl.on('scroll', function(){
18505 this.el.dom.scrollTop = 0;
18508 this.originalValue = this.getValue();
18510 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18512 this.inputEl().on("click", this.showTouchView, this);
18513 if (this.triggerEl) {
18514 this.triggerEl.on("click", this.showTouchView, this);
18518 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18519 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18521 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18523 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18524 this.store.on('load', this.onTouchViewLoad, this);
18525 this.store.on('loadexception', this.onTouchViewLoadException, this);
18527 if(this.hiddenName){
18529 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18531 this.hiddenField.dom.value =
18532 this.hiddenValue !== undefined ? this.hiddenValue :
18533 this.value !== undefined ? this.value : '';
18535 this.el.dom.removeAttribute('name');
18536 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18540 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18541 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18544 if(this.removable && !this.multiple){
18545 var close = this.closeTriggerEl();
18547 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18548 close.on('click', this.removeBtnClick, this, close);
18552 * fix the bug in Safari iOS8
18554 this.inputEl().on("focus", function(e){
18555 document.activeElement.blur();
18558 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18565 renderTouchView : function()
18567 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18568 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18570 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18571 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18573 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18574 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18575 this.touchViewBodyEl.setStyle('overflow', 'auto');
18577 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18578 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18580 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18581 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18585 showTouchView : function()
18591 this.touchViewHeaderEl.hide();
18593 if(this.modalTitle.length){
18594 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18595 this.touchViewHeaderEl.show();
18598 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18599 this.touchViewEl.show();
18601 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18603 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18604 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18606 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18608 if(this.modalTitle.length){
18609 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18612 this.touchViewBodyEl.setHeight(bodyHeight);
18616 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18618 this.touchViewEl.addClass(['in','show']);
18621 if(this._touchViewMask){
18622 Roo.get(document.body).addClass("x-body-masked");
18623 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18624 this._touchViewMask.setStyle('z-index', 10000);
18625 this._touchViewMask.addClass('show');
18628 this.doTouchViewQuery();
18632 hideTouchView : function()
18634 this.touchViewEl.removeClass(['in','show']);
18638 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18640 this.touchViewEl.setStyle('display', 'none');
18643 if(this._touchViewMask){
18644 this._touchViewMask.removeClass('show');
18645 Roo.get(document.body).removeClass("x-body-masked");
18649 setTouchViewValue : function()
18656 Roo.each(this.tickItems, function(o){
18661 this.hideTouchView();
18664 doTouchViewQuery : function()
18673 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18677 if(!this.alwaysQuery || this.mode == 'local'){
18678 this.onTouchViewLoad();
18685 onTouchViewBeforeLoad : function(combo,opts)
18691 onTouchViewLoad : function()
18693 if(this.store.getCount() < 1){
18694 this.onTouchViewEmptyResults();
18698 this.clearTouchView();
18700 var rawValue = this.getRawValue();
18702 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18704 this.tickItems = [];
18706 this.store.data.each(function(d, rowIndex){
18707 var row = this.touchViewListGroup.createChild(template);
18709 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18710 row.addClass(d.data.cls);
18713 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18716 html : d.data[this.displayField]
18719 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18720 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18723 row.removeClass('selected');
18724 if(!this.multiple && this.valueField &&
18725 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18728 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18729 row.addClass('selected');
18732 if(this.multiple && this.valueField &&
18733 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18737 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18738 this.tickItems.push(d.data);
18741 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18745 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18747 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18749 if(this.modalTitle.length){
18750 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18753 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18755 if(this.mobile_restrict_height && listHeight < bodyHeight){
18756 this.touchViewBodyEl.setHeight(listHeight);
18761 if(firstChecked && listHeight > bodyHeight){
18762 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18767 onTouchViewLoadException : function()
18769 this.hideTouchView();
18772 onTouchViewEmptyResults : function()
18774 this.clearTouchView();
18776 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18778 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18782 clearTouchView : function()
18784 this.touchViewListGroup.dom.innerHTML = '';
18787 onTouchViewClick : function(e, el, o)
18789 e.preventDefault();
18792 var rowIndex = o.rowIndex;
18794 var r = this.store.getAt(rowIndex);
18796 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18798 if(!this.multiple){
18799 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18800 c.dom.removeAttribute('checked');
18803 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18805 this.setFromData(r.data);
18807 var close = this.closeTriggerEl();
18813 this.hideTouchView();
18815 this.fireEvent('select', this, r, rowIndex);
18820 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18821 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18822 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18826 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18827 this.addItem(r.data);
18828 this.tickItems.push(r.data);
18832 getAutoCreateNativeIOS : function()
18835 cls: 'form-group' //input-group,
18840 cls : 'roo-ios-select'
18844 combobox.name = this.name;
18847 if (this.disabled) {
18848 combobox.disabled = true;
18851 var settings = this;
18853 ['xs','sm','md','lg'].map(function(size){
18854 if (settings[size]) {
18855 cfg.cls += ' col-' + size + '-' + settings[size];
18865 initIOSView : function()
18867 this.store.on('load', this.onIOSViewLoad, this);
18872 onIOSViewLoad : function()
18874 if(this.store.getCount() < 1){
18878 this.clearIOSView();
18880 if(this.allowBlank) {
18882 var default_text = '-- SELECT --';
18884 if(this.placeholder.length){
18885 default_text = this.placeholder;
18888 if(this.emptyTitle.length){
18889 default_text += ' - ' + this.emptyTitle + ' -';
18892 var opt = this.inputEl().createChild({
18895 html : default_text
18899 o[this.valueField] = 0;
18900 o[this.displayField] = default_text;
18902 this.ios_options.push({
18909 this.store.data.each(function(d, rowIndex){
18913 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18914 html = d.data[this.displayField];
18919 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18920 value = d.data[this.valueField];
18929 if(this.value == d.data[this.valueField]){
18930 option['selected'] = true;
18933 var opt = this.inputEl().createChild(option);
18935 this.ios_options.push({
18942 this.inputEl().on('change', function(){
18943 this.fireEvent('select', this);
18948 clearIOSView: function()
18950 this.inputEl().dom.innerHTML = '';
18952 this.ios_options = [];
18955 setIOSValue: function(v)
18959 if(!this.ios_options){
18963 Roo.each(this.ios_options, function(opts){
18965 opts.el.dom.removeAttribute('selected');
18967 if(opts.data[this.valueField] != v){
18971 opts.el.dom.setAttribute('selected', true);
18977 * @cfg {Boolean} grow
18981 * @cfg {Number} growMin
18985 * @cfg {Number} growMax
18994 Roo.apply(Roo.bootstrap.ComboBox, {
18998 cls: 'modal-header',
19020 cls: 'list-group-item',
19024 cls: 'roo-combobox-list-group-item-value'
19028 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19042 listItemCheckbox : {
19044 cls: 'list-group-item',
19048 cls: 'roo-combobox-list-group-item-value'
19052 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19068 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19073 cls: 'modal-footer',
19081 cls: 'col-xs-6 text-left',
19084 cls: 'btn btn-danger roo-touch-view-cancel',
19090 cls: 'col-xs-6 text-right',
19093 cls: 'btn btn-success roo-touch-view-ok',
19104 Roo.apply(Roo.bootstrap.ComboBox, {
19106 touchViewTemplate : {
19108 cls: 'modal fade roo-combobox-touch-view',
19112 cls: 'modal-dialog',
19113 style : 'position:fixed', // we have to fix position....
19117 cls: 'modal-content',
19119 Roo.bootstrap.ComboBox.header,
19120 Roo.bootstrap.ComboBox.body,
19121 Roo.bootstrap.ComboBox.footer
19130 * Ext JS Library 1.1.1
19131 * Copyright(c) 2006-2007, Ext JS, LLC.
19133 * Originally Released Under LGPL - original licence link has changed is not relivant.
19136 * <script type="text/javascript">
19141 * @extends Roo.util.Observable
19142 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19143 * This class also supports single and multi selection modes. <br>
19144 * Create a data model bound view:
19146 var store = new Roo.data.Store(...);
19148 var view = new Roo.View({
19150 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19152 singleSelect: true,
19153 selectedClass: "ydataview-selected",
19157 // listen for node click?
19158 view.on("click", function(vw, index, node, e){
19159 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19163 dataModel.load("foobar.xml");
19165 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19167 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19168 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19170 * Note: old style constructor is still suported (container, template, config)
19173 * Create a new View
19174 * @param {Object} config The config object
19177 Roo.View = function(config, depreciated_tpl, depreciated_config){
19179 this.parent = false;
19181 if (typeof(depreciated_tpl) == 'undefined') {
19182 // new way.. - universal constructor.
19183 Roo.apply(this, config);
19184 this.el = Roo.get(this.el);
19187 this.el = Roo.get(config);
19188 this.tpl = depreciated_tpl;
19189 Roo.apply(this, depreciated_config);
19191 this.wrapEl = this.el.wrap().wrap();
19192 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19195 if(typeof(this.tpl) == "string"){
19196 this.tpl = new Roo.Template(this.tpl);
19198 // support xtype ctors..
19199 this.tpl = new Roo.factory(this.tpl, Roo);
19203 this.tpl.compile();
19208 * @event beforeclick
19209 * Fires before a click is processed. Returns false to cancel the default action.
19210 * @param {Roo.View} this
19211 * @param {Number} index The index of the target node
19212 * @param {HTMLElement} node The target node
19213 * @param {Roo.EventObject} e The raw event object
19215 "beforeclick" : true,
19218 * Fires when a template node is clicked.
19219 * @param {Roo.View} this
19220 * @param {Number} index The index of the target node
19221 * @param {HTMLElement} node The target node
19222 * @param {Roo.EventObject} e The raw event object
19227 * Fires when a template node is double clicked.
19228 * @param {Roo.View} this
19229 * @param {Number} index The index of the target node
19230 * @param {HTMLElement} node The target node
19231 * @param {Roo.EventObject} e The raw event object
19235 * @event contextmenu
19236 * Fires when a template node is right clicked.
19237 * @param {Roo.View} this
19238 * @param {Number} index The index of the target node
19239 * @param {HTMLElement} node The target node
19240 * @param {Roo.EventObject} e The raw event object
19242 "contextmenu" : true,
19244 * @event selectionchange
19245 * Fires when the selected nodes change.
19246 * @param {Roo.View} this
19247 * @param {Array} selections Array of the selected nodes
19249 "selectionchange" : true,
19252 * @event beforeselect
19253 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19254 * @param {Roo.View} this
19255 * @param {HTMLElement} node The node to be selected
19256 * @param {Array} selections Array of currently selected nodes
19258 "beforeselect" : true,
19260 * @event preparedata
19261 * Fires on every row to render, to allow you to change the data.
19262 * @param {Roo.View} this
19263 * @param {Object} data to be rendered (change this)
19265 "preparedata" : true
19273 "click": this.onClick,
19274 "dblclick": this.onDblClick,
19275 "contextmenu": this.onContextMenu,
19279 this.selections = [];
19281 this.cmp = new Roo.CompositeElementLite([]);
19283 this.store = Roo.factory(this.store, Roo.data);
19284 this.setStore(this.store, true);
19287 if ( this.footer && this.footer.xtype) {
19289 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19291 this.footer.dataSource = this.store;
19292 this.footer.container = fctr;
19293 this.footer = Roo.factory(this.footer, Roo);
19294 fctr.insertFirst(this.el);
19296 // this is a bit insane - as the paging toolbar seems to detach the el..
19297 // dom.parentNode.parentNode.parentNode
19298 // they get detached?
19302 Roo.View.superclass.constructor.call(this);
19307 Roo.extend(Roo.View, Roo.util.Observable, {
19310 * @cfg {Roo.data.Store} store Data store to load data from.
19315 * @cfg {String|Roo.Element} el The container element.
19320 * @cfg {String|Roo.Template} tpl The template used by this View
19324 * @cfg {String} dataName the named area of the template to use as the data area
19325 * Works with domtemplates roo-name="name"
19329 * @cfg {String} selectedClass The css class to add to selected nodes
19331 selectedClass : "x-view-selected",
19333 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19338 * @cfg {String} text to display on mask (default Loading)
19342 * @cfg {Boolean} multiSelect Allow multiple selection
19344 multiSelect : false,
19346 * @cfg {Boolean} singleSelect Allow single selection
19348 singleSelect: false,
19351 * @cfg {Boolean} toggleSelect - selecting
19353 toggleSelect : false,
19356 * @cfg {Boolean} tickable - selecting
19361 * Returns the element this view is bound to.
19362 * @return {Roo.Element}
19364 getEl : function(){
19365 return this.wrapEl;
19371 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19373 refresh : function(){
19374 //Roo.log('refresh');
19377 // if we are using something like 'domtemplate', then
19378 // the what gets used is:
19379 // t.applySubtemplate(NAME, data, wrapping data..)
19380 // the outer template then get' applied with
19381 // the store 'extra data'
19382 // and the body get's added to the
19383 // roo-name="data" node?
19384 // <span class='roo-tpl-{name}'></span> ?????
19388 this.clearSelections();
19389 this.el.update("");
19391 var records = this.store.getRange();
19392 if(records.length < 1) {
19394 // is this valid?? = should it render a template??
19396 this.el.update(this.emptyText);
19400 if (this.dataName) {
19401 this.el.update(t.apply(this.store.meta)); //????
19402 el = this.el.child('.roo-tpl-' + this.dataName);
19405 for(var i = 0, len = records.length; i < len; i++){
19406 var data = this.prepareData(records[i].data, i, records[i]);
19407 this.fireEvent("preparedata", this, data, i, records[i]);
19409 var d = Roo.apply({}, data);
19412 Roo.apply(d, {'roo-id' : Roo.id()});
19416 Roo.each(this.parent.item, function(item){
19417 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19420 Roo.apply(d, {'roo-data-checked' : 'checked'});
19424 html[html.length] = Roo.util.Format.trim(
19426 t.applySubtemplate(this.dataName, d, this.store.meta) :
19433 el.update(html.join(""));
19434 this.nodes = el.dom.childNodes;
19435 this.updateIndexes(0);
19440 * Function to override to reformat the data that is sent to
19441 * the template for each node.
19442 * DEPRICATED - use the preparedata event handler.
19443 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19444 * a JSON object for an UpdateManager bound view).
19446 prepareData : function(data, index, record)
19448 this.fireEvent("preparedata", this, data, index, record);
19452 onUpdate : function(ds, record){
19453 // Roo.log('on update');
19454 this.clearSelections();
19455 var index = this.store.indexOf(record);
19456 var n = this.nodes[index];
19457 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19458 n.parentNode.removeChild(n);
19459 this.updateIndexes(index, index);
19465 onAdd : function(ds, records, index)
19467 //Roo.log(['on Add', ds, records, index] );
19468 this.clearSelections();
19469 if(this.nodes.length == 0){
19473 var n = this.nodes[index];
19474 for(var i = 0, len = records.length; i < len; i++){
19475 var d = this.prepareData(records[i].data, i, records[i]);
19477 this.tpl.insertBefore(n, d);
19480 this.tpl.append(this.el, d);
19483 this.updateIndexes(index);
19486 onRemove : function(ds, record, index){
19487 // Roo.log('onRemove');
19488 this.clearSelections();
19489 var el = this.dataName ?
19490 this.el.child('.roo-tpl-' + this.dataName) :
19493 el.dom.removeChild(this.nodes[index]);
19494 this.updateIndexes(index);
19498 * Refresh an individual node.
19499 * @param {Number} index
19501 refreshNode : function(index){
19502 this.onUpdate(this.store, this.store.getAt(index));
19505 updateIndexes : function(startIndex, endIndex){
19506 var ns = this.nodes;
19507 startIndex = startIndex || 0;
19508 endIndex = endIndex || ns.length - 1;
19509 for(var i = startIndex; i <= endIndex; i++){
19510 ns[i].nodeIndex = i;
19515 * Changes the data store this view uses and refresh the view.
19516 * @param {Store} store
19518 setStore : function(store, initial){
19519 if(!initial && this.store){
19520 this.store.un("datachanged", this.refresh);
19521 this.store.un("add", this.onAdd);
19522 this.store.un("remove", this.onRemove);
19523 this.store.un("update", this.onUpdate);
19524 this.store.un("clear", this.refresh);
19525 this.store.un("beforeload", this.onBeforeLoad);
19526 this.store.un("load", this.onLoad);
19527 this.store.un("loadexception", this.onLoad);
19531 store.on("datachanged", this.refresh, this);
19532 store.on("add", this.onAdd, this);
19533 store.on("remove", this.onRemove, this);
19534 store.on("update", this.onUpdate, this);
19535 store.on("clear", this.refresh, this);
19536 store.on("beforeload", this.onBeforeLoad, this);
19537 store.on("load", this.onLoad, this);
19538 store.on("loadexception", this.onLoad, this);
19546 * onbeforeLoad - masks the loading area.
19549 onBeforeLoad : function(store,opts)
19551 //Roo.log('onBeforeLoad');
19553 this.el.update("");
19555 this.el.mask(this.mask ? this.mask : "Loading" );
19557 onLoad : function ()
19564 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19565 * @param {HTMLElement} node
19566 * @return {HTMLElement} The template node
19568 findItemFromChild : function(node){
19569 var el = this.dataName ?
19570 this.el.child('.roo-tpl-' + this.dataName,true) :
19573 if(!node || node.parentNode == el){
19576 var p = node.parentNode;
19577 while(p && p != el){
19578 if(p.parentNode == el){
19587 onClick : function(e){
19588 var item = this.findItemFromChild(e.getTarget());
19590 var index = this.indexOf(item);
19591 if(this.onItemClick(item, index, e) !== false){
19592 this.fireEvent("click", this, index, item, e);
19595 this.clearSelections();
19600 onContextMenu : function(e){
19601 var item = this.findItemFromChild(e.getTarget());
19603 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19608 onDblClick : function(e){
19609 var item = this.findItemFromChild(e.getTarget());
19611 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19615 onItemClick : function(item, index, e)
19617 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19620 if (this.toggleSelect) {
19621 var m = this.isSelected(item) ? 'unselect' : 'select';
19624 _t[m](item, true, false);
19627 if(this.multiSelect || this.singleSelect){
19628 if(this.multiSelect && e.shiftKey && this.lastSelection){
19629 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19631 this.select(item, this.multiSelect && e.ctrlKey);
19632 this.lastSelection = item;
19635 if(!this.tickable){
19636 e.preventDefault();
19644 * Get the number of selected nodes.
19647 getSelectionCount : function(){
19648 return this.selections.length;
19652 * Get the currently selected nodes.
19653 * @return {Array} An array of HTMLElements
19655 getSelectedNodes : function(){
19656 return this.selections;
19660 * Get the indexes of the selected nodes.
19663 getSelectedIndexes : function(){
19664 var indexes = [], s = this.selections;
19665 for(var i = 0, len = s.length; i < len; i++){
19666 indexes.push(s[i].nodeIndex);
19672 * Clear all selections
19673 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19675 clearSelections : function(suppressEvent){
19676 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19677 this.cmp.elements = this.selections;
19678 this.cmp.removeClass(this.selectedClass);
19679 this.selections = [];
19680 if(!suppressEvent){
19681 this.fireEvent("selectionchange", this, this.selections);
19687 * Returns true if the passed node is selected
19688 * @param {HTMLElement/Number} node The node or node index
19689 * @return {Boolean}
19691 isSelected : function(node){
19692 var s = this.selections;
19696 node = this.getNode(node);
19697 return s.indexOf(node) !== -1;
19702 * @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
19703 * @param {Boolean} keepExisting (optional) true to keep existing selections
19704 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19706 select : function(nodeInfo, keepExisting, suppressEvent){
19707 if(nodeInfo instanceof Array){
19709 this.clearSelections(true);
19711 for(var i = 0, len = nodeInfo.length; i < len; i++){
19712 this.select(nodeInfo[i], true, true);
19716 var node = this.getNode(nodeInfo);
19717 if(!node || this.isSelected(node)){
19718 return; // already selected.
19721 this.clearSelections(true);
19724 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19725 Roo.fly(node).addClass(this.selectedClass);
19726 this.selections.push(node);
19727 if(!suppressEvent){
19728 this.fireEvent("selectionchange", this, this.selections);
19736 * @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
19737 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19738 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19740 unselect : function(nodeInfo, keepExisting, suppressEvent)
19742 if(nodeInfo instanceof Array){
19743 Roo.each(this.selections, function(s) {
19744 this.unselect(s, nodeInfo);
19748 var node = this.getNode(nodeInfo);
19749 if(!node || !this.isSelected(node)){
19750 //Roo.log("not selected");
19751 return; // not selected.
19755 Roo.each(this.selections, function(s) {
19757 Roo.fly(node).removeClass(this.selectedClass);
19764 this.selections= ns;
19765 this.fireEvent("selectionchange", this, this.selections);
19769 * Gets a template node.
19770 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19771 * @return {HTMLElement} The node or null if it wasn't found
19773 getNode : function(nodeInfo){
19774 if(typeof nodeInfo == "string"){
19775 return document.getElementById(nodeInfo);
19776 }else if(typeof nodeInfo == "number"){
19777 return this.nodes[nodeInfo];
19783 * Gets a range template nodes.
19784 * @param {Number} startIndex
19785 * @param {Number} endIndex
19786 * @return {Array} An array of nodes
19788 getNodes : function(start, end){
19789 var ns = this.nodes;
19790 start = start || 0;
19791 end = typeof end == "undefined" ? ns.length - 1 : end;
19794 for(var i = start; i <= end; i++){
19798 for(var i = start; i >= end; i--){
19806 * Finds the index of the passed node
19807 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19808 * @return {Number} The index of the node or -1
19810 indexOf : function(node){
19811 node = this.getNode(node);
19812 if(typeof node.nodeIndex == "number"){
19813 return node.nodeIndex;
19815 var ns = this.nodes;
19816 for(var i = 0, len = ns.length; i < len; i++){
19827 * based on jquery fullcalendar
19831 Roo.bootstrap = Roo.bootstrap || {};
19833 * @class Roo.bootstrap.Calendar
19834 * @extends Roo.bootstrap.Component
19835 * Bootstrap Calendar class
19836 * @cfg {Boolean} loadMask (true|false) default false
19837 * @cfg {Object} header generate the user specific header of the calendar, default false
19840 * Create a new Container
19841 * @param {Object} config The config object
19846 Roo.bootstrap.Calendar = function(config){
19847 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19851 * Fires when a date is selected
19852 * @param {DatePicker} this
19853 * @param {Date} date The selected date
19857 * @event monthchange
19858 * Fires when the displayed month changes
19859 * @param {DatePicker} this
19860 * @param {Date} date The selected month
19862 'monthchange': true,
19864 * @event evententer
19865 * Fires when mouse over an event
19866 * @param {Calendar} this
19867 * @param {event} Event
19869 'evententer': true,
19871 * @event eventleave
19872 * Fires when the mouse leaves an
19873 * @param {Calendar} this
19876 'eventleave': true,
19878 * @event eventclick
19879 * Fires when the mouse click an
19880 * @param {Calendar} this
19889 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19892 * @cfg {Number} startDay
19893 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19901 getAutoCreate : function(){
19904 var fc_button = function(name, corner, style, content ) {
19905 return Roo.apply({},{
19907 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19909 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19912 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19923 style : 'width:100%',
19930 cls : 'fc-header-left',
19932 fc_button('prev', 'left', 'arrow', '‹' ),
19933 fc_button('next', 'right', 'arrow', '›' ),
19934 { tag: 'span', cls: 'fc-header-space' },
19935 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19943 cls : 'fc-header-center',
19947 cls: 'fc-header-title',
19950 html : 'month / year'
19958 cls : 'fc-header-right',
19960 /* fc_button('month', 'left', '', 'month' ),
19961 fc_button('week', '', '', 'week' ),
19962 fc_button('day', 'right', '', 'day' )
19974 header = this.header;
19977 var cal_heads = function() {
19979 // fixme - handle this.
19981 for (var i =0; i < Date.dayNames.length; i++) {
19982 var d = Date.dayNames[i];
19985 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19986 html : d.substring(0,3)
19990 ret[0].cls += ' fc-first';
19991 ret[6].cls += ' fc-last';
19994 var cal_cell = function(n) {
19997 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20002 cls: 'fc-day-number',
20006 cls: 'fc-day-content',
20010 style: 'position: relative;' // height: 17px;
20022 var cal_rows = function() {
20025 for (var r = 0; r < 6; r++) {
20032 for (var i =0; i < Date.dayNames.length; i++) {
20033 var d = Date.dayNames[i];
20034 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20037 row.cn[0].cls+=' fc-first';
20038 row.cn[0].cn[0].style = 'min-height:90px';
20039 row.cn[6].cls+=' fc-last';
20043 ret[0].cls += ' fc-first';
20044 ret[4].cls += ' fc-prev-last';
20045 ret[5].cls += ' fc-last';
20052 cls: 'fc-border-separate',
20053 style : 'width:100%',
20061 cls : 'fc-first fc-last',
20079 cls : 'fc-content',
20080 style : "position: relative;",
20083 cls : 'fc-view fc-view-month fc-grid',
20084 style : 'position: relative',
20085 unselectable : 'on',
20088 cls : 'fc-event-container',
20089 style : 'position:absolute;z-index:8;top:0;left:0;'
20107 initEvents : function()
20110 throw "can not find store for calendar";
20116 style: "text-align:center",
20120 style: "background-color:white;width:50%;margin:250 auto",
20124 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20135 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20137 var size = this.el.select('.fc-content', true).first().getSize();
20138 this.maskEl.setSize(size.width, size.height);
20139 this.maskEl.enableDisplayMode("block");
20140 if(!this.loadMask){
20141 this.maskEl.hide();
20144 this.store = Roo.factory(this.store, Roo.data);
20145 this.store.on('load', this.onLoad, this);
20146 this.store.on('beforeload', this.onBeforeLoad, this);
20150 this.cells = this.el.select('.fc-day',true);
20151 //Roo.log(this.cells);
20152 this.textNodes = this.el.query('.fc-day-number');
20153 this.cells.addClassOnOver('fc-state-hover');
20155 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20156 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20157 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20158 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20160 this.on('monthchange', this.onMonthChange, this);
20162 this.update(new Date().clearTime());
20165 resize : function() {
20166 var sz = this.el.getSize();
20168 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20169 this.el.select('.fc-day-content div',true).setHeight(34);
20174 showPrevMonth : function(e){
20175 this.update(this.activeDate.add("mo", -1));
20177 showToday : function(e){
20178 this.update(new Date().clearTime());
20181 showNextMonth : function(e){
20182 this.update(this.activeDate.add("mo", 1));
20186 showPrevYear : function(){
20187 this.update(this.activeDate.add("y", -1));
20191 showNextYear : function(){
20192 this.update(this.activeDate.add("y", 1));
20197 update : function(date)
20199 var vd = this.activeDate;
20200 this.activeDate = date;
20201 // if(vd && this.el){
20202 // var t = date.getTime();
20203 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20204 // Roo.log('using add remove');
20206 // this.fireEvent('monthchange', this, date);
20208 // this.cells.removeClass("fc-state-highlight");
20209 // this.cells.each(function(c){
20210 // if(c.dateValue == t){
20211 // c.addClass("fc-state-highlight");
20212 // setTimeout(function(){
20213 // try{c.dom.firstChild.focus();}catch(e){}
20223 var days = date.getDaysInMonth();
20225 var firstOfMonth = date.getFirstDateOfMonth();
20226 var startingPos = firstOfMonth.getDay()-this.startDay;
20228 if(startingPos < this.startDay){
20232 var pm = date.add(Date.MONTH, -1);
20233 var prevStart = pm.getDaysInMonth()-startingPos;
20235 this.cells = this.el.select('.fc-day',true);
20236 this.textNodes = this.el.query('.fc-day-number');
20237 this.cells.addClassOnOver('fc-state-hover');
20239 var cells = this.cells.elements;
20240 var textEls = this.textNodes;
20242 Roo.each(cells, function(cell){
20243 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20246 days += startingPos;
20248 // convert everything to numbers so it's fast
20249 var day = 86400000;
20250 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20253 //Roo.log(prevStart);
20255 var today = new Date().clearTime().getTime();
20256 var sel = date.clearTime().getTime();
20257 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20258 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20259 var ddMatch = this.disabledDatesRE;
20260 var ddText = this.disabledDatesText;
20261 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20262 var ddaysText = this.disabledDaysText;
20263 var format = this.format;
20265 var setCellClass = function(cal, cell){
20269 //Roo.log('set Cell Class');
20271 var t = d.getTime();
20275 cell.dateValue = t;
20277 cell.className += " fc-today";
20278 cell.className += " fc-state-highlight";
20279 cell.title = cal.todayText;
20282 // disable highlight in other month..
20283 //cell.className += " fc-state-highlight";
20288 cell.className = " fc-state-disabled";
20289 cell.title = cal.minText;
20293 cell.className = " fc-state-disabled";
20294 cell.title = cal.maxText;
20298 if(ddays.indexOf(d.getDay()) != -1){
20299 cell.title = ddaysText;
20300 cell.className = " fc-state-disabled";
20303 if(ddMatch && format){
20304 var fvalue = d.dateFormat(format);
20305 if(ddMatch.test(fvalue)){
20306 cell.title = ddText.replace("%0", fvalue);
20307 cell.className = " fc-state-disabled";
20311 if (!cell.initialClassName) {
20312 cell.initialClassName = cell.dom.className;
20315 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20320 for(; i < startingPos; i++) {
20321 textEls[i].innerHTML = (++prevStart);
20322 d.setDate(d.getDate()+1);
20324 cells[i].className = "fc-past fc-other-month";
20325 setCellClass(this, cells[i]);
20330 for(; i < days; i++){
20331 intDay = i - startingPos + 1;
20332 textEls[i].innerHTML = (intDay);
20333 d.setDate(d.getDate()+1);
20335 cells[i].className = ''; // "x-date-active";
20336 setCellClass(this, cells[i]);
20340 for(; i < 42; i++) {
20341 textEls[i].innerHTML = (++extraDays);
20342 d.setDate(d.getDate()+1);
20344 cells[i].className = "fc-future fc-other-month";
20345 setCellClass(this, cells[i]);
20348 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20350 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20352 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20353 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20355 if(totalRows != 6){
20356 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20357 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20360 this.fireEvent('monthchange', this, date);
20364 if(!this.internalRender){
20365 var main = this.el.dom.firstChild;
20366 var w = main.offsetWidth;
20367 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20368 Roo.fly(main).setWidth(w);
20369 this.internalRender = true;
20370 // opera does not respect the auto grow header center column
20371 // then, after it gets a width opera refuses to recalculate
20372 // without a second pass
20373 if(Roo.isOpera && !this.secondPass){
20374 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20375 this.secondPass = true;
20376 this.update.defer(10, this, [date]);
20383 findCell : function(dt) {
20384 dt = dt.clearTime().getTime();
20386 this.cells.each(function(c){
20387 //Roo.log("check " +c.dateValue + '?=' + dt);
20388 if(c.dateValue == dt){
20398 findCells : function(ev) {
20399 var s = ev.start.clone().clearTime().getTime();
20401 var e= ev.end.clone().clearTime().getTime();
20404 this.cells.each(function(c){
20405 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20407 if(c.dateValue > e){
20410 if(c.dateValue < s){
20419 // findBestRow: function(cells)
20423 // for (var i =0 ; i < cells.length;i++) {
20424 // ret = Math.max(cells[i].rows || 0,ret);
20431 addItem : function(ev)
20433 // look for vertical location slot in
20434 var cells = this.findCells(ev);
20436 // ev.row = this.findBestRow(cells);
20438 // work out the location.
20442 for(var i =0; i < cells.length; i++) {
20444 cells[i].row = cells[0].row;
20447 cells[i].row = cells[i].row + 1;
20457 if (crow.start.getY() == cells[i].getY()) {
20459 crow.end = cells[i];
20476 cells[0].events.push(ev);
20478 this.calevents.push(ev);
20481 clearEvents: function() {
20483 if(!this.calevents){
20487 Roo.each(this.cells.elements, function(c){
20493 Roo.each(this.calevents, function(e) {
20494 Roo.each(e.els, function(el) {
20495 el.un('mouseenter' ,this.onEventEnter, this);
20496 el.un('mouseleave' ,this.onEventLeave, this);
20501 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20507 renderEvents: function()
20511 this.cells.each(function(c) {
20520 if(c.row != c.events.length){
20521 r = 4 - (4 - (c.row - c.events.length));
20524 c.events = ev.slice(0, r);
20525 c.more = ev.slice(r);
20527 if(c.more.length && c.more.length == 1){
20528 c.events.push(c.more.pop());
20531 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20535 this.cells.each(function(c) {
20537 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20540 for (var e = 0; e < c.events.length; e++){
20541 var ev = c.events[e];
20542 var rows = ev.rows;
20544 for(var i = 0; i < rows.length; i++) {
20546 // how many rows should it span..
20549 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20550 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20552 unselectable : "on",
20555 cls: 'fc-event-inner',
20559 // cls: 'fc-event-time',
20560 // html : cells.length > 1 ? '' : ev.time
20564 cls: 'fc-event-title',
20565 html : String.format('{0}', ev.title)
20572 cls: 'ui-resizable-handle ui-resizable-e',
20573 html : '  '
20580 cfg.cls += ' fc-event-start';
20582 if ((i+1) == rows.length) {
20583 cfg.cls += ' fc-event-end';
20586 var ctr = _this.el.select('.fc-event-container',true).first();
20587 var cg = ctr.createChild(cfg);
20589 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20590 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20592 var r = (c.more.length) ? 1 : 0;
20593 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20594 cg.setWidth(ebox.right - sbox.x -2);
20596 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20597 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20598 cg.on('click', _this.onEventClick, _this, ev);
20609 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20610 style : 'position: absolute',
20611 unselectable : "on",
20614 cls: 'fc-event-inner',
20618 cls: 'fc-event-title',
20626 cls: 'ui-resizable-handle ui-resizable-e',
20627 html : '  '
20633 var ctr = _this.el.select('.fc-event-container',true).first();
20634 var cg = ctr.createChild(cfg);
20636 var sbox = c.select('.fc-day-content',true).first().getBox();
20637 var ebox = c.select('.fc-day-content',true).first().getBox();
20639 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20640 cg.setWidth(ebox.right - sbox.x -2);
20642 cg.on('click', _this.onMoreEventClick, _this, c.more);
20652 onEventEnter: function (e, el,event,d) {
20653 this.fireEvent('evententer', this, el, event);
20656 onEventLeave: function (e, el,event,d) {
20657 this.fireEvent('eventleave', this, el, event);
20660 onEventClick: function (e, el,event,d) {
20661 this.fireEvent('eventclick', this, el, event);
20664 onMonthChange: function () {
20668 onMoreEventClick: function(e, el, more)
20672 this.calpopover.placement = 'right';
20673 this.calpopover.setTitle('More');
20675 this.calpopover.setContent('');
20677 var ctr = this.calpopover.el.select('.popover-content', true).first();
20679 Roo.each(more, function(m){
20681 cls : 'fc-event-hori fc-event-draggable',
20684 var cg = ctr.createChild(cfg);
20686 cg.on('click', _this.onEventClick, _this, m);
20689 this.calpopover.show(el);
20694 onLoad: function ()
20696 this.calevents = [];
20699 if(this.store.getCount() > 0){
20700 this.store.data.each(function(d){
20703 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20704 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20705 time : d.data.start_time,
20706 title : d.data.title,
20707 description : d.data.description,
20708 venue : d.data.venue
20713 this.renderEvents();
20715 if(this.calevents.length && this.loadMask){
20716 this.maskEl.hide();
20720 onBeforeLoad: function()
20722 this.clearEvents();
20724 this.maskEl.show();
20738 * @class Roo.bootstrap.Popover
20739 * @extends Roo.bootstrap.Component
20740 * Bootstrap Popover class
20741 * @cfg {String} html contents of the popover (or false to use children..)
20742 * @cfg {String} title of popover (or false to hide)
20743 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20744 * @cfg {String} trigger click || hover (or false to trigger manually)
20745 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20746 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20747 * - if false and it has a 'parent' then it will be automatically added to that element
20748 * - if string - Roo.get will be called
20749 * @cfg {Number} delay - delay before showing
20752 * Create a new Popover
20753 * @param {Object} config The config object
20756 Roo.bootstrap.Popover = function(config){
20757 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20763 * After the popover show
20765 * @param {Roo.bootstrap.Popover} this
20770 * After the popover hide
20772 * @param {Roo.bootstrap.Popover} this
20778 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20783 placement : 'right',
20784 trigger : 'hover', // hover
20790 can_build_overlaid : false,
20792 maskEl : false, // the mask element
20795 alignEl : false, // when show is called with an element - this get's stored.
20797 getChildContainer : function()
20799 return this.contentEl;
20802 getPopoverHeader : function()
20804 this.title = true; // flag not to hide it..
20805 this.headerEl.addClass('p-0');
20806 return this.headerEl
20810 getAutoCreate : function(){
20813 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20814 style: 'display:block',
20820 cls : 'popover-inner ',
20824 cls: 'popover-title popover-header',
20825 html : this.title === false ? '' : this.title
20828 cls : 'popover-content popover-body ' + (this.cls || ''),
20829 html : this.html || ''
20840 * @param {string} the title
20842 setTitle: function(str)
20846 this.headerEl.dom.innerHTML = str;
20851 * @param {string} the body content
20853 setContent: function(str)
20856 if (this.contentEl) {
20857 this.contentEl.dom.innerHTML = str;
20861 // as it get's added to the bottom of the page.
20862 onRender : function(ct, position)
20864 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20869 var cfg = Roo.apply({}, this.getAutoCreate());
20873 cfg.cls += ' ' + this.cls;
20876 cfg.style = this.style;
20878 //Roo.log("adding to ");
20879 this.el = Roo.get(document.body).createChild(cfg, position);
20880 // Roo.log(this.el);
20883 this.contentEl = this.el.select('.popover-content',true).first();
20884 this.headerEl = this.el.select('.popover-title',true).first();
20887 if(typeof(this.items) != 'undefined'){
20888 var items = this.items;
20891 for(var i =0;i < items.length;i++) {
20892 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20896 this.items = nitems;
20898 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20899 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20906 resizeMask : function()
20908 this.maskEl.setSize(
20909 Roo.lib.Dom.getViewWidth(true),
20910 Roo.lib.Dom.getViewHeight(true)
20914 initEvents : function()
20918 Roo.bootstrap.Popover.register(this);
20921 this.arrowEl = this.el.select('.arrow',true).first();
20922 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20923 this.el.enableDisplayMode('block');
20927 if (this.over === false && !this.parent()) {
20930 if (this.triggers === false) {
20935 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20936 var triggers = this.trigger ? this.trigger.split(' ') : [];
20937 Roo.each(triggers, function(trigger) {
20939 if (trigger == 'click') {
20940 on_el.on('click', this.toggle, this);
20941 } else if (trigger != 'manual') {
20942 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20943 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20945 on_el.on(eventIn ,this.enter, this);
20946 on_el.on(eventOut, this.leave, this);
20956 toggle : function () {
20957 this.hoverState == 'in' ? this.leave() : this.enter();
20960 enter : function () {
20962 clearTimeout(this.timeout);
20964 this.hoverState = 'in';
20966 if (!this.delay || !this.delay.show) {
20971 this.timeout = setTimeout(function () {
20972 if (_t.hoverState == 'in') {
20975 }, this.delay.show)
20978 leave : function() {
20979 clearTimeout(this.timeout);
20981 this.hoverState = 'out';
20983 if (!this.delay || !this.delay.hide) {
20988 this.timeout = setTimeout(function () {
20989 if (_t.hoverState == 'out') {
20992 }, this.delay.hide)
20996 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20997 * @param {string} (left|right|top|bottom) position
20999 show : function (on_el, placement)
21001 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21002 on_el = on_el || false; // default to false
21005 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21006 on_el = this.parent().el;
21007 } else if (this.over) {
21008 on_el = Roo.get(this.over);
21013 this.alignEl = Roo.get( on_el );
21016 this.render(document.body);
21022 if (this.title === false) {
21023 this.headerEl.hide();
21028 this.el.dom.style.display = 'block';
21031 if (this.alignEl) {
21032 this.updatePosition(this.placement, true);
21035 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21036 var es = this.el.getSize();
21037 var x = Roo.lib.Dom.getViewWidth()/2;
21038 var y = Roo.lib.Dom.getViewHeight()/2;
21039 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21044 //var arrow = this.el.select('.arrow',true).first();
21045 //arrow.set(align[2],
21047 this.el.addClass('in');
21051 this.hoverState = 'in';
21054 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21055 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21056 this.maskEl.dom.style.display = 'block';
21057 this.maskEl.addClass('show');
21059 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21061 this.fireEvent('show', this);
21065 * fire this manually after loading a grid in the table for example
21066 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21067 * @param {Boolean} try and move it if we cant get right position.
21069 updatePosition : function(placement, try_move)
21071 // allow for calling with no parameters
21072 placement = placement ? placement : this.placement;
21073 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21075 this.el.removeClass([
21076 'fade','top','bottom', 'left', 'right','in',
21077 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21079 this.el.addClass(placement + ' bs-popover-' + placement);
21081 if (!this.alignEl ) {
21085 switch (placement) {
21087 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21088 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21089 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21090 //normal display... or moved up/down.
21091 this.el.setXY(offset);
21092 var xy = this.alignEl.getAnchorXY('tr', false);
21094 this.arrowEl.setXY(xy);
21097 // continue through...
21098 return this.updatePosition('left', false);
21102 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21103 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21104 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21105 //normal display... or moved up/down.
21106 this.el.setXY(offset);
21107 var xy = this.alignEl.getAnchorXY('tl', false);
21108 xy[0]-=10;xy[1]+=5; // << fix me
21109 this.arrowEl.setXY(xy);
21113 return this.updatePosition('right', false);
21116 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21117 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21118 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21119 //normal display... or moved up/down.
21120 this.el.setXY(offset);
21121 var xy = this.alignEl.getAnchorXY('t', false);
21122 xy[1]-=10; // << fix me
21123 this.arrowEl.setXY(xy);
21127 return this.updatePosition('bottom', false);
21130 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21131 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21132 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21133 //normal display... or moved up/down.
21134 this.el.setXY(offset);
21135 var xy = this.alignEl.getAnchorXY('b', false);
21136 xy[1]+=2; // << fix me
21137 this.arrowEl.setXY(xy);
21141 return this.updatePosition('top', false);
21152 this.el.setXY([0,0]);
21153 this.el.removeClass('in');
21155 this.hoverState = null;
21156 this.maskEl.hide(); // always..
21157 this.fireEvent('hide', this);
21163 Roo.apply(Roo.bootstrap.Popover, {
21166 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21167 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21168 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21169 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21174 clickHander : false,
21178 onMouseDown : function(e)
21180 if (this.popups.length && !e.getTarget(".roo-popover")) {
21181 /// what is nothing is showing..
21190 register : function(popup)
21192 if (!Roo.bootstrap.Popover.clickHandler) {
21193 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21195 // hide other popups.
21196 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21197 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21198 this.hideAll(); //<< why?
21199 //this.popups.push(popup);
21201 hideAll : function()
21203 this.popups.forEach(function(p) {
21207 onShow : function() {
21208 Roo.bootstrap.Popover.popups.push(this);
21210 onHide : function() {
21211 Roo.bootstrap.Popover.popups.remove(this);
21217 * Card header - holder for the card header elements.
21222 * @class Roo.bootstrap.PopoverNav
21223 * @extends Roo.bootstrap.NavGroup
21224 * Bootstrap Popover header navigation class
21226 * Create a new Popover Header Navigation
21227 * @param {Object} config The config object
21230 Roo.bootstrap.PopoverNav = function(config){
21231 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21234 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21237 container_method : 'getPopoverHeader'
21255 * @class Roo.bootstrap.Progress
21256 * @extends Roo.bootstrap.Component
21257 * Bootstrap Progress class
21258 * @cfg {Boolean} striped striped of the progress bar
21259 * @cfg {Boolean} active animated of the progress bar
21263 * Create a new Progress
21264 * @param {Object} config The config object
21267 Roo.bootstrap.Progress = function(config){
21268 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21271 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21276 getAutoCreate : function(){
21284 cfg.cls += ' progress-striped';
21288 cfg.cls += ' active';
21307 * @class Roo.bootstrap.ProgressBar
21308 * @extends Roo.bootstrap.Component
21309 * Bootstrap ProgressBar class
21310 * @cfg {Number} aria_valuenow aria-value now
21311 * @cfg {Number} aria_valuemin aria-value min
21312 * @cfg {Number} aria_valuemax aria-value max
21313 * @cfg {String} label label for the progress bar
21314 * @cfg {String} panel (success | info | warning | danger )
21315 * @cfg {String} role role of the progress bar
21316 * @cfg {String} sr_only text
21320 * Create a new ProgressBar
21321 * @param {Object} config The config object
21324 Roo.bootstrap.ProgressBar = function(config){
21325 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21328 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21332 aria_valuemax : 100,
21338 getAutoCreate : function()
21343 cls: 'progress-bar',
21344 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21356 cfg.role = this.role;
21359 if(this.aria_valuenow){
21360 cfg['aria-valuenow'] = this.aria_valuenow;
21363 if(this.aria_valuemin){
21364 cfg['aria-valuemin'] = this.aria_valuemin;
21367 if(this.aria_valuemax){
21368 cfg['aria-valuemax'] = this.aria_valuemax;
21371 if(this.label && !this.sr_only){
21372 cfg.html = this.label;
21376 cfg.cls += ' progress-bar-' + this.panel;
21382 update : function(aria_valuenow)
21384 this.aria_valuenow = aria_valuenow;
21386 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21401 * @class Roo.bootstrap.TabGroup
21402 * @extends Roo.bootstrap.Column
21403 * Bootstrap Column class
21404 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21405 * @cfg {Boolean} carousel true to make the group behave like a carousel
21406 * @cfg {Boolean} bullets show bullets for the panels
21407 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21408 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21409 * @cfg {Boolean} showarrow (true|false) show arrow default true
21412 * Create a new TabGroup
21413 * @param {Object} config The config object
21416 Roo.bootstrap.TabGroup = function(config){
21417 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21419 this.navId = Roo.id();
21422 Roo.bootstrap.TabGroup.register(this);
21426 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21429 transition : false,
21434 slideOnTouch : false,
21437 getAutoCreate : function()
21439 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21441 cfg.cls += ' tab-content';
21443 if (this.carousel) {
21444 cfg.cls += ' carousel slide';
21447 cls : 'carousel-inner',
21451 if(this.bullets && !Roo.isTouch){
21454 cls : 'carousel-bullets',
21458 if(this.bullets_cls){
21459 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21466 cfg.cn[0].cn.push(bullets);
21469 if(this.showarrow){
21470 cfg.cn[0].cn.push({
21472 class : 'carousel-arrow',
21476 class : 'carousel-prev',
21480 class : 'fa fa-chevron-left'
21486 class : 'carousel-next',
21490 class : 'fa fa-chevron-right'
21503 initEvents: function()
21505 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21506 // this.el.on("touchstart", this.onTouchStart, this);
21509 if(this.autoslide){
21512 this.slideFn = window.setInterval(function() {
21513 _this.showPanelNext();
21517 if(this.showarrow){
21518 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21519 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21525 // onTouchStart : function(e, el, o)
21527 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21531 // this.showPanelNext();
21535 getChildContainer : function()
21537 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21541 * register a Navigation item
21542 * @param {Roo.bootstrap.NavItem} the navitem to add
21544 register : function(item)
21546 this.tabs.push( item);
21547 item.navId = this.navId; // not really needed..
21552 getActivePanel : function()
21555 Roo.each(this.tabs, function(t) {
21565 getPanelByName : function(n)
21568 Roo.each(this.tabs, function(t) {
21569 if (t.tabId == n) {
21577 indexOfPanel : function(p)
21580 Roo.each(this.tabs, function(t,i) {
21581 if (t.tabId == p.tabId) {
21590 * show a specific panel
21591 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21592 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21594 showPanel : function (pan)
21596 if(this.transition || typeof(pan) == 'undefined'){
21597 Roo.log("waiting for the transitionend");
21601 if (typeof(pan) == 'number') {
21602 pan = this.tabs[pan];
21605 if (typeof(pan) == 'string') {
21606 pan = this.getPanelByName(pan);
21609 var cur = this.getActivePanel();
21612 Roo.log('pan or acitve pan is undefined');
21616 if (pan.tabId == this.getActivePanel().tabId) {
21620 if (false === cur.fireEvent('beforedeactivate')) {
21624 if(this.bullets > 0 && !Roo.isTouch){
21625 this.setActiveBullet(this.indexOfPanel(pan));
21628 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21630 //class="carousel-item carousel-item-next carousel-item-left"
21632 this.transition = true;
21633 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21634 var lr = dir == 'next' ? 'left' : 'right';
21635 pan.el.addClass(dir); // or prev
21636 pan.el.addClass('carousel-item-' + dir); // or prev
21637 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21638 cur.el.addClass(lr); // or right
21639 pan.el.addClass(lr);
21640 cur.el.addClass('carousel-item-' +lr); // or right
21641 pan.el.addClass('carousel-item-' +lr);
21645 cur.el.on('transitionend', function() {
21646 Roo.log("trans end?");
21648 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21649 pan.setActive(true);
21651 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21652 cur.setActive(false);
21654 _this.transition = false;
21656 }, this, { single: true } );
21661 cur.setActive(false);
21662 pan.setActive(true);
21667 showPanelNext : function()
21669 var i = this.indexOfPanel(this.getActivePanel());
21671 if (i >= this.tabs.length - 1 && !this.autoslide) {
21675 if (i >= this.tabs.length - 1 && this.autoslide) {
21679 this.showPanel(this.tabs[i+1]);
21682 showPanelPrev : function()
21684 var i = this.indexOfPanel(this.getActivePanel());
21686 if (i < 1 && !this.autoslide) {
21690 if (i < 1 && this.autoslide) {
21691 i = this.tabs.length;
21694 this.showPanel(this.tabs[i-1]);
21698 addBullet: function()
21700 if(!this.bullets || Roo.isTouch){
21703 var ctr = this.el.select('.carousel-bullets',true).first();
21704 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21705 var bullet = ctr.createChild({
21706 cls : 'bullet bullet-' + i
21707 },ctr.dom.lastChild);
21712 bullet.on('click', (function(e, el, o, ii, t){
21714 e.preventDefault();
21716 this.showPanel(ii);
21718 if(this.autoslide && this.slideFn){
21719 clearInterval(this.slideFn);
21720 this.slideFn = window.setInterval(function() {
21721 _this.showPanelNext();
21725 }).createDelegate(this, [i, bullet], true));
21730 setActiveBullet : function(i)
21736 Roo.each(this.el.select('.bullet', true).elements, function(el){
21737 el.removeClass('selected');
21740 var bullet = this.el.select('.bullet-' + i, true).first();
21746 bullet.addClass('selected');
21757 Roo.apply(Roo.bootstrap.TabGroup, {
21761 * register a Navigation Group
21762 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21764 register : function(navgrp)
21766 this.groups[navgrp.navId] = navgrp;
21770 * fetch a Navigation Group based on the navigation ID
21771 * if one does not exist , it will get created.
21772 * @param {string} the navgroup to add
21773 * @returns {Roo.bootstrap.NavGroup} the navgroup
21775 get: function(navId) {
21776 if (typeof(this.groups[navId]) == 'undefined') {
21777 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21779 return this.groups[navId] ;
21794 * @class Roo.bootstrap.TabPanel
21795 * @extends Roo.bootstrap.Component
21796 * Bootstrap TabPanel class
21797 * @cfg {Boolean} active panel active
21798 * @cfg {String} html panel content
21799 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21800 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21801 * @cfg {String} href click to link..
21802 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21806 * Create a new TabPanel
21807 * @param {Object} config The config object
21810 Roo.bootstrap.TabPanel = function(config){
21811 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21815 * Fires when the active status changes
21816 * @param {Roo.bootstrap.TabPanel} this
21817 * @param {Boolean} state the new state
21822 * @event beforedeactivate
21823 * Fires before a tab is de-activated - can be used to do validation on a form.
21824 * @param {Roo.bootstrap.TabPanel} this
21825 * @return {Boolean} false if there is an error
21828 'beforedeactivate': true
21831 this.tabId = this.tabId || Roo.id();
21835 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21842 touchSlide : false,
21843 getAutoCreate : function(){
21848 // item is needed for carousel - not sure if it has any effect otherwise
21849 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21850 html: this.html || ''
21854 cfg.cls += ' active';
21858 cfg.tabId = this.tabId;
21866 initEvents: function()
21868 var p = this.parent();
21870 this.navId = this.navId || p.navId;
21872 if (typeof(this.navId) != 'undefined') {
21873 // not really needed.. but just in case.. parent should be a NavGroup.
21874 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21878 var i = tg.tabs.length - 1;
21880 if(this.active && tg.bullets > 0 && i < tg.bullets){
21881 tg.setActiveBullet(i);
21885 this.el.on('click', this.onClick, this);
21887 if(Roo.isTouch && this.touchSlide){
21888 this.el.on("touchstart", this.onTouchStart, this);
21889 this.el.on("touchmove", this.onTouchMove, this);
21890 this.el.on("touchend", this.onTouchEnd, this);
21895 onRender : function(ct, position)
21897 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21900 setActive : function(state)
21902 Roo.log("panel - set active " + this.tabId + "=" + state);
21904 this.active = state;
21906 this.el.removeClass('active');
21908 } else if (!this.el.hasClass('active')) {
21909 this.el.addClass('active');
21912 this.fireEvent('changed', this, state);
21915 onClick : function(e)
21917 e.preventDefault();
21919 if(!this.href.length){
21923 window.location.href = this.href;
21932 onTouchStart : function(e)
21934 this.swiping = false;
21936 this.startX = e.browserEvent.touches[0].clientX;
21937 this.startY = e.browserEvent.touches[0].clientY;
21940 onTouchMove : function(e)
21942 this.swiping = true;
21944 this.endX = e.browserEvent.touches[0].clientX;
21945 this.endY = e.browserEvent.touches[0].clientY;
21948 onTouchEnd : function(e)
21955 var tabGroup = this.parent();
21957 if(this.endX > this.startX){ // swiping right
21958 tabGroup.showPanelPrev();
21962 if(this.startX > this.endX){ // swiping left
21963 tabGroup.showPanelNext();
21982 * @class Roo.bootstrap.DateField
21983 * @extends Roo.bootstrap.Input
21984 * Bootstrap DateField class
21985 * @cfg {Number} weekStart default 0
21986 * @cfg {String} viewMode default empty, (months|years)
21987 * @cfg {String} minViewMode default empty, (months|years)
21988 * @cfg {Number} startDate default -Infinity
21989 * @cfg {Number} endDate default Infinity
21990 * @cfg {Boolean} todayHighlight default false
21991 * @cfg {Boolean} todayBtn default false
21992 * @cfg {Boolean} calendarWeeks default false
21993 * @cfg {Object} daysOfWeekDisabled default empty
21994 * @cfg {Boolean} singleMode default false (true | false)
21996 * @cfg {Boolean} keyboardNavigation default true
21997 * @cfg {String} language default en
22000 * Create a new DateField
22001 * @param {Object} config The config object
22004 Roo.bootstrap.DateField = function(config){
22005 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22009 * Fires when this field show.
22010 * @param {Roo.bootstrap.DateField} this
22011 * @param {Mixed} date The date value
22016 * Fires when this field hide.
22017 * @param {Roo.bootstrap.DateField} this
22018 * @param {Mixed} date The date value
22023 * Fires when select a date.
22024 * @param {Roo.bootstrap.DateField} this
22025 * @param {Mixed} date The date value
22029 * @event beforeselect
22030 * Fires when before select a date.
22031 * @param {Roo.bootstrap.DateField} this
22032 * @param {Mixed} date The date value
22034 beforeselect : true
22038 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22041 * @cfg {String} format
22042 * The default date format string which can be overriden for localization support. The format must be
22043 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22047 * @cfg {String} altFormats
22048 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22049 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22051 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22059 todayHighlight : false,
22065 keyboardNavigation: true,
22067 calendarWeeks: false,
22069 startDate: -Infinity,
22073 daysOfWeekDisabled: [],
22077 singleMode : false,
22079 UTCDate: function()
22081 return new Date(Date.UTC.apply(Date, arguments));
22084 UTCToday: function()
22086 var today = new Date();
22087 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22090 getDate: function() {
22091 var d = this.getUTCDate();
22092 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22095 getUTCDate: function() {
22099 setDate: function(d) {
22100 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22103 setUTCDate: function(d) {
22105 this.setValue(this.formatDate(this.date));
22108 onRender: function(ct, position)
22111 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22113 this.language = this.language || 'en';
22114 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22115 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22117 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22118 this.format = this.format || 'm/d/y';
22119 this.isInline = false;
22120 this.isInput = true;
22121 this.component = this.el.select('.add-on', true).first() || false;
22122 this.component = (this.component && this.component.length === 0) ? false : this.component;
22123 this.hasInput = this.component && this.inputEl().length;
22125 if (typeof(this.minViewMode === 'string')) {
22126 switch (this.minViewMode) {
22128 this.minViewMode = 1;
22131 this.minViewMode = 2;
22134 this.minViewMode = 0;
22139 if (typeof(this.viewMode === 'string')) {
22140 switch (this.viewMode) {
22153 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22155 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22157 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22159 this.picker().on('mousedown', this.onMousedown, this);
22160 this.picker().on('click', this.onClick, this);
22162 this.picker().addClass('datepicker-dropdown');
22164 this.startViewMode = this.viewMode;
22166 if(this.singleMode){
22167 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22168 v.setVisibilityMode(Roo.Element.DISPLAY);
22172 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22173 v.setStyle('width', '189px');
22177 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22178 if(!this.calendarWeeks){
22183 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22184 v.attr('colspan', function(i, val){
22185 return parseInt(val) + 1;
22190 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22192 this.setStartDate(this.startDate);
22193 this.setEndDate(this.endDate);
22195 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22202 if(this.isInline) {
22207 picker : function()
22209 return this.pickerEl;
22210 // return this.el.select('.datepicker', true).first();
22213 fillDow: function()
22215 var dowCnt = this.weekStart;
22224 if(this.calendarWeeks){
22232 while (dowCnt < this.weekStart + 7) {
22236 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22240 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22243 fillMonths: function()
22246 var months = this.picker().select('>.datepicker-months td', true).first();
22248 months.dom.innerHTML = '';
22254 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22257 months.createChild(month);
22264 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;
22266 if (this.date < this.startDate) {
22267 this.viewDate = new Date(this.startDate);
22268 } else if (this.date > this.endDate) {
22269 this.viewDate = new Date(this.endDate);
22271 this.viewDate = new Date(this.date);
22279 var d = new Date(this.viewDate),
22280 year = d.getUTCFullYear(),
22281 month = d.getUTCMonth(),
22282 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22283 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22284 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22285 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22286 currentDate = this.date && this.date.valueOf(),
22287 today = this.UTCToday();
22289 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22291 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22293 // this.picker.select('>tfoot th.today').
22294 // .text(dates[this.language].today)
22295 // .toggle(this.todayBtn !== false);
22297 this.updateNavArrows();
22300 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22302 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22304 prevMonth.setUTCDate(day);
22306 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22308 var nextMonth = new Date(prevMonth);
22310 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22312 nextMonth = nextMonth.valueOf();
22314 var fillMonths = false;
22316 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22318 while(prevMonth.valueOf() <= nextMonth) {
22321 if (prevMonth.getUTCDay() === this.weekStart) {
22323 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22331 if(this.calendarWeeks){
22332 // ISO 8601: First week contains first thursday.
22333 // ISO also states week starts on Monday, but we can be more abstract here.
22335 // Start of current week: based on weekstart/current date
22336 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22337 // Thursday of this week
22338 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22339 // First Thursday of year, year from thursday
22340 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22341 // Calendar week: ms between thursdays, div ms per day, div 7 days
22342 calWeek = (th - yth) / 864e5 / 7 + 1;
22344 fillMonths.cn.push({
22352 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22354 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22357 if (this.todayHighlight &&
22358 prevMonth.getUTCFullYear() == today.getFullYear() &&
22359 prevMonth.getUTCMonth() == today.getMonth() &&
22360 prevMonth.getUTCDate() == today.getDate()) {
22361 clsName += ' today';
22364 if (currentDate && prevMonth.valueOf() === currentDate) {
22365 clsName += ' active';
22368 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22369 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22370 clsName += ' disabled';
22373 fillMonths.cn.push({
22375 cls: 'day ' + clsName,
22376 html: prevMonth.getDate()
22379 prevMonth.setDate(prevMonth.getDate()+1);
22382 var currentYear = this.date && this.date.getUTCFullYear();
22383 var currentMonth = this.date && this.date.getUTCMonth();
22385 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22387 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22388 v.removeClass('active');
22390 if(currentYear === year && k === currentMonth){
22391 v.addClass('active');
22394 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22395 v.addClass('disabled');
22401 year = parseInt(year/10, 10) * 10;
22403 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22405 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22408 for (var i = -1; i < 11; i++) {
22409 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22411 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22419 showMode: function(dir)
22422 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22425 Roo.each(this.picker().select('>div',true).elements, function(v){
22426 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22429 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22434 if(this.isInline) {
22438 this.picker().removeClass(['bottom', 'top']);
22440 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22442 * place to the top of element!
22446 this.picker().addClass('top');
22447 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22452 this.picker().addClass('bottom');
22454 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22457 parseDate : function(value)
22459 if(!value || value instanceof Date){
22462 var v = Date.parseDate(value, this.format);
22463 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22464 v = Date.parseDate(value, 'Y-m-d');
22466 if(!v && this.altFormats){
22467 if(!this.altFormatsArray){
22468 this.altFormatsArray = this.altFormats.split("|");
22470 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22471 v = Date.parseDate(value, this.altFormatsArray[i]);
22477 formatDate : function(date, fmt)
22479 return (!date || !(date instanceof Date)) ?
22480 date : date.dateFormat(fmt || this.format);
22483 onFocus : function()
22485 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22489 onBlur : function()
22491 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22493 var d = this.inputEl().getValue();
22500 showPopup : function()
22502 this.picker().show();
22506 this.fireEvent('showpopup', this, this.date);
22509 hidePopup : function()
22511 if(this.isInline) {
22514 this.picker().hide();
22515 this.viewMode = this.startViewMode;
22518 this.fireEvent('hidepopup', this, this.date);
22522 onMousedown: function(e)
22524 e.stopPropagation();
22525 e.preventDefault();
22530 Roo.bootstrap.DateField.superclass.keyup.call(this);
22534 setValue: function(v)
22536 if(this.fireEvent('beforeselect', this, v) !== false){
22537 var d = new Date(this.parseDate(v) ).clearTime();
22539 if(isNaN(d.getTime())){
22540 this.date = this.viewDate = '';
22541 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22545 v = this.formatDate(d);
22547 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22549 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22553 this.fireEvent('select', this, this.date);
22557 getValue: function()
22559 return this.formatDate(this.date);
22562 fireKey: function(e)
22564 if (!this.picker().isVisible()){
22565 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22571 var dateChanged = false,
22573 newDate, newViewDate;
22578 e.preventDefault();
22582 if (!this.keyboardNavigation) {
22585 dir = e.keyCode == 37 ? -1 : 1;
22588 newDate = this.moveYear(this.date, dir);
22589 newViewDate = this.moveYear(this.viewDate, dir);
22590 } else if (e.shiftKey){
22591 newDate = this.moveMonth(this.date, dir);
22592 newViewDate = this.moveMonth(this.viewDate, dir);
22594 newDate = new Date(this.date);
22595 newDate.setUTCDate(this.date.getUTCDate() + dir);
22596 newViewDate = new Date(this.viewDate);
22597 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22599 if (this.dateWithinRange(newDate)){
22600 this.date = newDate;
22601 this.viewDate = newViewDate;
22602 this.setValue(this.formatDate(this.date));
22604 e.preventDefault();
22605 dateChanged = true;
22610 if (!this.keyboardNavigation) {
22613 dir = e.keyCode == 38 ? -1 : 1;
22615 newDate = this.moveYear(this.date, dir);
22616 newViewDate = this.moveYear(this.viewDate, dir);
22617 } else if (e.shiftKey){
22618 newDate = this.moveMonth(this.date, dir);
22619 newViewDate = this.moveMonth(this.viewDate, dir);
22621 newDate = new Date(this.date);
22622 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22623 newViewDate = new Date(this.viewDate);
22624 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22626 if (this.dateWithinRange(newDate)){
22627 this.date = newDate;
22628 this.viewDate = newViewDate;
22629 this.setValue(this.formatDate(this.date));
22631 e.preventDefault();
22632 dateChanged = true;
22636 this.setValue(this.formatDate(this.date));
22638 e.preventDefault();
22641 this.setValue(this.formatDate(this.date));
22655 onClick: function(e)
22657 e.stopPropagation();
22658 e.preventDefault();
22660 var target = e.getTarget();
22662 if(target.nodeName.toLowerCase() === 'i'){
22663 target = Roo.get(target).dom.parentNode;
22666 var nodeName = target.nodeName;
22667 var className = target.className;
22668 var html = target.innerHTML;
22669 //Roo.log(nodeName);
22671 switch(nodeName.toLowerCase()) {
22673 switch(className) {
22679 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22680 switch(this.viewMode){
22682 this.viewDate = this.moveMonth(this.viewDate, dir);
22686 this.viewDate = this.moveYear(this.viewDate, dir);
22692 var date = new Date();
22693 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22695 this.setValue(this.formatDate(this.date));
22702 if (className.indexOf('disabled') < 0) {
22703 if (!this.viewDate) {
22704 this.viewDate = new Date();
22706 this.viewDate.setUTCDate(1);
22707 if (className.indexOf('month') > -1) {
22708 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22710 var year = parseInt(html, 10) || 0;
22711 this.viewDate.setUTCFullYear(year);
22715 if(this.singleMode){
22716 this.setValue(this.formatDate(this.viewDate));
22727 //Roo.log(className);
22728 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22729 var day = parseInt(html, 10) || 1;
22730 var year = (this.viewDate || new Date()).getUTCFullYear(),
22731 month = (this.viewDate || new Date()).getUTCMonth();
22733 if (className.indexOf('old') > -1) {
22740 } else if (className.indexOf('new') > -1) {
22748 //Roo.log([year,month,day]);
22749 this.date = this.UTCDate(year, month, day,0,0,0,0);
22750 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22752 //Roo.log(this.formatDate(this.date));
22753 this.setValue(this.formatDate(this.date));
22760 setStartDate: function(startDate)
22762 this.startDate = startDate || -Infinity;
22763 if (this.startDate !== -Infinity) {
22764 this.startDate = this.parseDate(this.startDate);
22767 this.updateNavArrows();
22770 setEndDate: function(endDate)
22772 this.endDate = endDate || Infinity;
22773 if (this.endDate !== Infinity) {
22774 this.endDate = this.parseDate(this.endDate);
22777 this.updateNavArrows();
22780 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22782 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22783 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22784 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22786 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22787 return parseInt(d, 10);
22790 this.updateNavArrows();
22793 updateNavArrows: function()
22795 if(this.singleMode){
22799 var d = new Date(this.viewDate),
22800 year = d.getUTCFullYear(),
22801 month = d.getUTCMonth();
22803 Roo.each(this.picker().select('.prev', true).elements, function(v){
22805 switch (this.viewMode) {
22808 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22814 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22821 Roo.each(this.picker().select('.next', true).elements, function(v){
22823 switch (this.viewMode) {
22826 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22832 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22840 moveMonth: function(date, dir)
22845 var new_date = new Date(date.valueOf()),
22846 day = new_date.getUTCDate(),
22847 month = new_date.getUTCMonth(),
22848 mag = Math.abs(dir),
22850 dir = dir > 0 ? 1 : -1;
22853 // If going back one month, make sure month is not current month
22854 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22856 return new_date.getUTCMonth() == month;
22858 // If going forward one month, make sure month is as expected
22859 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22861 return new_date.getUTCMonth() != new_month;
22863 new_month = month + dir;
22864 new_date.setUTCMonth(new_month);
22865 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22866 if (new_month < 0 || new_month > 11) {
22867 new_month = (new_month + 12) % 12;
22870 // For magnitudes >1, move one month at a time...
22871 for (var i=0; i<mag; i++) {
22872 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22873 new_date = this.moveMonth(new_date, dir);
22875 // ...then reset the day, keeping it in the new month
22876 new_month = new_date.getUTCMonth();
22877 new_date.setUTCDate(day);
22879 return new_month != new_date.getUTCMonth();
22882 // Common date-resetting loop -- if date is beyond end of month, make it
22885 new_date.setUTCDate(--day);
22886 new_date.setUTCMonth(new_month);
22891 moveYear: function(date, dir)
22893 return this.moveMonth(date, dir*12);
22896 dateWithinRange: function(date)
22898 return date >= this.startDate && date <= this.endDate;
22904 this.picker().remove();
22907 validateValue : function(value)
22909 if(this.getVisibilityEl().hasClass('hidden')){
22913 if(value.length < 1) {
22914 if(this.allowBlank){
22920 if(value.length < this.minLength){
22923 if(value.length > this.maxLength){
22927 var vt = Roo.form.VTypes;
22928 if(!vt[this.vtype](value, this)){
22932 if(typeof this.validator == "function"){
22933 var msg = this.validator(value);
22939 if(this.regex && !this.regex.test(value)){
22943 if(typeof(this.parseDate(value)) == 'undefined'){
22947 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22951 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22961 this.date = this.viewDate = '';
22963 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22968 Roo.apply(Roo.bootstrap.DateField, {
22979 html: '<i class="fa fa-arrow-left"/>'
22989 html: '<i class="fa fa-arrow-right"/>'
23031 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23032 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23033 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23034 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23035 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23048 navFnc: 'FullYear',
23053 navFnc: 'FullYear',
23058 Roo.apply(Roo.bootstrap.DateField, {
23062 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23066 cls: 'datepicker-days',
23070 cls: 'table-condensed',
23072 Roo.bootstrap.DateField.head,
23076 Roo.bootstrap.DateField.footer
23083 cls: 'datepicker-months',
23087 cls: 'table-condensed',
23089 Roo.bootstrap.DateField.head,
23090 Roo.bootstrap.DateField.content,
23091 Roo.bootstrap.DateField.footer
23098 cls: 'datepicker-years',
23102 cls: 'table-condensed',
23104 Roo.bootstrap.DateField.head,
23105 Roo.bootstrap.DateField.content,
23106 Roo.bootstrap.DateField.footer
23125 * @class Roo.bootstrap.TimeField
23126 * @extends Roo.bootstrap.Input
23127 * Bootstrap DateField class
23131 * Create a new TimeField
23132 * @param {Object} config The config object
23135 Roo.bootstrap.TimeField = function(config){
23136 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23140 * Fires when this field show.
23141 * @param {Roo.bootstrap.DateField} thisthis
23142 * @param {Mixed} date The date value
23147 * Fires when this field hide.
23148 * @param {Roo.bootstrap.DateField} this
23149 * @param {Mixed} date The date value
23154 * Fires when select a date.
23155 * @param {Roo.bootstrap.DateField} this
23156 * @param {Mixed} date The date value
23162 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23165 * @cfg {String} format
23166 * The default time format string which can be overriden for localization support. The format must be
23167 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23171 getAutoCreate : function()
23173 this.after = '<i class="fa far fa-clock"></i>';
23174 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23178 onRender: function(ct, position)
23181 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23183 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23185 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23187 this.pop = this.picker().select('>.datepicker-time',true).first();
23188 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23190 this.picker().on('mousedown', this.onMousedown, this);
23191 this.picker().on('click', this.onClick, this);
23193 this.picker().addClass('datepicker-dropdown');
23198 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23199 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23200 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23201 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23202 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23203 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23207 fireKey: function(e){
23208 if (!this.picker().isVisible()){
23209 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23215 e.preventDefault();
23223 this.onTogglePeriod();
23226 this.onIncrementMinutes();
23229 this.onDecrementMinutes();
23238 onClick: function(e) {
23239 e.stopPropagation();
23240 e.preventDefault();
23243 picker : function()
23245 return this.pickerEl;
23248 fillTime: function()
23250 var time = this.pop.select('tbody', true).first();
23252 time.dom.innerHTML = '';
23267 cls: 'hours-up fa fas fa-chevron-up'
23287 cls: 'minutes-up fa fas fa-chevron-up'
23308 cls: 'timepicker-hour',
23323 cls: 'timepicker-minute',
23338 cls: 'btn btn-primary period',
23360 cls: 'hours-down fa fas fa-chevron-down'
23380 cls: 'minutes-down fa fas fa-chevron-down'
23398 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23405 var hours = this.time.getHours();
23406 var minutes = this.time.getMinutes();
23419 hours = hours - 12;
23423 hours = '0' + hours;
23427 minutes = '0' + minutes;
23430 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23431 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23432 this.pop.select('button', true).first().dom.innerHTML = period;
23438 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23440 var cls = ['bottom'];
23442 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23449 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23453 //this.picker().setXY(20000,20000);
23454 this.picker().addClass(cls.join('-'));
23458 Roo.each(cls, function(c){
23463 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23464 //_this.picker().setTop(_this.inputEl().getHeight());
23468 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23470 //_this.picker().setTop(0 - _this.picker().getHeight());
23475 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23479 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23487 onFocus : function()
23489 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23493 onBlur : function()
23495 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23501 this.picker().show();
23506 this.fireEvent('show', this, this.date);
23511 this.picker().hide();
23514 this.fireEvent('hide', this, this.date);
23517 setTime : function()
23520 this.setValue(this.time.format(this.format));
23522 this.fireEvent('select', this, this.date);
23527 onMousedown: function(e){
23528 e.stopPropagation();
23529 e.preventDefault();
23532 onIncrementHours: function()
23534 Roo.log('onIncrementHours');
23535 this.time = this.time.add(Date.HOUR, 1);
23540 onDecrementHours: function()
23542 Roo.log('onDecrementHours');
23543 this.time = this.time.add(Date.HOUR, -1);
23547 onIncrementMinutes: function()
23549 Roo.log('onIncrementMinutes');
23550 this.time = this.time.add(Date.MINUTE, 1);
23554 onDecrementMinutes: function()
23556 Roo.log('onDecrementMinutes');
23557 this.time = this.time.add(Date.MINUTE, -1);
23561 onTogglePeriod: function()
23563 Roo.log('onTogglePeriod');
23564 this.time = this.time.add(Date.HOUR, 12);
23572 Roo.apply(Roo.bootstrap.TimeField, {
23576 cls: 'datepicker dropdown-menu',
23580 cls: 'datepicker-time',
23584 cls: 'table-condensed',
23613 cls: 'btn btn-info ok',
23641 * @class Roo.bootstrap.MonthField
23642 * @extends Roo.bootstrap.Input
23643 * Bootstrap MonthField class
23645 * @cfg {String} language default en
23648 * Create a new MonthField
23649 * @param {Object} config The config object
23652 Roo.bootstrap.MonthField = function(config){
23653 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23658 * Fires when this field show.
23659 * @param {Roo.bootstrap.MonthField} this
23660 * @param {Mixed} date The date value
23665 * Fires when this field hide.
23666 * @param {Roo.bootstrap.MonthField} this
23667 * @param {Mixed} date The date value
23672 * Fires when select a date.
23673 * @param {Roo.bootstrap.MonthField} this
23674 * @param {String} oldvalue The old value
23675 * @param {String} newvalue The new value
23681 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23683 onRender: function(ct, position)
23686 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23688 this.language = this.language || 'en';
23689 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23690 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23692 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23693 this.isInline = false;
23694 this.isInput = true;
23695 this.component = this.el.select('.add-on', true).first() || false;
23696 this.component = (this.component && this.component.length === 0) ? false : this.component;
23697 this.hasInput = this.component && this.inputEL().length;
23699 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23701 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23703 this.picker().on('mousedown', this.onMousedown, this);
23704 this.picker().on('click', this.onClick, this);
23706 this.picker().addClass('datepicker-dropdown');
23708 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23709 v.setStyle('width', '189px');
23716 if(this.isInline) {
23722 setValue: function(v, suppressEvent)
23724 var o = this.getValue();
23726 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23730 if(suppressEvent !== true){
23731 this.fireEvent('select', this, o, v);
23736 getValue: function()
23741 onClick: function(e)
23743 e.stopPropagation();
23744 e.preventDefault();
23746 var target = e.getTarget();
23748 if(target.nodeName.toLowerCase() === 'i'){
23749 target = Roo.get(target).dom.parentNode;
23752 var nodeName = target.nodeName;
23753 var className = target.className;
23754 var html = target.innerHTML;
23756 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23760 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23762 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23768 picker : function()
23770 return this.pickerEl;
23773 fillMonths: function()
23776 var months = this.picker().select('>.datepicker-months td', true).first();
23778 months.dom.innerHTML = '';
23784 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23787 months.createChild(month);
23796 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23797 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23800 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23801 e.removeClass('active');
23803 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23804 e.addClass('active');
23811 if(this.isInline) {
23815 this.picker().removeClass(['bottom', 'top']);
23817 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23819 * place to the top of element!
23823 this.picker().addClass('top');
23824 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23829 this.picker().addClass('bottom');
23831 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23834 onFocus : function()
23836 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23840 onBlur : function()
23842 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23844 var d = this.inputEl().getValue();
23853 this.picker().show();
23854 this.picker().select('>.datepicker-months', true).first().show();
23858 this.fireEvent('show', this, this.date);
23863 if(this.isInline) {
23866 this.picker().hide();
23867 this.fireEvent('hide', this, this.date);
23871 onMousedown: function(e)
23873 e.stopPropagation();
23874 e.preventDefault();
23879 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23883 fireKey: function(e)
23885 if (!this.picker().isVisible()){
23886 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23897 e.preventDefault();
23901 dir = e.keyCode == 37 ? -1 : 1;
23903 this.vIndex = this.vIndex + dir;
23905 if(this.vIndex < 0){
23909 if(this.vIndex > 11){
23913 if(isNaN(this.vIndex)){
23917 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23923 dir = e.keyCode == 38 ? -1 : 1;
23925 this.vIndex = this.vIndex + dir * 4;
23927 if(this.vIndex < 0){
23931 if(this.vIndex > 11){
23935 if(isNaN(this.vIndex)){
23939 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23944 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23945 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23949 e.preventDefault();
23952 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23953 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23969 this.picker().remove();
23974 Roo.apply(Roo.bootstrap.MonthField, {
23993 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23994 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23999 Roo.apply(Roo.bootstrap.MonthField, {
24003 cls: 'datepicker dropdown-menu roo-dynamic',
24007 cls: 'datepicker-months',
24011 cls: 'table-condensed',
24013 Roo.bootstrap.DateField.content
24033 * @class Roo.bootstrap.CheckBox
24034 * @extends Roo.bootstrap.Input
24035 * Bootstrap CheckBox class
24037 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24038 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24039 * @cfg {String} boxLabel The text that appears beside the checkbox
24040 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24041 * @cfg {Boolean} checked initnal the element
24042 * @cfg {Boolean} inline inline the element (default false)
24043 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24044 * @cfg {String} tooltip label tooltip
24047 * Create a new CheckBox
24048 * @param {Object} config The config object
24051 Roo.bootstrap.CheckBox = function(config){
24052 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24057 * Fires when the element is checked or unchecked.
24058 * @param {Roo.bootstrap.CheckBox} this This input
24059 * @param {Boolean} checked The new checked value
24064 * Fires when the element is click.
24065 * @param {Roo.bootstrap.CheckBox} this This input
24072 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24074 inputType: 'checkbox',
24083 // checkbox success does not make any sense really..
24088 getAutoCreate : function()
24090 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24096 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24099 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24105 type : this.inputType,
24106 value : this.inputValue,
24107 cls : 'roo-' + this.inputType, //'form-box',
24108 placeholder : this.placeholder || ''
24112 if(this.inputType != 'radio'){
24116 cls : 'roo-hidden-value',
24117 value : this.checked ? this.inputValue : this.valueOff
24122 if (this.weight) { // Validity check?
24123 cfg.cls += " " + this.inputType + "-" + this.weight;
24126 if (this.disabled) {
24127 input.disabled=true;
24131 input.checked = this.checked;
24136 input.name = this.name;
24138 if(this.inputType != 'radio'){
24139 hidden.name = this.name;
24140 input.name = '_hidden_' + this.name;
24145 input.cls += ' input-' + this.size;
24150 ['xs','sm','md','lg'].map(function(size){
24151 if (settings[size]) {
24152 cfg.cls += ' col-' + size + '-' + settings[size];
24156 var inputblock = input;
24158 if (this.before || this.after) {
24161 cls : 'input-group',
24166 inputblock.cn.push({
24168 cls : 'input-group-addon',
24173 inputblock.cn.push(input);
24175 if(this.inputType != 'radio'){
24176 inputblock.cn.push(hidden);
24180 inputblock.cn.push({
24182 cls : 'input-group-addon',
24188 var boxLabelCfg = false;
24194 //'for': id, // box label is handled by onclick - so no for...
24196 html: this.boxLabel
24199 boxLabelCfg.tooltip = this.tooltip;
24205 if (align ==='left' && this.fieldLabel.length) {
24206 // Roo.log("left and has label");
24211 cls : 'control-label',
24212 html : this.fieldLabel
24223 cfg.cn[1].cn.push(boxLabelCfg);
24226 if(this.labelWidth > 12){
24227 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24230 if(this.labelWidth < 13 && this.labelmd == 0){
24231 this.labelmd = this.labelWidth;
24234 if(this.labellg > 0){
24235 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24236 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24239 if(this.labelmd > 0){
24240 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24241 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24244 if(this.labelsm > 0){
24245 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24246 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24249 if(this.labelxs > 0){
24250 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24251 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24254 } else if ( this.fieldLabel.length) {
24255 // Roo.log(" label");
24259 tag: this.boxLabel ? 'span' : 'label',
24261 cls: 'control-label box-input-label',
24262 //cls : 'input-group-addon',
24263 html : this.fieldLabel
24270 cfg.cn.push(boxLabelCfg);
24275 // Roo.log(" no label && no align");
24276 cfg.cn = [ inputblock ] ;
24278 cfg.cn.push(boxLabelCfg);
24286 if(this.inputType != 'radio'){
24287 cfg.cn.push(hidden);
24295 * return the real input element.
24297 inputEl: function ()
24299 return this.el.select('input.roo-' + this.inputType,true).first();
24301 hiddenEl: function ()
24303 return this.el.select('input.roo-hidden-value',true).first();
24306 labelEl: function()
24308 return this.el.select('label.control-label',true).first();
24310 /* depricated... */
24314 return this.labelEl();
24317 boxLabelEl: function()
24319 return this.el.select('label.box-label',true).first();
24322 initEvents : function()
24324 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24326 this.inputEl().on('click', this.onClick, this);
24328 if (this.boxLabel) {
24329 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24332 this.startValue = this.getValue();
24335 Roo.bootstrap.CheckBox.register(this);
24339 onClick : function(e)
24341 if(this.fireEvent('click', this, e) !== false){
24342 this.setChecked(!this.checked);
24347 setChecked : function(state,suppressEvent)
24349 this.startValue = this.getValue();
24351 if(this.inputType == 'radio'){
24353 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24354 e.dom.checked = false;
24357 this.inputEl().dom.checked = true;
24359 this.inputEl().dom.value = this.inputValue;
24361 if(suppressEvent !== true){
24362 this.fireEvent('check', this, true);
24370 this.checked = state;
24372 this.inputEl().dom.checked = state;
24375 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24377 if(suppressEvent !== true){
24378 this.fireEvent('check', this, state);
24384 getValue : function()
24386 if(this.inputType == 'radio'){
24387 return this.getGroupValue();
24390 return this.hiddenEl().dom.value;
24394 getGroupValue : function()
24396 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24400 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24403 setValue : function(v,suppressEvent)
24405 if(this.inputType == 'radio'){
24406 this.setGroupValue(v, suppressEvent);
24410 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24415 setGroupValue : function(v, suppressEvent)
24417 this.startValue = this.getValue();
24419 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24420 e.dom.checked = false;
24422 if(e.dom.value == v){
24423 e.dom.checked = true;
24427 if(suppressEvent !== true){
24428 this.fireEvent('check', this, true);
24436 validate : function()
24438 if(this.getVisibilityEl().hasClass('hidden')){
24444 (this.inputType == 'radio' && this.validateRadio()) ||
24445 (this.inputType == 'checkbox' && this.validateCheckbox())
24451 this.markInvalid();
24455 validateRadio : function()
24457 if(this.getVisibilityEl().hasClass('hidden')){
24461 if(this.allowBlank){
24467 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24468 if(!e.dom.checked){
24480 validateCheckbox : function()
24483 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24484 //return (this.getValue() == this.inputValue) ? true : false;
24487 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24495 for(var i in group){
24496 if(group[i].el.isVisible(true)){
24504 for(var i in group){
24509 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24516 * Mark this field as valid
24518 markValid : function()
24522 this.fireEvent('valid', this);
24524 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24527 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24534 if(this.inputType == 'radio'){
24535 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24536 var fg = e.findParent('.form-group', false, true);
24537 if (Roo.bootstrap.version == 3) {
24538 fg.removeClass([_this.invalidClass, _this.validClass]);
24539 fg.addClass(_this.validClass);
24541 fg.removeClass(['is-valid', 'is-invalid']);
24542 fg.addClass('is-valid');
24550 var fg = this.el.findParent('.form-group', false, true);
24551 if (Roo.bootstrap.version == 3) {
24552 fg.removeClass([this.invalidClass, this.validClass]);
24553 fg.addClass(this.validClass);
24555 fg.removeClass(['is-valid', 'is-invalid']);
24556 fg.addClass('is-valid');
24561 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24567 for(var i in group){
24568 var fg = group[i].el.findParent('.form-group', false, true);
24569 if (Roo.bootstrap.version == 3) {
24570 fg.removeClass([this.invalidClass, this.validClass]);
24571 fg.addClass(this.validClass);
24573 fg.removeClass(['is-valid', 'is-invalid']);
24574 fg.addClass('is-valid');
24580 * Mark this field as invalid
24581 * @param {String} msg The validation message
24583 markInvalid : function(msg)
24585 if(this.allowBlank){
24591 this.fireEvent('invalid', this, msg);
24593 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24596 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24600 label.markInvalid();
24603 if(this.inputType == 'radio'){
24605 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24606 var fg = e.findParent('.form-group', false, true);
24607 if (Roo.bootstrap.version == 3) {
24608 fg.removeClass([_this.invalidClass, _this.validClass]);
24609 fg.addClass(_this.invalidClass);
24611 fg.removeClass(['is-invalid', 'is-valid']);
24612 fg.addClass('is-invalid');
24620 var fg = this.el.findParent('.form-group', false, true);
24621 if (Roo.bootstrap.version == 3) {
24622 fg.removeClass([_this.invalidClass, _this.validClass]);
24623 fg.addClass(_this.invalidClass);
24625 fg.removeClass(['is-invalid', 'is-valid']);
24626 fg.addClass('is-invalid');
24631 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24637 for(var i in group){
24638 var fg = group[i].el.findParent('.form-group', false, true);
24639 if (Roo.bootstrap.version == 3) {
24640 fg.removeClass([_this.invalidClass, _this.validClass]);
24641 fg.addClass(_this.invalidClass);
24643 fg.removeClass(['is-invalid', 'is-valid']);
24644 fg.addClass('is-invalid');
24650 clearInvalid : function()
24652 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24654 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24656 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24658 if (label && label.iconEl) {
24659 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24660 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24664 disable : function()
24666 if(this.inputType != 'radio'){
24667 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24674 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24675 _this.getActionEl().addClass(this.disabledClass);
24676 e.dom.disabled = true;
24680 this.disabled = true;
24681 this.fireEvent("disable", this);
24685 enable : function()
24687 if(this.inputType != 'radio'){
24688 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24695 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24696 _this.getActionEl().removeClass(this.disabledClass);
24697 e.dom.disabled = false;
24701 this.disabled = false;
24702 this.fireEvent("enable", this);
24706 setBoxLabel : function(v)
24711 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24717 Roo.apply(Roo.bootstrap.CheckBox, {
24722 * register a CheckBox Group
24723 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24725 register : function(checkbox)
24727 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24728 this.groups[checkbox.groupId] = {};
24731 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24735 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24739 * fetch a CheckBox Group based on the group ID
24740 * @param {string} the group ID
24741 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24743 get: function(groupId) {
24744 if (typeof(this.groups[groupId]) == 'undefined') {
24748 return this.groups[groupId] ;
24761 * @class Roo.bootstrap.Radio
24762 * @extends Roo.bootstrap.Component
24763 * Bootstrap Radio class
24764 * @cfg {String} boxLabel - the label associated
24765 * @cfg {String} value - the value of radio
24768 * Create a new Radio
24769 * @param {Object} config The config object
24771 Roo.bootstrap.Radio = function(config){
24772 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24776 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24782 getAutoCreate : function()
24786 cls : 'form-group radio',
24791 html : this.boxLabel
24799 initEvents : function()
24801 this.parent().register(this);
24803 this.el.on('click', this.onClick, this);
24807 onClick : function(e)
24809 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24810 this.setChecked(true);
24814 setChecked : function(state, suppressEvent)
24816 this.parent().setValue(this.value, suppressEvent);
24820 setBoxLabel : function(v)
24825 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24840 * @class Roo.bootstrap.SecurePass
24841 * @extends Roo.bootstrap.Input
24842 * Bootstrap SecurePass class
24846 * Create a new SecurePass
24847 * @param {Object} config The config object
24850 Roo.bootstrap.SecurePass = function (config) {
24851 // these go here, so the translation tool can replace them..
24853 PwdEmpty: "Please type a password, and then retype it to confirm.",
24854 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24855 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24856 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24857 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24858 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24859 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24860 TooWeak: "Your password is Too Weak."
24862 this.meterLabel = "Password strength:";
24863 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24864 this.meterClass = [
24865 "roo-password-meter-tooweak",
24866 "roo-password-meter-weak",
24867 "roo-password-meter-medium",
24868 "roo-password-meter-strong",
24869 "roo-password-meter-grey"
24874 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24877 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24879 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24881 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24882 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24883 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24884 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24885 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24886 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24887 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24897 * @cfg {String/Object} Label for the strength meter (defaults to
24898 * 'Password strength:')
24903 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24904 * ['Weak', 'Medium', 'Strong'])
24907 pwdStrengths: false,
24920 initEvents: function ()
24922 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24924 if (this.el.is('input[type=password]') && Roo.isSafari) {
24925 this.el.on('keydown', this.SafariOnKeyDown, this);
24928 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24931 onRender: function (ct, position)
24933 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24934 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24935 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24937 this.trigger.createChild({
24942 cls: 'roo-password-meter-grey col-xs-12',
24945 //width: this.meterWidth + 'px'
24949 cls: 'roo-password-meter-text'
24955 if (this.hideTrigger) {
24956 this.trigger.setDisplayed(false);
24958 this.setSize(this.width || '', this.height || '');
24961 onDestroy: function ()
24963 if (this.trigger) {
24964 this.trigger.removeAllListeners();
24965 this.trigger.remove();
24968 this.wrap.remove();
24970 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24973 checkStrength: function ()
24975 var pwd = this.inputEl().getValue();
24976 if (pwd == this._lastPwd) {
24981 if (this.ClientSideStrongPassword(pwd)) {
24983 } else if (this.ClientSideMediumPassword(pwd)) {
24985 } else if (this.ClientSideWeakPassword(pwd)) {
24991 Roo.log('strength1: ' + strength);
24993 //var pm = this.trigger.child('div/div/div').dom;
24994 var pm = this.trigger.child('div/div');
24995 pm.removeClass(this.meterClass);
24996 pm.addClass(this.meterClass[strength]);
24999 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25001 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25003 this._lastPwd = pwd;
25007 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25009 this._lastPwd = '';
25011 var pm = this.trigger.child('div/div');
25012 pm.removeClass(this.meterClass);
25013 pm.addClass('roo-password-meter-grey');
25016 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25019 this.inputEl().dom.type='password';
25022 validateValue: function (value)
25024 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25027 if (value.length == 0) {
25028 if (this.allowBlank) {
25029 this.clearInvalid();
25033 this.markInvalid(this.errors.PwdEmpty);
25034 this.errorMsg = this.errors.PwdEmpty;
25042 if (!value.match(/[\x21-\x7e]+/)) {
25043 this.markInvalid(this.errors.PwdBadChar);
25044 this.errorMsg = this.errors.PwdBadChar;
25047 if (value.length < 6) {
25048 this.markInvalid(this.errors.PwdShort);
25049 this.errorMsg = this.errors.PwdShort;
25052 if (value.length > 16) {
25053 this.markInvalid(this.errors.PwdLong);
25054 this.errorMsg = this.errors.PwdLong;
25058 if (this.ClientSideStrongPassword(value)) {
25060 } else if (this.ClientSideMediumPassword(value)) {
25062 } else if (this.ClientSideWeakPassword(value)) {
25069 if (strength < 2) {
25070 //this.markInvalid(this.errors.TooWeak);
25071 this.errorMsg = this.errors.TooWeak;
25076 console.log('strength2: ' + strength);
25078 //var pm = this.trigger.child('div/div/div').dom;
25080 var pm = this.trigger.child('div/div');
25081 pm.removeClass(this.meterClass);
25082 pm.addClass(this.meterClass[strength]);
25084 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25086 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25088 this.errorMsg = '';
25092 CharacterSetChecks: function (type)
25095 this.fResult = false;
25098 isctype: function (character, type)
25101 case this.kCapitalLetter:
25102 if (character >= 'A' && character <= 'Z') {
25107 case this.kSmallLetter:
25108 if (character >= 'a' && character <= 'z') {
25114 if (character >= '0' && character <= '9') {
25119 case this.kPunctuation:
25120 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25131 IsLongEnough: function (pwd, size)
25133 return !(pwd == null || isNaN(size) || pwd.length < size);
25136 SpansEnoughCharacterSets: function (word, nb)
25138 if (!this.IsLongEnough(word, nb))
25143 var characterSetChecks = new Array(
25144 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25145 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25148 for (var index = 0; index < word.length; ++index) {
25149 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25150 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25151 characterSetChecks[nCharSet].fResult = true;
25158 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25159 if (characterSetChecks[nCharSet].fResult) {
25164 if (nCharSets < nb) {
25170 ClientSideStrongPassword: function (pwd)
25172 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25175 ClientSideMediumPassword: function (pwd)
25177 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25180 ClientSideWeakPassword: function (pwd)
25182 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25185 })//<script type="text/javascript">
25188 * Based Ext JS Library 1.1.1
25189 * Copyright(c) 2006-2007, Ext JS, LLC.
25195 * @class Roo.HtmlEditorCore
25196 * @extends Roo.Component
25197 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25199 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25202 Roo.HtmlEditorCore = function(config){
25205 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25210 * @event initialize
25211 * Fires when the editor is fully initialized (including the iframe)
25212 * @param {Roo.HtmlEditorCore} this
25217 * Fires when the editor is first receives the focus. Any insertion must wait
25218 * until after this event.
25219 * @param {Roo.HtmlEditorCore} this
25223 * @event beforesync
25224 * Fires before the textarea is updated with content from the editor iframe. Return false
25225 * to cancel the sync.
25226 * @param {Roo.HtmlEditorCore} this
25227 * @param {String} html
25231 * @event beforepush
25232 * Fires before the iframe editor is updated with content from the textarea. Return false
25233 * to cancel the push.
25234 * @param {Roo.HtmlEditorCore} this
25235 * @param {String} html
25240 * Fires when the textarea is updated with content from the editor iframe.
25241 * @param {Roo.HtmlEditorCore} this
25242 * @param {String} html
25247 * Fires when the iframe editor is updated with content from the textarea.
25248 * @param {Roo.HtmlEditorCore} this
25249 * @param {String} html
25254 * @event editorevent
25255 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25256 * @param {Roo.HtmlEditorCore} this
25262 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25264 // defaults : white / black...
25265 this.applyBlacklists();
25272 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25276 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25282 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25287 * @cfg {Number} height (in pixels)
25291 * @cfg {Number} width (in pixels)
25296 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25299 stylesheets: false,
25304 // private properties
25305 validationEvent : false,
25307 initialized : false,
25309 sourceEditMode : false,
25310 onFocus : Roo.emptyFn,
25312 hideMode:'offsets',
25316 // blacklist + whitelisted elements..
25323 * Protected method that will not generally be called directly. It
25324 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25325 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25327 getDocMarkup : function(){
25331 // inherit styels from page...??
25332 if (this.stylesheets === false) {
25334 Roo.get(document.head).select('style').each(function(node) {
25335 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25338 Roo.get(document.head).select('link').each(function(node) {
25339 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25342 } else if (!this.stylesheets.length) {
25344 st = '<style type="text/css">' +
25345 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25348 for (var i in this.stylesheets) {
25349 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25354 st += '<style type="text/css">' +
25355 'IMG { cursor: pointer } ' +
25358 var cls = 'roo-htmleditor-body';
25360 if(this.bodyCls.length){
25361 cls += ' ' + this.bodyCls;
25364 return '<html><head>' + st +
25365 //<style type="text/css">' +
25366 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25368 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25372 onRender : function(ct, position)
25375 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25376 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25379 this.el.dom.style.border = '0 none';
25380 this.el.dom.setAttribute('tabIndex', -1);
25381 this.el.addClass('x-hidden hide');
25385 if(Roo.isIE){ // fix IE 1px bogus margin
25386 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25390 this.frameId = Roo.id();
25394 var iframe = this.owner.wrap.createChild({
25396 cls: 'form-control', // bootstrap..
25398 name: this.frameId,
25399 frameBorder : 'no',
25400 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25405 this.iframe = iframe.dom;
25407 this.assignDocWin();
25409 this.doc.designMode = 'on';
25412 this.doc.write(this.getDocMarkup());
25416 var task = { // must defer to wait for browser to be ready
25418 //console.log("run task?" + this.doc.readyState);
25419 this.assignDocWin();
25420 if(this.doc.body || this.doc.readyState == 'complete'){
25422 this.doc.designMode="on";
25426 Roo.TaskMgr.stop(task);
25427 this.initEditor.defer(10, this);
25434 Roo.TaskMgr.start(task);
25439 onResize : function(w, h)
25441 Roo.log('resize: ' +w + ',' + h );
25442 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25446 if(typeof w == 'number'){
25448 this.iframe.style.width = w + 'px';
25450 if(typeof h == 'number'){
25452 this.iframe.style.height = h + 'px';
25454 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25461 * Toggles the editor between standard and source edit mode.
25462 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25464 toggleSourceEdit : function(sourceEditMode){
25466 this.sourceEditMode = sourceEditMode === true;
25468 if(this.sourceEditMode){
25470 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25473 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25474 //this.iframe.className = '';
25477 //this.setSize(this.owner.wrap.getSize());
25478 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25485 * Protected method that will not generally be called directly. If you need/want
25486 * custom HTML cleanup, this is the method you should override.
25487 * @param {String} html The HTML to be cleaned
25488 * return {String} The cleaned HTML
25490 cleanHtml : function(html){
25491 html = String(html);
25492 if(html.length > 5){
25493 if(Roo.isSafari){ // strip safari nonsense
25494 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25497 if(html == ' '){
25504 * HTML Editor -> Textarea
25505 * Protected method that will not generally be called directly. Syncs the contents
25506 * of the editor iframe with the textarea.
25508 syncValue : function(){
25509 if(this.initialized){
25510 var bd = (this.doc.body || this.doc.documentElement);
25511 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25512 var html = bd.innerHTML;
25514 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25515 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25517 html = '<div style="'+m[0]+'">' + html + '</div>';
25520 html = this.cleanHtml(html);
25521 // fix up the special chars.. normaly like back quotes in word...
25522 // however we do not want to do this with chinese..
25523 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25525 var cc = match.charCodeAt();
25527 // Get the character value, handling surrogate pairs
25528 if (match.length == 2) {
25529 // It's a surrogate pair, calculate the Unicode code point
25530 var high = match.charCodeAt(0) - 0xD800;
25531 var low = match.charCodeAt(1) - 0xDC00;
25532 cc = (high * 0x400) + low + 0x10000;
25534 (cc >= 0x4E00 && cc < 0xA000 ) ||
25535 (cc >= 0x3400 && cc < 0x4E00 ) ||
25536 (cc >= 0xf900 && cc < 0xfb00 )
25541 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25542 return "&#" + cc + ";";
25549 if(this.owner.fireEvent('beforesync', this, html) !== false){
25550 this.el.dom.value = html;
25551 this.owner.fireEvent('sync', this, html);
25557 * Protected method that will not generally be called directly. Pushes the value of the textarea
25558 * into the iframe editor.
25560 pushValue : function(){
25561 if(this.initialized){
25562 var v = this.el.dom.value.trim();
25564 // if(v.length < 1){
25568 if(this.owner.fireEvent('beforepush', this, v) !== false){
25569 var d = (this.doc.body || this.doc.documentElement);
25571 this.cleanUpPaste();
25572 this.el.dom.value = d.innerHTML;
25573 this.owner.fireEvent('push', this, v);
25579 deferFocus : function(){
25580 this.focus.defer(10, this);
25584 focus : function(){
25585 if(this.win && !this.sourceEditMode){
25592 assignDocWin: function()
25594 var iframe = this.iframe;
25597 this.doc = iframe.contentWindow.document;
25598 this.win = iframe.contentWindow;
25600 // if (!Roo.get(this.frameId)) {
25603 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25604 // this.win = Roo.get(this.frameId).dom.contentWindow;
25606 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25610 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25611 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25616 initEditor : function(){
25617 //console.log("INIT EDITOR");
25618 this.assignDocWin();
25622 this.doc.designMode="on";
25624 this.doc.write(this.getDocMarkup());
25627 var dbody = (this.doc.body || this.doc.documentElement);
25628 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25629 // this copies styles from the containing element into thsi one..
25630 // not sure why we need all of this..
25631 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25633 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25634 //ss['background-attachment'] = 'fixed'; // w3c
25635 dbody.bgProperties = 'fixed'; // ie
25636 //Roo.DomHelper.applyStyles(dbody, ss);
25637 Roo.EventManager.on(this.doc, {
25638 //'mousedown': this.onEditorEvent,
25639 'mouseup': this.onEditorEvent,
25640 'dblclick': this.onEditorEvent,
25641 'click': this.onEditorEvent,
25642 'keyup': this.onEditorEvent,
25647 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25649 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25650 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25652 this.initialized = true;
25654 this.owner.fireEvent('initialize', this);
25659 onDestroy : function(){
25665 //for (var i =0; i < this.toolbars.length;i++) {
25666 // // fixme - ask toolbars for heights?
25667 // this.toolbars[i].onDestroy();
25670 //this.wrap.dom.innerHTML = '';
25671 //this.wrap.remove();
25676 onFirstFocus : function(){
25678 this.assignDocWin();
25681 this.activated = true;
25684 if(Roo.isGecko){ // prevent silly gecko errors
25686 var s = this.win.getSelection();
25687 if(!s.focusNode || s.focusNode.nodeType != 3){
25688 var r = s.getRangeAt(0);
25689 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25694 this.execCmd('useCSS', true);
25695 this.execCmd('styleWithCSS', false);
25698 this.owner.fireEvent('activate', this);
25702 adjustFont: function(btn){
25703 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25704 //if(Roo.isSafari){ // safari
25707 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25708 if(Roo.isSafari){ // safari
25709 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25710 v = (v < 10) ? 10 : v;
25711 v = (v > 48) ? 48 : v;
25712 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25717 v = Math.max(1, v+adjust);
25719 this.execCmd('FontSize', v );
25722 onEditorEvent : function(e)
25724 this.owner.fireEvent('editorevent', this, e);
25725 // this.updateToolbar();
25726 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25729 insertTag : function(tg)
25731 // could be a bit smarter... -> wrap the current selected tRoo..
25732 if (tg.toLowerCase() == 'span' ||
25733 tg.toLowerCase() == 'code' ||
25734 tg.toLowerCase() == 'sup' ||
25735 tg.toLowerCase() == 'sub'
25738 range = this.createRange(this.getSelection());
25739 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25740 wrappingNode.appendChild(range.extractContents());
25741 range.insertNode(wrappingNode);
25748 this.execCmd("formatblock", tg);
25752 insertText : function(txt)
25756 var range = this.createRange();
25757 range.deleteContents();
25758 //alert(Sender.getAttribute('label'));
25760 range.insertNode(this.doc.createTextNode(txt));
25766 * Executes a Midas editor command on the editor document and performs necessary focus and
25767 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25768 * @param {String} cmd The Midas command
25769 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25771 relayCmd : function(cmd, value){
25773 this.execCmd(cmd, value);
25774 this.owner.fireEvent('editorevent', this);
25775 //this.updateToolbar();
25776 this.owner.deferFocus();
25780 * Executes a Midas editor command directly on the editor document.
25781 * For visual commands, you should use {@link #relayCmd} instead.
25782 * <b>This should only be called after the editor is initialized.</b>
25783 * @param {String} cmd The Midas command
25784 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25786 execCmd : function(cmd, value){
25787 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25794 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25796 * @param {String} text | dom node..
25798 insertAtCursor : function(text)
25801 if(!this.activated){
25807 var r = this.doc.selection.createRange();
25818 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25822 // from jquery ui (MIT licenced)
25824 var win = this.win;
25826 if (win.getSelection && win.getSelection().getRangeAt) {
25827 range = win.getSelection().getRangeAt(0);
25828 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25829 range.insertNode(node);
25830 } else if (win.document.selection && win.document.selection.createRange) {
25831 // no firefox support
25832 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25833 win.document.selection.createRange().pasteHTML(txt);
25835 // no firefox support
25836 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25837 this.execCmd('InsertHTML', txt);
25846 mozKeyPress : function(e){
25848 var c = e.getCharCode(), cmd;
25851 c = String.fromCharCode(c).toLowerCase();
25865 this.cleanUpPaste.defer(100, this);
25873 e.preventDefault();
25881 fixKeys : function(){ // load time branching for fastest keydown performance
25883 return function(e){
25884 var k = e.getKey(), r;
25887 r = this.doc.selection.createRange();
25890 r.pasteHTML('    ');
25897 r = this.doc.selection.createRange();
25899 var target = r.parentElement();
25900 if(!target || target.tagName.toLowerCase() != 'li'){
25902 r.pasteHTML('<br />');
25908 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25909 this.cleanUpPaste.defer(100, this);
25915 }else if(Roo.isOpera){
25916 return function(e){
25917 var k = e.getKey();
25921 this.execCmd('InsertHTML','    ');
25924 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25925 this.cleanUpPaste.defer(100, this);
25930 }else if(Roo.isSafari){
25931 return function(e){
25932 var k = e.getKey();
25936 this.execCmd('InsertText','\t');
25940 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25941 this.cleanUpPaste.defer(100, this);
25949 getAllAncestors: function()
25951 var p = this.getSelectedNode();
25954 a.push(p); // push blank onto stack..
25955 p = this.getParentElement();
25959 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25963 a.push(this.doc.body);
25967 lastSelNode : false,
25970 getSelection : function()
25972 this.assignDocWin();
25973 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25976 getSelectedNode: function()
25978 // this may only work on Gecko!!!
25980 // should we cache this!!!!
25985 var range = this.createRange(this.getSelection()).cloneRange();
25988 var parent = range.parentElement();
25990 var testRange = range.duplicate();
25991 testRange.moveToElementText(parent);
25992 if (testRange.inRange(range)) {
25995 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25998 parent = parent.parentElement;
26003 // is ancestor a text element.
26004 var ac = range.commonAncestorContainer;
26005 if (ac.nodeType == 3) {
26006 ac = ac.parentNode;
26009 var ar = ac.childNodes;
26012 var other_nodes = [];
26013 var has_other_nodes = false;
26014 for (var i=0;i<ar.length;i++) {
26015 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26018 // fullly contained node.
26020 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26025 // probably selected..
26026 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26027 other_nodes.push(ar[i]);
26031 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26036 has_other_nodes = true;
26038 if (!nodes.length && other_nodes.length) {
26039 nodes= other_nodes;
26041 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26047 createRange: function(sel)
26049 // this has strange effects when using with
26050 // top toolbar - not sure if it's a great idea.
26051 //this.editor.contentWindow.focus();
26052 if (typeof sel != "undefined") {
26054 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26056 return this.doc.createRange();
26059 return this.doc.createRange();
26062 getParentElement: function()
26065 this.assignDocWin();
26066 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26068 var range = this.createRange(sel);
26071 var p = range.commonAncestorContainer;
26072 while (p.nodeType == 3) { // text node
26083 * Range intersection.. the hard stuff...
26087 * [ -- selected range --- ]
26091 * if end is before start or hits it. fail.
26092 * if start is after end or hits it fail.
26094 * if either hits (but other is outside. - then it's not
26100 // @see http://www.thismuchiknow.co.uk/?p=64.
26101 rangeIntersectsNode : function(range, node)
26103 var nodeRange = node.ownerDocument.createRange();
26105 nodeRange.selectNode(node);
26107 nodeRange.selectNodeContents(node);
26110 var rangeStartRange = range.cloneRange();
26111 rangeStartRange.collapse(true);
26113 var rangeEndRange = range.cloneRange();
26114 rangeEndRange.collapse(false);
26116 var nodeStartRange = nodeRange.cloneRange();
26117 nodeStartRange.collapse(true);
26119 var nodeEndRange = nodeRange.cloneRange();
26120 nodeEndRange.collapse(false);
26122 return rangeStartRange.compareBoundaryPoints(
26123 Range.START_TO_START, nodeEndRange) == -1 &&
26124 rangeEndRange.compareBoundaryPoints(
26125 Range.START_TO_START, nodeStartRange) == 1;
26129 rangeCompareNode : function(range, node)
26131 var nodeRange = node.ownerDocument.createRange();
26133 nodeRange.selectNode(node);
26135 nodeRange.selectNodeContents(node);
26139 range.collapse(true);
26141 nodeRange.collapse(true);
26143 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26144 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26146 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26148 var nodeIsBefore = ss == 1;
26149 var nodeIsAfter = ee == -1;
26151 if (nodeIsBefore && nodeIsAfter) {
26154 if (!nodeIsBefore && nodeIsAfter) {
26155 return 1; //right trailed.
26158 if (nodeIsBefore && !nodeIsAfter) {
26159 return 2; // left trailed.
26165 // private? - in a new class?
26166 cleanUpPaste : function()
26168 // cleans up the whole document..
26169 Roo.log('cleanuppaste');
26171 this.cleanUpChildren(this.doc.body);
26172 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26173 if (clean != this.doc.body.innerHTML) {
26174 this.doc.body.innerHTML = clean;
26179 cleanWordChars : function(input) {// change the chars to hex code
26180 var he = Roo.HtmlEditorCore;
26182 var output = input;
26183 Roo.each(he.swapCodes, function(sw) {
26184 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26186 output = output.replace(swapper, sw[1]);
26193 cleanUpChildren : function (n)
26195 if (!n.childNodes.length) {
26198 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26199 this.cleanUpChild(n.childNodes[i]);
26206 cleanUpChild : function (node)
26209 //console.log(node);
26210 if (node.nodeName == "#text") {
26211 // clean up silly Windows -- stuff?
26214 if (node.nodeName == "#comment") {
26215 node.parentNode.removeChild(node);
26216 // clean up silly Windows -- stuff?
26219 var lcname = node.tagName.toLowerCase();
26220 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26221 // whitelist of tags..
26223 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26225 node.parentNode.removeChild(node);
26230 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26232 // spans with no attributes - just remove them..
26233 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26234 remove_keep_children = true;
26237 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26238 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26240 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26241 // remove_keep_children = true;
26244 if (remove_keep_children) {
26245 this.cleanUpChildren(node);
26246 // inserts everything just before this node...
26247 while (node.childNodes.length) {
26248 var cn = node.childNodes[0];
26249 node.removeChild(cn);
26250 node.parentNode.insertBefore(cn, node);
26252 node.parentNode.removeChild(node);
26256 if (!node.attributes || !node.attributes.length) {
26261 this.cleanUpChildren(node);
26265 function cleanAttr(n,v)
26268 if (v.match(/^\./) || v.match(/^\//)) {
26271 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26274 if (v.match(/^#/)) {
26277 if (v.match(/^\{/)) { // allow template editing.
26280 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26281 node.removeAttribute(n);
26285 var cwhite = this.cwhite;
26286 var cblack = this.cblack;
26288 function cleanStyle(n,v)
26290 if (v.match(/expression/)) { //XSS?? should we even bother..
26291 node.removeAttribute(n);
26295 var parts = v.split(/;/);
26298 Roo.each(parts, function(p) {
26299 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26303 var l = p.split(':').shift().replace(/\s+/g,'');
26304 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26306 if ( cwhite.length && cblack.indexOf(l) > -1) {
26307 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26308 //node.removeAttribute(n);
26312 // only allow 'c whitelisted system attributes'
26313 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26314 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26315 //node.removeAttribute(n);
26325 if (clean.length) {
26326 node.setAttribute(n, clean.join(';'));
26328 node.removeAttribute(n);
26334 for (var i = node.attributes.length-1; i > -1 ; i--) {
26335 var a = node.attributes[i];
26338 if (a.name.toLowerCase().substr(0,2)=='on') {
26339 node.removeAttribute(a.name);
26342 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26343 node.removeAttribute(a.name);
26346 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26347 cleanAttr(a.name,a.value); // fixme..
26350 if (a.name == 'style') {
26351 cleanStyle(a.name,a.value);
26354 /// clean up MS crap..
26355 // tecnically this should be a list of valid class'es..
26358 if (a.name == 'class') {
26359 if (a.value.match(/^Mso/)) {
26360 node.removeAttribute('class');
26363 if (a.value.match(/^body$/)) {
26364 node.removeAttribute('class');
26375 this.cleanUpChildren(node);
26381 * Clean up MS wordisms...
26383 cleanWord : function(node)
26386 this.cleanWord(this.doc.body);
26391 node.nodeName == 'SPAN' &&
26392 !node.hasAttributes() &&
26393 node.childNodes.length == 1 &&
26394 node.firstChild.nodeName == "#text"
26396 var textNode = node.firstChild;
26397 node.removeChild(textNode);
26398 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26399 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26401 node.parentNode.insertBefore(textNode, node);
26402 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26403 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26405 node.parentNode.removeChild(node);
26408 if (node.nodeName == "#text") {
26409 // clean up silly Windows -- stuff?
26412 if (node.nodeName == "#comment") {
26413 node.parentNode.removeChild(node);
26414 // clean up silly Windows -- stuff?
26418 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26419 node.parentNode.removeChild(node);
26422 //Roo.log(node.tagName);
26423 // remove - but keep children..
26424 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26425 //Roo.log('-- removed');
26426 while (node.childNodes.length) {
26427 var cn = node.childNodes[0];
26428 node.removeChild(cn);
26429 node.parentNode.insertBefore(cn, node);
26430 // move node to parent - and clean it..
26431 this.cleanWord(cn);
26433 node.parentNode.removeChild(node);
26434 /// no need to iterate chidlren = it's got none..
26435 //this.iterateChildren(node, this.cleanWord);
26439 if (node.className.length) {
26441 var cn = node.className.split(/\W+/);
26443 Roo.each(cn, function(cls) {
26444 if (cls.match(/Mso[a-zA-Z]+/)) {
26449 node.className = cna.length ? cna.join(' ') : '';
26451 node.removeAttribute("class");
26455 if (node.hasAttribute("lang")) {
26456 node.removeAttribute("lang");
26459 if (node.hasAttribute("style")) {
26461 var styles = node.getAttribute("style").split(";");
26463 Roo.each(styles, function(s) {
26464 if (!s.match(/:/)) {
26467 var kv = s.split(":");
26468 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26471 // what ever is left... we allow.
26474 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26475 if (!nstyle.length) {
26476 node.removeAttribute('style');
26479 this.iterateChildren(node, this.cleanWord);
26485 * iterateChildren of a Node, calling fn each time, using this as the scole..
26486 * @param {DomNode} node node to iterate children of.
26487 * @param {Function} fn method of this class to call on each item.
26489 iterateChildren : function(node, fn)
26491 if (!node.childNodes.length) {
26494 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26495 fn.call(this, node.childNodes[i])
26501 * cleanTableWidths.
26503 * Quite often pasting from word etc.. results in tables with column and widths.
26504 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26507 cleanTableWidths : function(node)
26512 this.cleanTableWidths(this.doc.body);
26517 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26520 Roo.log(node.tagName);
26521 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26522 this.iterateChildren(node, this.cleanTableWidths);
26525 if (node.hasAttribute('width')) {
26526 node.removeAttribute('width');
26530 if (node.hasAttribute("style")) {
26533 var styles = node.getAttribute("style").split(";");
26535 Roo.each(styles, function(s) {
26536 if (!s.match(/:/)) {
26539 var kv = s.split(":");
26540 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26543 // what ever is left... we allow.
26546 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26547 if (!nstyle.length) {
26548 node.removeAttribute('style');
26552 this.iterateChildren(node, this.cleanTableWidths);
26560 domToHTML : function(currentElement, depth, nopadtext) {
26562 depth = depth || 0;
26563 nopadtext = nopadtext || false;
26565 if (!currentElement) {
26566 return this.domToHTML(this.doc.body);
26569 //Roo.log(currentElement);
26571 var allText = false;
26572 var nodeName = currentElement.nodeName;
26573 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26575 if (nodeName == '#text') {
26577 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26582 if (nodeName != 'BODY') {
26585 // Prints the node tagName, such as <A>, <IMG>, etc
26588 for(i = 0; i < currentElement.attributes.length;i++) {
26590 var aname = currentElement.attributes.item(i).name;
26591 if (!currentElement.attributes.item(i).value.length) {
26594 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26597 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26606 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26609 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26614 // Traverse the tree
26616 var currentElementChild = currentElement.childNodes.item(i);
26617 var allText = true;
26618 var innerHTML = '';
26620 while (currentElementChild) {
26621 // Formatting code (indent the tree so it looks nice on the screen)
26622 var nopad = nopadtext;
26623 if (lastnode == 'SPAN') {
26627 if (currentElementChild.nodeName == '#text') {
26628 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26629 toadd = nopadtext ? toadd : toadd.trim();
26630 if (!nopad && toadd.length > 80) {
26631 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26633 innerHTML += toadd;
26636 currentElementChild = currentElement.childNodes.item(i);
26642 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26644 // Recursively traverse the tree structure of the child node
26645 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26646 lastnode = currentElementChild.nodeName;
26648 currentElementChild=currentElement.childNodes.item(i);
26654 // The remaining code is mostly for formatting the tree
26655 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26660 ret+= "</"+tagName+">";
26666 applyBlacklists : function()
26668 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26669 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26673 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26674 if (b.indexOf(tag) > -1) {
26677 this.white.push(tag);
26681 Roo.each(w, function(tag) {
26682 if (b.indexOf(tag) > -1) {
26685 if (this.white.indexOf(tag) > -1) {
26688 this.white.push(tag);
26693 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26694 if (w.indexOf(tag) > -1) {
26697 this.black.push(tag);
26701 Roo.each(b, function(tag) {
26702 if (w.indexOf(tag) > -1) {
26705 if (this.black.indexOf(tag) > -1) {
26708 this.black.push(tag);
26713 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26714 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26718 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26719 if (b.indexOf(tag) > -1) {
26722 this.cwhite.push(tag);
26726 Roo.each(w, function(tag) {
26727 if (b.indexOf(tag) > -1) {
26730 if (this.cwhite.indexOf(tag) > -1) {
26733 this.cwhite.push(tag);
26738 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26739 if (w.indexOf(tag) > -1) {
26742 this.cblack.push(tag);
26746 Roo.each(b, function(tag) {
26747 if (w.indexOf(tag) > -1) {
26750 if (this.cblack.indexOf(tag) > -1) {
26753 this.cblack.push(tag);
26758 setStylesheets : function(stylesheets)
26760 if(typeof(stylesheets) == 'string'){
26761 Roo.get(this.iframe.contentDocument.head).createChild({
26763 rel : 'stylesheet',
26772 Roo.each(stylesheets, function(s) {
26777 Roo.get(_this.iframe.contentDocument.head).createChild({
26779 rel : 'stylesheet',
26788 removeStylesheets : function()
26792 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26797 setStyle : function(style)
26799 Roo.get(this.iframe.contentDocument.head).createChild({
26808 // hide stuff that is not compatible
26822 * @event specialkey
26826 * @cfg {String} fieldClass @hide
26829 * @cfg {String} focusClass @hide
26832 * @cfg {String} autoCreate @hide
26835 * @cfg {String} inputType @hide
26838 * @cfg {String} invalidClass @hide
26841 * @cfg {String} invalidText @hide
26844 * @cfg {String} msgFx @hide
26847 * @cfg {String} validateOnBlur @hide
26851 Roo.HtmlEditorCore.white = [
26852 'area', 'br', 'img', 'input', 'hr', 'wbr',
26854 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26855 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26856 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26857 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26858 'table', 'ul', 'xmp',
26860 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26863 'dir', 'menu', 'ol', 'ul', 'dl',
26869 Roo.HtmlEditorCore.black = [
26870 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26872 'base', 'basefont', 'bgsound', 'blink', 'body',
26873 'frame', 'frameset', 'head', 'html', 'ilayer',
26874 'iframe', 'layer', 'link', 'meta', 'object',
26875 'script', 'style' ,'title', 'xml' // clean later..
26877 Roo.HtmlEditorCore.clean = [
26878 'script', 'style', 'title', 'xml'
26880 Roo.HtmlEditorCore.remove = [
26885 Roo.HtmlEditorCore.ablack = [
26889 Roo.HtmlEditorCore.aclean = [
26890 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26894 Roo.HtmlEditorCore.pwhite= [
26895 'http', 'https', 'mailto'
26898 // white listed style attributes.
26899 Roo.HtmlEditorCore.cwhite= [
26900 // 'text-align', /// default is to allow most things..
26906 // black listed style attributes.
26907 Roo.HtmlEditorCore.cblack= [
26908 // 'font-size' -- this can be set by the project
26912 Roo.HtmlEditorCore.swapCodes =[
26913 [ 8211, "–" ],
26914 [ 8212, "—" ],
26931 * @class Roo.bootstrap.HtmlEditor
26932 * @extends Roo.bootstrap.TextArea
26933 * Bootstrap HtmlEditor class
26936 * Create a new HtmlEditor
26937 * @param {Object} config The config object
26940 Roo.bootstrap.HtmlEditor = function(config){
26941 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26942 if (!this.toolbars) {
26943 this.toolbars = [];
26946 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26949 * @event initialize
26950 * Fires when the editor is fully initialized (including the iframe)
26951 * @param {HtmlEditor} this
26956 * Fires when the editor is first receives the focus. Any insertion must wait
26957 * until after this event.
26958 * @param {HtmlEditor} this
26962 * @event beforesync
26963 * Fires before the textarea is updated with content from the editor iframe. Return false
26964 * to cancel the sync.
26965 * @param {HtmlEditor} this
26966 * @param {String} html
26970 * @event beforepush
26971 * Fires before the iframe editor is updated with content from the textarea. Return false
26972 * to cancel the push.
26973 * @param {HtmlEditor} this
26974 * @param {String} html
26979 * Fires when the textarea is updated with content from the editor iframe.
26980 * @param {HtmlEditor} this
26981 * @param {String} html
26986 * Fires when the iframe editor is updated with content from the textarea.
26987 * @param {HtmlEditor} this
26988 * @param {String} html
26992 * @event editmodechange
26993 * Fires when the editor switches edit modes
26994 * @param {HtmlEditor} this
26995 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26997 editmodechange: true,
26999 * @event editorevent
27000 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27001 * @param {HtmlEditor} this
27005 * @event firstfocus
27006 * Fires when on first focus - needed by toolbars..
27007 * @param {HtmlEditor} this
27012 * Auto save the htmlEditor value as a file into Events
27013 * @param {HtmlEditor} this
27017 * @event savedpreview
27018 * preview the saved version of htmlEditor
27019 * @param {HtmlEditor} this
27026 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27030 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27035 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27040 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27045 * @cfg {Number} height (in pixels)
27049 * @cfg {Number} width (in pixels)
27054 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27057 stylesheets: false,
27062 // private properties
27063 validationEvent : false,
27065 initialized : false,
27068 onFocus : Roo.emptyFn,
27070 hideMode:'offsets',
27072 tbContainer : false,
27076 toolbarContainer :function() {
27077 return this.wrap.select('.x-html-editor-tb',true).first();
27081 * Protected method that will not generally be called directly. It
27082 * is called when the editor creates its toolbar. Override this method if you need to
27083 * add custom toolbar buttons.
27084 * @param {HtmlEditor} editor
27086 createToolbar : function(){
27087 Roo.log('renewing');
27088 Roo.log("create toolbars");
27090 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27091 this.toolbars[0].render(this.toolbarContainer());
27095 // if (!editor.toolbars || !editor.toolbars.length) {
27096 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27099 // for (var i =0 ; i < editor.toolbars.length;i++) {
27100 // editor.toolbars[i] = Roo.factory(
27101 // typeof(editor.toolbars[i]) == 'string' ?
27102 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27103 // Roo.bootstrap.HtmlEditor);
27104 // editor.toolbars[i].init(editor);
27110 onRender : function(ct, position)
27112 // Roo.log("Call onRender: " + this.xtype);
27114 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27116 this.wrap = this.inputEl().wrap({
27117 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27120 this.editorcore.onRender(ct, position);
27122 if (this.resizable) {
27123 this.resizeEl = new Roo.Resizable(this.wrap, {
27127 minHeight : this.height,
27128 height: this.height,
27129 handles : this.resizable,
27132 resize : function(r, w, h) {
27133 _t.onResize(w,h); // -something
27139 this.createToolbar(this);
27142 if(!this.width && this.resizable){
27143 this.setSize(this.wrap.getSize());
27145 if (this.resizeEl) {
27146 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27147 // should trigger onReize..
27153 onResize : function(w, h)
27155 Roo.log('resize: ' +w + ',' + h );
27156 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27160 if(this.inputEl() ){
27161 if(typeof w == 'number'){
27162 var aw = w - this.wrap.getFrameWidth('lr');
27163 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27166 if(typeof h == 'number'){
27167 var tbh = -11; // fixme it needs to tool bar size!
27168 for (var i =0; i < this.toolbars.length;i++) {
27169 // fixme - ask toolbars for heights?
27170 tbh += this.toolbars[i].el.getHeight();
27171 //if (this.toolbars[i].footer) {
27172 // tbh += this.toolbars[i].footer.el.getHeight();
27180 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27181 ah -= 5; // knock a few pixes off for look..
27182 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27186 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27187 this.editorcore.onResize(ew,eh);
27192 * Toggles the editor between standard and source edit mode.
27193 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27195 toggleSourceEdit : function(sourceEditMode)
27197 this.editorcore.toggleSourceEdit(sourceEditMode);
27199 if(this.editorcore.sourceEditMode){
27200 Roo.log('editor - showing textarea');
27203 // Roo.log(this.syncValue());
27205 this.inputEl().removeClass(['hide', 'x-hidden']);
27206 this.inputEl().dom.removeAttribute('tabIndex');
27207 this.inputEl().focus();
27209 Roo.log('editor - hiding textarea');
27211 // Roo.log(this.pushValue());
27214 this.inputEl().addClass(['hide', 'x-hidden']);
27215 this.inputEl().dom.setAttribute('tabIndex', -1);
27216 //this.deferFocus();
27219 if(this.resizable){
27220 this.setSize(this.wrap.getSize());
27223 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27226 // private (for BoxComponent)
27227 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27229 // private (for BoxComponent)
27230 getResizeEl : function(){
27234 // private (for BoxComponent)
27235 getPositionEl : function(){
27240 initEvents : function(){
27241 this.originalValue = this.getValue();
27245 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27248 // markInvalid : Roo.emptyFn,
27250 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27253 // clearInvalid : Roo.emptyFn,
27255 setValue : function(v){
27256 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27257 this.editorcore.pushValue();
27262 deferFocus : function(){
27263 this.focus.defer(10, this);
27267 focus : function(){
27268 this.editorcore.focus();
27274 onDestroy : function(){
27280 for (var i =0; i < this.toolbars.length;i++) {
27281 // fixme - ask toolbars for heights?
27282 this.toolbars[i].onDestroy();
27285 this.wrap.dom.innerHTML = '';
27286 this.wrap.remove();
27291 onFirstFocus : function(){
27292 //Roo.log("onFirstFocus");
27293 this.editorcore.onFirstFocus();
27294 for (var i =0; i < this.toolbars.length;i++) {
27295 this.toolbars[i].onFirstFocus();
27301 syncValue : function()
27303 this.editorcore.syncValue();
27306 pushValue : function()
27308 this.editorcore.pushValue();
27312 // hide stuff that is not compatible
27326 * @event specialkey
27330 * @cfg {String} fieldClass @hide
27333 * @cfg {String} focusClass @hide
27336 * @cfg {String} autoCreate @hide
27339 * @cfg {String} inputType @hide
27343 * @cfg {String} invalidText @hide
27346 * @cfg {String} msgFx @hide
27349 * @cfg {String} validateOnBlur @hide
27358 Roo.namespace('Roo.bootstrap.htmleditor');
27360 * @class Roo.bootstrap.HtmlEditorToolbar1
27366 new Roo.bootstrap.HtmlEditor({
27369 new Roo.bootstrap.HtmlEditorToolbar1({
27370 disable : { fonts: 1 , format: 1, ..., ... , ...],
27376 * @cfg {Object} disable List of elements to disable..
27377 * @cfg {Array} btns List of additional buttons.
27381 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27384 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27387 Roo.apply(this, config);
27389 // default disabled, based on 'good practice'..
27390 this.disable = this.disable || {};
27391 Roo.applyIf(this.disable, {
27394 specialElements : true
27396 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27398 this.editor = config.editor;
27399 this.editorcore = config.editor.editorcore;
27401 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27403 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27404 // dont call parent... till later.
27406 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27411 editorcore : false,
27416 "h1","h2","h3","h4","h5","h6",
27418 "abbr", "acronym", "address", "cite", "samp", "var",
27422 onRender : function(ct, position)
27424 // Roo.log("Call onRender: " + this.xtype);
27426 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27428 this.el.dom.style.marginBottom = '0';
27430 var editorcore = this.editorcore;
27431 var editor= this.editor;
27434 var btn = function(id,cmd , toggle, handler, html){
27436 var event = toggle ? 'toggle' : 'click';
27441 xns: Roo.bootstrap,
27445 enableToggle:toggle !== false,
27447 pressed : toggle ? false : null,
27450 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27451 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27457 // var cb_box = function...
27462 xns: Roo.bootstrap,
27467 xns: Roo.bootstrap,
27471 Roo.each(this.formats, function(f) {
27472 style.menu.items.push({
27474 xns: Roo.bootstrap,
27475 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27480 editorcore.insertTag(this.tagname);
27487 children.push(style);
27489 btn('bold',false,true);
27490 btn('italic',false,true);
27491 btn('align-left', 'justifyleft',true);
27492 btn('align-center', 'justifycenter',true);
27493 btn('align-right' , 'justifyright',true);
27494 btn('link', false, false, function(btn) {
27495 //Roo.log("create link?");
27496 var url = prompt(this.createLinkText, this.defaultLinkValue);
27497 if(url && url != 'http:/'+'/'){
27498 this.editorcore.relayCmd('createlink', url);
27501 btn('list','insertunorderedlist',true);
27502 btn('pencil', false,true, function(btn){
27504 this.toggleSourceEdit(btn.pressed);
27507 if (this.editor.btns.length > 0) {
27508 for (var i = 0; i<this.editor.btns.length; i++) {
27509 children.push(this.editor.btns[i]);
27517 xns: Roo.bootstrap,
27522 xns: Roo.bootstrap,
27527 cog.menu.items.push({
27529 xns: Roo.bootstrap,
27530 html : Clean styles,
27535 editorcore.insertTag(this.tagname);
27544 this.xtype = 'NavSimplebar';
27546 for(var i=0;i< children.length;i++) {
27548 this.buttons.add(this.addxtypeChild(children[i]));
27552 editor.on('editorevent', this.updateToolbar, this);
27554 onBtnClick : function(id)
27556 this.editorcore.relayCmd(id);
27557 this.editorcore.focus();
27561 * Protected method that will not generally be called directly. It triggers
27562 * a toolbar update by reading the markup state of the current selection in the editor.
27564 updateToolbar: function(){
27566 if(!this.editorcore.activated){
27567 this.editor.onFirstFocus(); // is this neeed?
27571 var btns = this.buttons;
27572 var doc = this.editorcore.doc;
27573 btns.get('bold').setActive(doc.queryCommandState('bold'));
27574 btns.get('italic').setActive(doc.queryCommandState('italic'));
27575 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27577 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27578 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27579 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27581 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27582 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27585 var ans = this.editorcore.getAllAncestors();
27586 if (this.formatCombo) {
27589 var store = this.formatCombo.store;
27590 this.formatCombo.setValue("");
27591 for (var i =0; i < ans.length;i++) {
27592 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27594 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27602 // hides menus... - so this cant be on a menu...
27603 Roo.bootstrap.MenuMgr.hideAll();
27605 Roo.bootstrap.MenuMgr.hideAll();
27606 //this.editorsyncValue();
27608 onFirstFocus: function() {
27609 this.buttons.each(function(item){
27613 toggleSourceEdit : function(sourceEditMode){
27616 if(sourceEditMode){
27617 Roo.log("disabling buttons");
27618 this.buttons.each( function(item){
27619 if(item.cmd != 'pencil'){
27625 Roo.log("enabling buttons");
27626 if(this.editorcore.initialized){
27627 this.buttons.each( function(item){
27633 Roo.log("calling toggole on editor");
27634 // tell the editor that it's been pressed..
27635 this.editor.toggleSourceEdit(sourceEditMode);
27649 * @class Roo.bootstrap.Markdown
27650 * @extends Roo.bootstrap.TextArea
27651 * Bootstrap Showdown editable area
27652 * @cfg {string} content
27655 * Create a new Showdown
27658 Roo.bootstrap.Markdown = function(config){
27659 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27663 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27667 initEvents : function()
27670 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27671 this.markdownEl = this.el.createChild({
27672 cls : 'roo-markdown-area'
27674 this.inputEl().addClass('d-none');
27675 if (this.getValue() == '') {
27676 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27679 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27681 this.markdownEl.on('click', this.toggleTextEdit, this);
27682 this.on('blur', this.toggleTextEdit, this);
27683 this.on('specialkey', this.resizeTextArea, this);
27686 toggleTextEdit : function()
27688 var sh = this.markdownEl.getHeight();
27689 this.inputEl().addClass('d-none');
27690 this.markdownEl.addClass('d-none');
27691 if (!this.editing) {
27693 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27694 this.inputEl().removeClass('d-none');
27695 this.inputEl().focus();
27696 this.editing = true;
27699 // show showdown...
27700 this.updateMarkdown();
27701 this.markdownEl.removeClass('d-none');
27702 this.editing = false;
27705 updateMarkdown : function()
27707 if (this.getValue() == '') {
27708 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27712 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27715 resizeTextArea: function () {
27718 Roo.log([sh, this.getValue().split("\n").length * 30]);
27719 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27721 setValue : function(val)
27723 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27724 if (!this.editing) {
27725 this.updateMarkdown();
27731 if (!this.editing) {
27732 this.toggleTextEdit();
27740 * Ext JS Library 1.1.1
27741 * Copyright(c) 2006-2007, Ext JS, LLC.
27743 * Originally Released Under LGPL - original licence link has changed is not relivant.
27746 * <script type="text/javascript">
27750 * @class Roo.bootstrap.PagingToolbar
27751 * @extends Roo.bootstrap.NavSimplebar
27752 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27754 * Create a new PagingToolbar
27755 * @param {Object} config The config object
27756 * @param {Roo.data.Store} store
27758 Roo.bootstrap.PagingToolbar = function(config)
27760 // old args format still supported... - xtype is prefered..
27761 // created from xtype...
27763 this.ds = config.dataSource;
27765 if (config.store && !this.ds) {
27766 this.store= Roo.factory(config.store, Roo.data);
27767 this.ds = this.store;
27768 this.ds.xmodule = this.xmodule || false;
27771 this.toolbarItems = [];
27772 if (config.items) {
27773 this.toolbarItems = config.items;
27776 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27781 this.bind(this.ds);
27784 if (Roo.bootstrap.version == 4) {
27785 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27787 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27792 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27794 * @cfg {Roo.data.Store} dataSource
27795 * The underlying data store providing the paged data
27798 * @cfg {String/HTMLElement/Element} container
27799 * container The id or element that will contain the toolbar
27802 * @cfg {Boolean} displayInfo
27803 * True to display the displayMsg (defaults to false)
27806 * @cfg {Number} pageSize
27807 * The number of records to display per page (defaults to 20)
27811 * @cfg {String} displayMsg
27812 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27814 displayMsg : 'Displaying {0} - {1} of {2}',
27816 * @cfg {String} emptyMsg
27817 * The message to display when no records are found (defaults to "No data to display")
27819 emptyMsg : 'No data to display',
27821 * Customizable piece of the default paging text (defaults to "Page")
27824 beforePageText : "Page",
27826 * Customizable piece of the default paging text (defaults to "of %0")
27829 afterPageText : "of {0}",
27831 * Customizable piece of the default paging text (defaults to "First Page")
27834 firstText : "First Page",
27836 * Customizable piece of the default paging text (defaults to "Previous Page")
27839 prevText : "Previous Page",
27841 * Customizable piece of the default paging text (defaults to "Next Page")
27844 nextText : "Next Page",
27846 * Customizable piece of the default paging text (defaults to "Last Page")
27849 lastText : "Last Page",
27851 * Customizable piece of the default paging text (defaults to "Refresh")
27854 refreshText : "Refresh",
27858 onRender : function(ct, position)
27860 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27861 this.navgroup.parentId = this.id;
27862 this.navgroup.onRender(this.el, null);
27863 // add the buttons to the navgroup
27865 if(this.displayInfo){
27866 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27867 this.displayEl = this.el.select('.x-paging-info', true).first();
27868 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27869 // this.displayEl = navel.el.select('span',true).first();
27875 Roo.each(_this.buttons, function(e){ // this might need to use render????
27876 Roo.factory(e).render(_this.el);
27880 Roo.each(_this.toolbarItems, function(e) {
27881 _this.navgroup.addItem(e);
27885 this.first = this.navgroup.addItem({
27886 tooltip: this.firstText,
27887 cls: "prev btn-outline-secondary",
27888 html : ' <i class="fa fa-step-backward"></i>',
27890 preventDefault: true,
27891 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27894 this.prev = this.navgroup.addItem({
27895 tooltip: this.prevText,
27896 cls: "prev btn-outline-secondary",
27897 html : ' <i class="fa fa-backward"></i>',
27899 preventDefault: true,
27900 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27902 //this.addSeparator();
27905 var field = this.navgroup.addItem( {
27907 cls : 'x-paging-position btn-outline-secondary',
27909 html : this.beforePageText +
27910 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27911 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27914 this.field = field.el.select('input', true).first();
27915 this.field.on("keydown", this.onPagingKeydown, this);
27916 this.field.on("focus", function(){this.dom.select();});
27919 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27920 //this.field.setHeight(18);
27921 //this.addSeparator();
27922 this.next = this.navgroup.addItem({
27923 tooltip: this.nextText,
27924 cls: "next btn-outline-secondary",
27925 html : ' <i class="fa fa-forward"></i>',
27927 preventDefault: true,
27928 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27930 this.last = this.navgroup.addItem({
27931 tooltip: this.lastText,
27932 html : ' <i class="fa fa-step-forward"></i>',
27933 cls: "next btn-outline-secondary",
27935 preventDefault: true,
27936 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27938 //this.addSeparator();
27939 this.loading = this.navgroup.addItem({
27940 tooltip: this.refreshText,
27941 cls: "btn-outline-secondary",
27942 html : ' <i class="fa fa-refresh"></i>',
27943 preventDefault: true,
27944 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27950 updateInfo : function(){
27951 if(this.displayEl){
27952 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27953 var msg = count == 0 ?
27957 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27959 this.displayEl.update(msg);
27964 onLoad : function(ds, r, o)
27966 this.cursor = o.params && o.params.start ? o.params.start : 0;
27968 var d = this.getPageData(),
27973 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27974 this.field.dom.value = ap;
27975 this.first.setDisabled(ap == 1);
27976 this.prev.setDisabled(ap == 1);
27977 this.next.setDisabled(ap == ps);
27978 this.last.setDisabled(ap == ps);
27979 this.loading.enable();
27984 getPageData : function(){
27985 var total = this.ds.getTotalCount();
27988 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27989 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27994 onLoadError : function(){
27995 this.loading.enable();
27999 onPagingKeydown : function(e){
28000 var k = e.getKey();
28001 var d = this.getPageData();
28003 var v = this.field.dom.value, pageNum;
28004 if(!v || isNaN(pageNum = parseInt(v, 10))){
28005 this.field.dom.value = d.activePage;
28008 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28009 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28012 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))
28014 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28015 this.field.dom.value = pageNum;
28016 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28019 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28021 var v = this.field.dom.value, pageNum;
28022 var increment = (e.shiftKey) ? 10 : 1;
28023 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28026 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28027 this.field.dom.value = d.activePage;
28030 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28032 this.field.dom.value = parseInt(v, 10) + increment;
28033 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28034 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28041 beforeLoad : function(){
28043 this.loading.disable();
28048 onClick : function(which){
28057 ds.load({params:{start: 0, limit: this.pageSize}});
28060 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28063 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28066 var total = ds.getTotalCount();
28067 var extra = total % this.pageSize;
28068 var lastStart = extra ? (total - extra) : total-this.pageSize;
28069 ds.load({params:{start: lastStart, limit: this.pageSize}});
28072 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28078 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28079 * @param {Roo.data.Store} store The data store to unbind
28081 unbind : function(ds){
28082 ds.un("beforeload", this.beforeLoad, this);
28083 ds.un("load", this.onLoad, this);
28084 ds.un("loadexception", this.onLoadError, this);
28085 ds.un("remove", this.updateInfo, this);
28086 ds.un("add", this.updateInfo, this);
28087 this.ds = undefined;
28091 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28092 * @param {Roo.data.Store} store The data store to bind
28094 bind : function(ds){
28095 ds.on("beforeload", this.beforeLoad, this);
28096 ds.on("load", this.onLoad, this);
28097 ds.on("loadexception", this.onLoadError, this);
28098 ds.on("remove", this.updateInfo, this);
28099 ds.on("add", this.updateInfo, this);
28110 * @class Roo.bootstrap.MessageBar
28111 * @extends Roo.bootstrap.Component
28112 * Bootstrap MessageBar class
28113 * @cfg {String} html contents of the MessageBar
28114 * @cfg {String} weight (info | success | warning | danger) default info
28115 * @cfg {String} beforeClass insert the bar before the given class
28116 * @cfg {Boolean} closable (true | false) default false
28117 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28120 * Create a new Element
28121 * @param {Object} config The config object
28124 Roo.bootstrap.MessageBar = function(config){
28125 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28128 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28134 beforeClass: 'bootstrap-sticky-wrap',
28136 getAutoCreate : function(){
28140 cls: 'alert alert-dismissable alert-' + this.weight,
28145 html: this.html || ''
28151 cfg.cls += ' alert-messages-fixed';
28165 onRender : function(ct, position)
28167 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28170 var cfg = Roo.apply({}, this.getAutoCreate());
28174 cfg.cls += ' ' + this.cls;
28177 cfg.style = this.style;
28179 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28181 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28184 this.el.select('>button.close').on('click', this.hide, this);
28190 if (!this.rendered) {
28196 this.fireEvent('show', this);
28202 if (!this.rendered) {
28208 this.fireEvent('hide', this);
28211 update : function()
28213 // var e = this.el.dom.firstChild;
28215 // if(this.closable){
28216 // e = e.nextSibling;
28219 // e.data = this.html || '';
28221 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28237 * @class Roo.bootstrap.Graph
28238 * @extends Roo.bootstrap.Component
28239 * Bootstrap Graph class
28243 @cfg {String} graphtype bar | vbar | pie
28244 @cfg {number} g_x coodinator | centre x (pie)
28245 @cfg {number} g_y coodinator | centre y (pie)
28246 @cfg {number} g_r radius (pie)
28247 @cfg {number} g_height height of the chart (respected by all elements in the set)
28248 @cfg {number} g_width width of the chart (respected by all elements in the set)
28249 @cfg {Object} title The title of the chart
28252 -opts (object) options for the chart
28254 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28255 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28257 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.
28258 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28260 o stretch (boolean)
28262 -opts (object) options for the pie
28265 o startAngle (number)
28266 o endAngle (number)
28270 * Create a new Input
28271 * @param {Object} config The config object
28274 Roo.bootstrap.Graph = function(config){
28275 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28281 * The img click event for the img.
28282 * @param {Roo.EventObject} e
28288 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28299 //g_colors: this.colors,
28306 getAutoCreate : function(){
28317 onRender : function(ct,position){
28320 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28322 if (typeof(Raphael) == 'undefined') {
28323 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28327 this.raphael = Raphael(this.el.dom);
28329 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28330 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28331 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28332 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28334 r.text(160, 10, "Single Series Chart").attr(txtattr);
28335 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28336 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28337 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28339 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28340 r.barchart(330, 10, 300, 220, data1);
28341 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28342 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28345 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28346 // r.barchart(30, 30, 560, 250, xdata, {
28347 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28348 // axis : "0 0 1 1",
28349 // axisxlabels : xdata
28350 // //yvalues : cols,
28353 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28355 // this.load(null,xdata,{
28356 // axis : "0 0 1 1",
28357 // axisxlabels : xdata
28362 load : function(graphtype,xdata,opts)
28364 this.raphael.clear();
28366 graphtype = this.graphtype;
28371 var r = this.raphael,
28372 fin = function () {
28373 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28375 fout = function () {
28376 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28378 pfin = function() {
28379 this.sector.stop();
28380 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28383 this.label[0].stop();
28384 this.label[0].attr({ r: 7.5 });
28385 this.label[1].attr({ "font-weight": 800 });
28388 pfout = function() {
28389 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28392 this.label[0].animate({ r: 5 }, 500, "bounce");
28393 this.label[1].attr({ "font-weight": 400 });
28399 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28402 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28405 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28406 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28408 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28415 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28420 setTitle: function(o)
28425 initEvents: function() {
28428 this.el.on('click', this.onClick, this);
28432 onClick : function(e)
28434 Roo.log('img onclick');
28435 this.fireEvent('click', this, e);
28447 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28450 * @class Roo.bootstrap.dash.NumberBox
28451 * @extends Roo.bootstrap.Component
28452 * Bootstrap NumberBox class
28453 * @cfg {String} headline Box headline
28454 * @cfg {String} content Box content
28455 * @cfg {String} icon Box icon
28456 * @cfg {String} footer Footer text
28457 * @cfg {String} fhref Footer href
28460 * Create a new NumberBox
28461 * @param {Object} config The config object
28465 Roo.bootstrap.dash.NumberBox = function(config){
28466 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28470 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28479 getAutoCreate : function(){
28483 cls : 'small-box ',
28491 cls : 'roo-headline',
28492 html : this.headline
28496 cls : 'roo-content',
28497 html : this.content
28511 cls : 'ion ' + this.icon
28520 cls : 'small-box-footer',
28521 href : this.fhref || '#',
28525 cfg.cn.push(footer);
28532 onRender : function(ct,position){
28533 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28540 setHeadline: function (value)
28542 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28545 setFooter: function (value, href)
28547 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28550 this.el.select('a.small-box-footer',true).first().attr('href', href);
28555 setContent: function (value)
28557 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28560 initEvents: function()
28574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28577 * @class Roo.bootstrap.dash.TabBox
28578 * @extends Roo.bootstrap.Component
28579 * Bootstrap TabBox class
28580 * @cfg {String} title Title of the TabBox
28581 * @cfg {String} icon Icon of the TabBox
28582 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28583 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28586 * Create a new TabBox
28587 * @param {Object} config The config object
28591 Roo.bootstrap.dash.TabBox = function(config){
28592 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28597 * When a pane is added
28598 * @param {Roo.bootstrap.dash.TabPane} pane
28602 * @event activatepane
28603 * When a pane is activated
28604 * @param {Roo.bootstrap.dash.TabPane} pane
28606 "activatepane" : true
28614 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28619 tabScrollable : false,
28621 getChildContainer : function()
28623 return this.el.select('.tab-content', true).first();
28626 getAutoCreate : function(){
28630 cls: 'pull-left header',
28638 cls: 'fa ' + this.icon
28644 cls: 'nav nav-tabs pull-right',
28650 if(this.tabScrollable){
28657 cls: 'nav nav-tabs pull-right',
28668 cls: 'nav-tabs-custom',
28673 cls: 'tab-content no-padding',
28681 initEvents : function()
28683 //Roo.log('add add pane handler');
28684 this.on('addpane', this.onAddPane, this);
28687 * Updates the box title
28688 * @param {String} html to set the title to.
28690 setTitle : function(value)
28692 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28694 onAddPane : function(pane)
28696 this.panes.push(pane);
28697 //Roo.log('addpane');
28699 // tabs are rendere left to right..
28700 if(!this.showtabs){
28704 var ctr = this.el.select('.nav-tabs', true).first();
28707 var existing = ctr.select('.nav-tab',true);
28708 var qty = existing.getCount();;
28711 var tab = ctr.createChild({
28713 cls : 'nav-tab' + (qty ? '' : ' active'),
28721 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28724 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28726 pane.el.addClass('active');
28731 onTabClick : function(ev,un,ob,pane)
28733 //Roo.log('tab - prev default');
28734 ev.preventDefault();
28737 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28738 pane.tab.addClass('active');
28739 //Roo.log(pane.title);
28740 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28741 // technically we should have a deactivate event.. but maybe add later.
28742 // and it should not de-activate the selected tab...
28743 this.fireEvent('activatepane', pane);
28744 pane.el.addClass('active');
28745 pane.fireEvent('activate');
28750 getActivePane : function()
28753 Roo.each(this.panes, function(p) {
28754 if(p.el.hasClass('active')){
28775 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28777 * @class Roo.bootstrap.TabPane
28778 * @extends Roo.bootstrap.Component
28779 * Bootstrap TabPane class
28780 * @cfg {Boolean} active (false | true) Default false
28781 * @cfg {String} title title of panel
28785 * Create a new TabPane
28786 * @param {Object} config The config object
28789 Roo.bootstrap.dash.TabPane = function(config){
28790 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28796 * When a pane is activated
28797 * @param {Roo.bootstrap.dash.TabPane} pane
28804 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28809 // the tabBox that this is attached to.
28812 getAutoCreate : function()
28820 cfg.cls += ' active';
28825 initEvents : function()
28827 //Roo.log('trigger add pane handler');
28828 this.parent().fireEvent('addpane', this)
28832 * Updates the tab title
28833 * @param {String} html to set the title to.
28835 setTitle: function(str)
28841 this.tab.select('a', true).first().dom.innerHTML = str;
28858 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28861 * @class Roo.bootstrap.menu.Menu
28862 * @extends Roo.bootstrap.Component
28863 * Bootstrap Menu class - container for Menu
28864 * @cfg {String} html Text of the menu
28865 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28866 * @cfg {String} icon Font awesome icon
28867 * @cfg {String} pos Menu align to (top | bottom) default bottom
28871 * Create a new Menu
28872 * @param {Object} config The config object
28876 Roo.bootstrap.menu.Menu = function(config){
28877 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28881 * @event beforeshow
28882 * Fires before this menu is displayed
28883 * @param {Roo.bootstrap.menu.Menu} this
28887 * @event beforehide
28888 * Fires before this menu is hidden
28889 * @param {Roo.bootstrap.menu.Menu} this
28894 * Fires after this menu is displayed
28895 * @param {Roo.bootstrap.menu.Menu} this
28900 * Fires after this menu is hidden
28901 * @param {Roo.bootstrap.menu.Menu} this
28906 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28907 * @param {Roo.bootstrap.menu.Menu} this
28908 * @param {Roo.EventObject} e
28915 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28919 weight : 'default',
28924 getChildContainer : function() {
28925 if(this.isSubMenu){
28929 return this.el.select('ul.dropdown-menu', true).first();
28932 getAutoCreate : function()
28937 cls : 'roo-menu-text',
28945 cls : 'fa ' + this.icon
28956 cls : 'dropdown-button btn btn-' + this.weight,
28961 cls : 'dropdown-toggle btn btn-' + this.weight,
28971 cls : 'dropdown-menu'
28977 if(this.pos == 'top'){
28978 cfg.cls += ' dropup';
28981 if(this.isSubMenu){
28984 cls : 'dropdown-menu'
28991 onRender : function(ct, position)
28993 this.isSubMenu = ct.hasClass('dropdown-submenu');
28995 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28998 initEvents : function()
29000 if(this.isSubMenu){
29004 this.hidden = true;
29006 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29007 this.triggerEl.on('click', this.onTriggerPress, this);
29009 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29010 this.buttonEl.on('click', this.onClick, this);
29016 if(this.isSubMenu){
29020 return this.el.select('ul.dropdown-menu', true).first();
29023 onClick : function(e)
29025 this.fireEvent("click", this, e);
29028 onTriggerPress : function(e)
29030 if (this.isVisible()) {
29037 isVisible : function(){
29038 return !this.hidden;
29043 this.fireEvent("beforeshow", this);
29045 this.hidden = false;
29046 this.el.addClass('open');
29048 Roo.get(document).on("mouseup", this.onMouseUp, this);
29050 this.fireEvent("show", this);
29057 this.fireEvent("beforehide", this);
29059 this.hidden = true;
29060 this.el.removeClass('open');
29062 Roo.get(document).un("mouseup", this.onMouseUp);
29064 this.fireEvent("hide", this);
29067 onMouseUp : function()
29081 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29084 * @class Roo.bootstrap.menu.Item
29085 * @extends Roo.bootstrap.Component
29086 * Bootstrap MenuItem class
29087 * @cfg {Boolean} submenu (true | false) default false
29088 * @cfg {String} html text of the item
29089 * @cfg {String} href the link
29090 * @cfg {Boolean} disable (true | false) default false
29091 * @cfg {Boolean} preventDefault (true | false) default true
29092 * @cfg {String} icon Font awesome icon
29093 * @cfg {String} pos Submenu align to (left | right) default right
29097 * Create a new Item
29098 * @param {Object} config The config object
29102 Roo.bootstrap.menu.Item = function(config){
29103 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29107 * Fires when the mouse is hovering over this menu
29108 * @param {Roo.bootstrap.menu.Item} this
29109 * @param {Roo.EventObject} e
29114 * Fires when the mouse exits this menu
29115 * @param {Roo.bootstrap.menu.Item} this
29116 * @param {Roo.EventObject} e
29122 * The raw click event for the entire grid.
29123 * @param {Roo.EventObject} e
29129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29134 preventDefault: true,
29139 getAutoCreate : function()
29144 cls : 'roo-menu-item-text',
29152 cls : 'fa ' + this.icon
29161 href : this.href || '#',
29168 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29172 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29174 if(this.pos == 'left'){
29175 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29182 initEvents : function()
29184 this.el.on('mouseover', this.onMouseOver, this);
29185 this.el.on('mouseout', this.onMouseOut, this);
29187 this.el.select('a', true).first().on('click', this.onClick, this);
29191 onClick : function(e)
29193 if(this.preventDefault){
29194 e.preventDefault();
29197 this.fireEvent("click", this, e);
29200 onMouseOver : function(e)
29202 if(this.submenu && this.pos == 'left'){
29203 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29206 this.fireEvent("mouseover", this, e);
29209 onMouseOut : function(e)
29211 this.fireEvent("mouseout", this, e);
29223 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29226 * @class Roo.bootstrap.menu.Separator
29227 * @extends Roo.bootstrap.Component
29228 * Bootstrap Separator class
29231 * Create a new Separator
29232 * @param {Object} config The config object
29236 Roo.bootstrap.menu.Separator = function(config){
29237 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29240 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29242 getAutoCreate : function(){
29245 cls: 'dropdown-divider divider'
29263 * @class Roo.bootstrap.Tooltip
29264 * Bootstrap Tooltip class
29265 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29266 * to determine which dom element triggers the tooltip.
29268 * It needs to add support for additional attributes like tooltip-position
29271 * Create a new Toolti
29272 * @param {Object} config The config object
29275 Roo.bootstrap.Tooltip = function(config){
29276 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29278 this.alignment = Roo.bootstrap.Tooltip.alignment;
29280 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29281 this.alignment = config.alignment;
29286 Roo.apply(Roo.bootstrap.Tooltip, {
29288 * @function init initialize tooltip monitoring.
29292 currentTip : false,
29293 currentRegion : false,
29299 Roo.get(document).on('mouseover', this.enter ,this);
29300 Roo.get(document).on('mouseout', this.leave, this);
29303 this.currentTip = new Roo.bootstrap.Tooltip();
29306 enter : function(ev)
29308 var dom = ev.getTarget();
29310 //Roo.log(['enter',dom]);
29311 var el = Roo.fly(dom);
29312 if (this.currentEl) {
29314 //Roo.log(this.currentEl);
29315 //Roo.log(this.currentEl.contains(dom));
29316 if (this.currentEl == el) {
29319 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29325 if (this.currentTip.el) {
29326 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29330 if(!el || el.dom == document){
29336 if (!el.attr('tooltip')) {
29337 pel = el.findParent("[tooltip]");
29339 bindEl = Roo.get(pel);
29345 // you can not look for children, as if el is the body.. then everythign is the child..
29346 if (!pel && !el.attr('tooltip')) { //
29347 if (!el.select("[tooltip]").elements.length) {
29350 // is the mouse over this child...?
29351 bindEl = el.select("[tooltip]").first();
29352 var xy = ev.getXY();
29353 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29354 //Roo.log("not in region.");
29357 //Roo.log("child element over..");
29360 this.currentEl = el;
29361 this.currentTip.bind(bindEl);
29362 this.currentRegion = Roo.lib.Region.getRegion(dom);
29363 this.currentTip.enter();
29366 leave : function(ev)
29368 var dom = ev.getTarget();
29369 //Roo.log(['leave',dom]);
29370 if (!this.currentEl) {
29375 if (dom != this.currentEl.dom) {
29378 var xy = ev.getXY();
29379 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29382 // only activate leave if mouse cursor is outside... bounding box..
29387 if (this.currentTip) {
29388 this.currentTip.leave();
29390 //Roo.log('clear currentEl');
29391 this.currentEl = false;
29396 'left' : ['r-l', [-2,0], 'right'],
29397 'right' : ['l-r', [2,0], 'left'],
29398 'bottom' : ['t-b', [0,2], 'top'],
29399 'top' : [ 'b-t', [0,-2], 'bottom']
29405 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29410 delay : null, // can be { show : 300 , hide: 500}
29414 hoverState : null, //???
29416 placement : 'bottom',
29420 getAutoCreate : function(){
29427 cls : 'tooltip-arrow arrow'
29430 cls : 'tooltip-inner'
29437 bind : function(el)
29442 initEvents : function()
29444 this.arrowEl = this.el.select('.arrow', true).first();
29445 this.innerEl = this.el.select('.tooltip-inner', true).first();
29448 enter : function () {
29450 if (this.timeout != null) {
29451 clearTimeout(this.timeout);
29454 this.hoverState = 'in';
29455 //Roo.log("enter - show");
29456 if (!this.delay || !this.delay.show) {
29461 this.timeout = setTimeout(function () {
29462 if (_t.hoverState == 'in') {
29465 }, this.delay.show);
29469 clearTimeout(this.timeout);
29471 this.hoverState = 'out';
29472 if (!this.delay || !this.delay.hide) {
29478 this.timeout = setTimeout(function () {
29479 //Roo.log("leave - timeout");
29481 if (_t.hoverState == 'out') {
29483 Roo.bootstrap.Tooltip.currentEl = false;
29488 show : function (msg)
29491 this.render(document.body);
29494 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29496 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29498 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29500 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29501 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29503 var placement = typeof this.placement == 'function' ?
29504 this.placement.call(this, this.el, on_el) :
29507 var autoToken = /\s?auto?\s?/i;
29508 var autoPlace = autoToken.test(placement);
29510 placement = placement.replace(autoToken, '') || 'top';
29514 //this.el.setXY([0,0]);
29516 //this.el.dom.style.display='block';
29518 //this.el.appendTo(on_el);
29520 var p = this.getPosition();
29521 var box = this.el.getBox();
29527 var align = this.alignment[placement];
29529 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29531 if(placement == 'top' || placement == 'bottom'){
29533 placement = 'right';
29536 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29537 placement = 'left';
29540 var scroll = Roo.select('body', true).first().getScroll();
29542 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29546 align = this.alignment[placement];
29548 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29552 var elems = document.getElementsByTagName('div');
29553 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29554 for (var i = 0; i < elems.length; i++) {
29555 var zindex = Number.parseInt(
29556 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29559 if (zindex > highest) {
29566 this.el.dom.style.zIndex = highest;
29568 this.el.alignTo(this.bindEl, align[0],align[1]);
29569 //var arrow = this.el.select('.arrow',true).first();
29570 //arrow.set(align[2],
29572 this.el.addClass(placement);
29573 this.el.addClass("bs-tooltip-"+ placement);
29575 this.el.addClass('in fade show');
29577 this.hoverState = null;
29579 if (this.el.hasClass('fade')) {
29594 //this.el.setXY([0,0]);
29595 this.el.removeClass(['show', 'in']);
29611 * @class Roo.bootstrap.LocationPicker
29612 * @extends Roo.bootstrap.Component
29613 * Bootstrap LocationPicker class
29614 * @cfg {Number} latitude Position when init default 0
29615 * @cfg {Number} longitude Position when init default 0
29616 * @cfg {Number} zoom default 15
29617 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29618 * @cfg {Boolean} mapTypeControl default false
29619 * @cfg {Boolean} disableDoubleClickZoom default false
29620 * @cfg {Boolean} scrollwheel default true
29621 * @cfg {Boolean} streetViewControl default false
29622 * @cfg {Number} radius default 0
29623 * @cfg {String} locationName
29624 * @cfg {Boolean} draggable default true
29625 * @cfg {Boolean} enableAutocomplete default false
29626 * @cfg {Boolean} enableReverseGeocode default true
29627 * @cfg {String} markerTitle
29630 * Create a new LocationPicker
29631 * @param {Object} config The config object
29635 Roo.bootstrap.LocationPicker = function(config){
29637 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29642 * Fires when the picker initialized.
29643 * @param {Roo.bootstrap.LocationPicker} this
29644 * @param {Google Location} location
29648 * @event positionchanged
29649 * Fires when the picker position changed.
29650 * @param {Roo.bootstrap.LocationPicker} this
29651 * @param {Google Location} location
29653 positionchanged : true,
29656 * Fires when the map resize.
29657 * @param {Roo.bootstrap.LocationPicker} this
29662 * Fires when the map show.
29663 * @param {Roo.bootstrap.LocationPicker} this
29668 * Fires when the map hide.
29669 * @param {Roo.bootstrap.LocationPicker} this
29674 * Fires when click the map.
29675 * @param {Roo.bootstrap.LocationPicker} this
29676 * @param {Map event} e
29680 * @event mapRightClick
29681 * Fires when right click the map.
29682 * @param {Roo.bootstrap.LocationPicker} this
29683 * @param {Map event} e
29685 mapRightClick : true,
29687 * @event markerClick
29688 * Fires when click the marker.
29689 * @param {Roo.bootstrap.LocationPicker} this
29690 * @param {Map event} e
29692 markerClick : true,
29694 * @event markerRightClick
29695 * Fires when right click the marker.
29696 * @param {Roo.bootstrap.LocationPicker} this
29697 * @param {Map event} e
29699 markerRightClick : true,
29701 * @event OverlayViewDraw
29702 * Fires when OverlayView Draw
29703 * @param {Roo.bootstrap.LocationPicker} this
29705 OverlayViewDraw : true,
29707 * @event OverlayViewOnAdd
29708 * Fires when OverlayView Draw
29709 * @param {Roo.bootstrap.LocationPicker} this
29711 OverlayViewOnAdd : true,
29713 * @event OverlayViewOnRemove
29714 * Fires when OverlayView Draw
29715 * @param {Roo.bootstrap.LocationPicker} this
29717 OverlayViewOnRemove : true,
29719 * @event OverlayViewShow
29720 * Fires when OverlayView Draw
29721 * @param {Roo.bootstrap.LocationPicker} this
29722 * @param {Pixel} cpx
29724 OverlayViewShow : true,
29726 * @event OverlayViewHide
29727 * Fires when OverlayView Draw
29728 * @param {Roo.bootstrap.LocationPicker} this
29730 OverlayViewHide : true,
29732 * @event loadexception
29733 * Fires when load google lib failed.
29734 * @param {Roo.bootstrap.LocationPicker} this
29736 loadexception : true
29741 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29743 gMapContext: false,
29749 mapTypeControl: false,
29750 disableDoubleClickZoom: false,
29752 streetViewControl: false,
29756 enableAutocomplete: false,
29757 enableReverseGeocode: true,
29760 getAutoCreate: function()
29765 cls: 'roo-location-picker'
29771 initEvents: function(ct, position)
29773 if(!this.el.getWidth() || this.isApplied()){
29777 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29782 initial: function()
29784 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29785 this.fireEvent('loadexception', this);
29789 if(!this.mapTypeId){
29790 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29793 this.gMapContext = this.GMapContext();
29795 this.initOverlayView();
29797 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29801 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29802 _this.setPosition(_this.gMapContext.marker.position);
29805 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29806 _this.fireEvent('mapClick', this, event);
29810 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29811 _this.fireEvent('mapRightClick', this, event);
29815 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29816 _this.fireEvent('markerClick', this, event);
29820 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29821 _this.fireEvent('markerRightClick', this, event);
29825 this.setPosition(this.gMapContext.location);
29827 this.fireEvent('initial', this, this.gMapContext.location);
29830 initOverlayView: function()
29834 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29838 _this.fireEvent('OverlayViewDraw', _this);
29843 _this.fireEvent('OverlayViewOnAdd', _this);
29846 onRemove: function()
29848 _this.fireEvent('OverlayViewOnRemove', _this);
29851 show: function(cpx)
29853 _this.fireEvent('OverlayViewShow', _this, cpx);
29858 _this.fireEvent('OverlayViewHide', _this);
29864 fromLatLngToContainerPixel: function(event)
29866 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29869 isApplied: function()
29871 return this.getGmapContext() == false ? false : true;
29874 getGmapContext: function()
29876 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29879 GMapContext: function()
29881 var position = new google.maps.LatLng(this.latitude, this.longitude);
29883 var _map = new google.maps.Map(this.el.dom, {
29886 mapTypeId: this.mapTypeId,
29887 mapTypeControl: this.mapTypeControl,
29888 disableDoubleClickZoom: this.disableDoubleClickZoom,
29889 scrollwheel: this.scrollwheel,
29890 streetViewControl: this.streetViewControl,
29891 locationName: this.locationName,
29892 draggable: this.draggable,
29893 enableAutocomplete: this.enableAutocomplete,
29894 enableReverseGeocode: this.enableReverseGeocode
29897 var _marker = new google.maps.Marker({
29898 position: position,
29900 title: this.markerTitle,
29901 draggable: this.draggable
29908 location: position,
29909 radius: this.radius,
29910 locationName: this.locationName,
29911 addressComponents: {
29912 formatted_address: null,
29913 addressLine1: null,
29914 addressLine2: null,
29916 streetNumber: null,
29920 stateOrProvince: null
29923 domContainer: this.el.dom,
29924 geodecoder: new google.maps.Geocoder()
29928 drawCircle: function(center, radius, options)
29930 if (this.gMapContext.circle != null) {
29931 this.gMapContext.circle.setMap(null);
29935 options = Roo.apply({}, options, {
29936 strokeColor: "#0000FF",
29937 strokeOpacity: .35,
29939 fillColor: "#0000FF",
29943 options.map = this.gMapContext.map;
29944 options.radius = radius;
29945 options.center = center;
29946 this.gMapContext.circle = new google.maps.Circle(options);
29947 return this.gMapContext.circle;
29953 setPosition: function(location)
29955 this.gMapContext.location = location;
29956 this.gMapContext.marker.setPosition(location);
29957 this.gMapContext.map.panTo(location);
29958 this.drawCircle(location, this.gMapContext.radius, {});
29962 if (this.gMapContext.settings.enableReverseGeocode) {
29963 this.gMapContext.geodecoder.geocode({
29964 latLng: this.gMapContext.location
29965 }, function(results, status) {
29967 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29968 _this.gMapContext.locationName = results[0].formatted_address;
29969 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29971 _this.fireEvent('positionchanged', this, location);
29978 this.fireEvent('positionchanged', this, location);
29983 google.maps.event.trigger(this.gMapContext.map, "resize");
29985 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29987 this.fireEvent('resize', this);
29990 setPositionByLatLng: function(latitude, longitude)
29992 this.setPosition(new google.maps.LatLng(latitude, longitude));
29995 getCurrentPosition: function()
29998 latitude: this.gMapContext.location.lat(),
29999 longitude: this.gMapContext.location.lng()
30003 getAddressName: function()
30005 return this.gMapContext.locationName;
30008 getAddressComponents: function()
30010 return this.gMapContext.addressComponents;
30013 address_component_from_google_geocode: function(address_components)
30017 for (var i = 0; i < address_components.length; i++) {
30018 var component = address_components[i];
30019 if (component.types.indexOf("postal_code") >= 0) {
30020 result.postalCode = component.short_name;
30021 } else if (component.types.indexOf("street_number") >= 0) {
30022 result.streetNumber = component.short_name;
30023 } else if (component.types.indexOf("route") >= 0) {
30024 result.streetName = component.short_name;
30025 } else if (component.types.indexOf("neighborhood") >= 0) {
30026 result.city = component.short_name;
30027 } else if (component.types.indexOf("locality") >= 0) {
30028 result.city = component.short_name;
30029 } else if (component.types.indexOf("sublocality") >= 0) {
30030 result.district = component.short_name;
30031 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30032 result.stateOrProvince = component.short_name;
30033 } else if (component.types.indexOf("country") >= 0) {
30034 result.country = component.short_name;
30038 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30039 result.addressLine2 = "";
30043 setZoomLevel: function(zoom)
30045 this.gMapContext.map.setZoom(zoom);
30058 this.fireEvent('show', this);
30069 this.fireEvent('hide', this);
30074 Roo.apply(Roo.bootstrap.LocationPicker, {
30076 OverlayView : function(map, options)
30078 options = options || {};
30085 * @class Roo.bootstrap.Alert
30086 * @extends Roo.bootstrap.Component
30087 * Bootstrap Alert class - shows an alert area box
30089 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30090 Enter a valid email address
30093 * @cfg {String} title The title of alert
30094 * @cfg {String} html The content of alert
30095 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30096 * @cfg {String} fa font-awesomeicon
30097 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30098 * @cfg {Boolean} close true to show a x closer
30102 * Create a new alert
30103 * @param {Object} config The config object
30107 Roo.bootstrap.Alert = function(config){
30108 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30112 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30118 faicon: false, // BC
30122 getAutoCreate : function()
30134 style : this.close ? '' : 'display:none'
30138 cls : 'roo-alert-icon'
30143 cls : 'roo-alert-title',
30148 cls : 'roo-alert-text',
30155 cfg.cn[0].cls += ' fa ' + this.faicon;
30158 cfg.cn[0].cls += ' fa ' + this.fa;
30162 cfg.cls += ' alert-' + this.weight;
30168 initEvents: function()
30170 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30171 this.titleEl = this.el.select('.roo-alert-title',true).first();
30172 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30173 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30174 if (this.seconds > 0) {
30175 this.hide.defer(this.seconds, this);
30179 * Set the Title Message HTML
30180 * @param {String} html
30182 setTitle : function(str)
30184 this.titleEl.dom.innerHTML = str;
30188 * Set the Body Message HTML
30189 * @param {String} html
30191 setHtml : function(str)
30193 this.htmlEl.dom.innerHTML = str;
30196 * Set the Weight of the alert
30197 * @param {String} (success|info|warning|danger) weight
30200 setWeight : function(weight)
30203 this.el.removeClass('alert-' + this.weight);
30206 this.weight = weight;
30208 this.el.addClass('alert-' + this.weight);
30211 * Set the Icon of the alert
30212 * @param {String} see fontawsome names (name without the 'fa-' bit)
30214 setIcon : function(icon)
30217 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30220 this.faicon = icon;
30222 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30247 * @class Roo.bootstrap.UploadCropbox
30248 * @extends Roo.bootstrap.Component
30249 * Bootstrap UploadCropbox class
30250 * @cfg {String} emptyText show when image has been loaded
30251 * @cfg {String} rotateNotify show when image too small to rotate
30252 * @cfg {Number} errorTimeout default 3000
30253 * @cfg {Number} minWidth default 300
30254 * @cfg {Number} minHeight default 300
30255 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30256 * @cfg {Boolean} isDocument (true|false) default false
30257 * @cfg {String} url action url
30258 * @cfg {String} paramName default 'imageUpload'
30259 * @cfg {String} method default POST
30260 * @cfg {Boolean} loadMask (true|false) default true
30261 * @cfg {Boolean} loadingText default 'Loading...'
30264 * Create a new UploadCropbox
30265 * @param {Object} config The config object
30268 Roo.bootstrap.UploadCropbox = function(config){
30269 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30273 * @event beforeselectfile
30274 * Fire before select file
30275 * @param {Roo.bootstrap.UploadCropbox} this
30277 "beforeselectfile" : true,
30280 * Fire after initEvent
30281 * @param {Roo.bootstrap.UploadCropbox} this
30286 * Fire after initEvent
30287 * @param {Roo.bootstrap.UploadCropbox} this
30288 * @param {String} data
30293 * Fire when preparing the file data
30294 * @param {Roo.bootstrap.UploadCropbox} this
30295 * @param {Object} file
30300 * Fire when get exception
30301 * @param {Roo.bootstrap.UploadCropbox} this
30302 * @param {XMLHttpRequest} xhr
30304 "exception" : true,
30306 * @event beforeloadcanvas
30307 * Fire before load the canvas
30308 * @param {Roo.bootstrap.UploadCropbox} this
30309 * @param {String} src
30311 "beforeloadcanvas" : true,
30314 * Fire when trash image
30315 * @param {Roo.bootstrap.UploadCropbox} this
30320 * Fire when download the image
30321 * @param {Roo.bootstrap.UploadCropbox} this
30325 * @event footerbuttonclick
30326 * Fire when footerbuttonclick
30327 * @param {Roo.bootstrap.UploadCropbox} this
30328 * @param {String} type
30330 "footerbuttonclick" : true,
30334 * @param {Roo.bootstrap.UploadCropbox} this
30339 * Fire when rotate the image
30340 * @param {Roo.bootstrap.UploadCropbox} this
30341 * @param {String} pos
30346 * Fire when inspect the file
30347 * @param {Roo.bootstrap.UploadCropbox} this
30348 * @param {Object} file
30353 * Fire when xhr upload the file
30354 * @param {Roo.bootstrap.UploadCropbox} this
30355 * @param {Object} data
30360 * Fire when arrange the file data
30361 * @param {Roo.bootstrap.UploadCropbox} this
30362 * @param {Object} formData
30367 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30370 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30372 emptyText : 'Click to upload image',
30373 rotateNotify : 'Image is too small to rotate',
30374 errorTimeout : 3000,
30388 cropType : 'image/jpeg',
30390 canvasLoaded : false,
30391 isDocument : false,
30393 paramName : 'imageUpload',
30395 loadingText : 'Loading...',
30398 getAutoCreate : function()
30402 cls : 'roo-upload-cropbox',
30406 cls : 'roo-upload-cropbox-selector',
30411 cls : 'roo-upload-cropbox-body',
30412 style : 'cursor:pointer',
30416 cls : 'roo-upload-cropbox-preview'
30420 cls : 'roo-upload-cropbox-thumb'
30424 cls : 'roo-upload-cropbox-empty-notify',
30425 html : this.emptyText
30429 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30430 html : this.rotateNotify
30436 cls : 'roo-upload-cropbox-footer',
30439 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30449 onRender : function(ct, position)
30451 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30453 if (this.buttons.length) {
30455 Roo.each(this.buttons, function(bb) {
30457 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30459 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30465 this.maskEl = this.el;
30469 initEvents : function()
30471 this.urlAPI = (window.createObjectURL && window) ||
30472 (window.URL && URL.revokeObjectURL && URL) ||
30473 (window.webkitURL && webkitURL);
30475 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30476 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30478 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30479 this.selectorEl.hide();
30481 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30482 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30484 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30485 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30486 this.thumbEl.hide();
30488 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30489 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30491 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30492 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30493 this.errorEl.hide();
30495 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30496 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30497 this.footerEl.hide();
30499 this.setThumbBoxSize();
30505 this.fireEvent('initial', this);
30512 window.addEventListener("resize", function() { _this.resize(); } );
30514 this.bodyEl.on('click', this.beforeSelectFile, this);
30517 this.bodyEl.on('touchstart', this.onTouchStart, this);
30518 this.bodyEl.on('touchmove', this.onTouchMove, this);
30519 this.bodyEl.on('touchend', this.onTouchEnd, this);
30523 this.bodyEl.on('mousedown', this.onMouseDown, this);
30524 this.bodyEl.on('mousemove', this.onMouseMove, this);
30525 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30526 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30527 Roo.get(document).on('mouseup', this.onMouseUp, this);
30530 this.selectorEl.on('change', this.onFileSelected, this);
30536 this.baseScale = 1;
30538 this.baseRotate = 1;
30539 this.dragable = false;
30540 this.pinching = false;
30543 this.cropData = false;
30544 this.notifyEl.dom.innerHTML = this.emptyText;
30546 this.selectorEl.dom.value = '';
30550 resize : function()
30552 if(this.fireEvent('resize', this) != false){
30553 this.setThumbBoxPosition();
30554 this.setCanvasPosition();
30558 onFooterButtonClick : function(e, el, o, type)
30561 case 'rotate-left' :
30562 this.onRotateLeft(e);
30564 case 'rotate-right' :
30565 this.onRotateRight(e);
30568 this.beforeSelectFile(e);
30583 this.fireEvent('footerbuttonclick', this, type);
30586 beforeSelectFile : function(e)
30588 e.preventDefault();
30590 if(this.fireEvent('beforeselectfile', this) != false){
30591 this.selectorEl.dom.click();
30595 onFileSelected : function(e)
30597 e.preventDefault();
30599 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30603 var file = this.selectorEl.dom.files[0];
30605 if(this.fireEvent('inspect', this, file) != false){
30606 this.prepare(file);
30611 trash : function(e)
30613 this.fireEvent('trash', this);
30616 download : function(e)
30618 this.fireEvent('download', this);
30621 loadCanvas : function(src)
30623 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30627 this.imageEl = document.createElement('img');
30631 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30633 this.imageEl.src = src;
30637 onLoadCanvas : function()
30639 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30640 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30642 this.bodyEl.un('click', this.beforeSelectFile, this);
30644 this.notifyEl.hide();
30645 this.thumbEl.show();
30646 this.footerEl.show();
30648 this.baseRotateLevel();
30650 if(this.isDocument){
30651 this.setThumbBoxSize();
30654 this.setThumbBoxPosition();
30656 this.baseScaleLevel();
30662 this.canvasLoaded = true;
30665 this.maskEl.unmask();
30670 setCanvasPosition : function()
30672 if(!this.canvasEl){
30676 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30677 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30679 this.previewEl.setLeft(pw);
30680 this.previewEl.setTop(ph);
30684 onMouseDown : function(e)
30688 this.dragable = true;
30689 this.pinching = false;
30691 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30692 this.dragable = false;
30696 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30697 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30701 onMouseMove : function(e)
30705 if(!this.canvasLoaded){
30709 if (!this.dragable){
30713 var minX = Math.ceil(this.thumbEl.getLeft(true));
30714 var minY = Math.ceil(this.thumbEl.getTop(true));
30716 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30717 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30719 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30720 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30722 x = x - this.mouseX;
30723 y = y - this.mouseY;
30725 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30726 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30728 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30729 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30731 this.previewEl.setLeft(bgX);
30732 this.previewEl.setTop(bgY);
30734 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30735 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30738 onMouseUp : function(e)
30742 this.dragable = false;
30745 onMouseWheel : function(e)
30749 this.startScale = this.scale;
30751 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30753 if(!this.zoomable()){
30754 this.scale = this.startScale;
30763 zoomable : function()
30765 var minScale = this.thumbEl.getWidth() / this.minWidth;
30767 if(this.minWidth < this.minHeight){
30768 minScale = this.thumbEl.getHeight() / this.minHeight;
30771 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30772 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30776 (this.rotate == 0 || this.rotate == 180) &&
30778 width > this.imageEl.OriginWidth ||
30779 height > this.imageEl.OriginHeight ||
30780 (width < this.minWidth && height < this.minHeight)
30788 (this.rotate == 90 || this.rotate == 270) &&
30790 width > this.imageEl.OriginWidth ||
30791 height > this.imageEl.OriginHeight ||
30792 (width < this.minHeight && height < this.minWidth)
30799 !this.isDocument &&
30800 (this.rotate == 0 || this.rotate == 180) &&
30802 width < this.minWidth ||
30803 width > this.imageEl.OriginWidth ||
30804 height < this.minHeight ||
30805 height > this.imageEl.OriginHeight
30812 !this.isDocument &&
30813 (this.rotate == 90 || this.rotate == 270) &&
30815 width < this.minHeight ||
30816 width > this.imageEl.OriginWidth ||
30817 height < this.minWidth ||
30818 height > this.imageEl.OriginHeight
30828 onRotateLeft : function(e)
30830 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30832 var minScale = this.thumbEl.getWidth() / this.minWidth;
30834 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30835 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30837 this.startScale = this.scale;
30839 while (this.getScaleLevel() < minScale){
30841 this.scale = this.scale + 1;
30843 if(!this.zoomable()){
30848 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30849 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30854 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30861 this.scale = this.startScale;
30863 this.onRotateFail();
30868 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30870 if(this.isDocument){
30871 this.setThumbBoxSize();
30872 this.setThumbBoxPosition();
30873 this.setCanvasPosition();
30878 this.fireEvent('rotate', this, 'left');
30882 onRotateRight : function(e)
30884 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30886 var minScale = this.thumbEl.getWidth() / this.minWidth;
30888 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30889 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30891 this.startScale = this.scale;
30893 while (this.getScaleLevel() < minScale){
30895 this.scale = this.scale + 1;
30897 if(!this.zoomable()){
30902 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30903 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30908 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30915 this.scale = this.startScale;
30917 this.onRotateFail();
30922 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30924 if(this.isDocument){
30925 this.setThumbBoxSize();
30926 this.setThumbBoxPosition();
30927 this.setCanvasPosition();
30932 this.fireEvent('rotate', this, 'right');
30935 onRotateFail : function()
30937 this.errorEl.show(true);
30941 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30946 this.previewEl.dom.innerHTML = '';
30948 var canvasEl = document.createElement("canvas");
30950 var contextEl = canvasEl.getContext("2d");
30952 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30953 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30954 var center = this.imageEl.OriginWidth / 2;
30956 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30957 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30958 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30959 center = this.imageEl.OriginHeight / 2;
30962 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30964 contextEl.translate(center, center);
30965 contextEl.rotate(this.rotate * Math.PI / 180);
30967 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30969 this.canvasEl = document.createElement("canvas");
30971 this.contextEl = this.canvasEl.getContext("2d");
30973 switch (this.rotate) {
30976 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30977 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30979 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30984 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30985 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30987 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30988 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);
30992 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30997 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30998 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31000 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31001 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);
31005 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);
31010 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31011 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31013 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31014 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31018 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);
31025 this.previewEl.appendChild(this.canvasEl);
31027 this.setCanvasPosition();
31032 if(!this.canvasLoaded){
31036 var imageCanvas = document.createElement("canvas");
31038 var imageContext = imageCanvas.getContext("2d");
31040 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31041 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31043 var center = imageCanvas.width / 2;
31045 imageContext.translate(center, center);
31047 imageContext.rotate(this.rotate * Math.PI / 180);
31049 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31051 var canvas = document.createElement("canvas");
31053 var context = canvas.getContext("2d");
31055 canvas.width = this.minWidth;
31056 canvas.height = this.minHeight;
31058 switch (this.rotate) {
31061 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31062 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31064 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31065 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31067 var targetWidth = this.minWidth - 2 * x;
31068 var targetHeight = this.minHeight - 2 * y;
31072 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31073 scale = targetWidth / width;
31076 if(x > 0 && y == 0){
31077 scale = targetHeight / height;
31080 if(x > 0 && y > 0){
31081 scale = targetWidth / width;
31083 if(width < height){
31084 scale = targetHeight / height;
31088 context.scale(scale, scale);
31090 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31091 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31093 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31094 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31096 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31101 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31102 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31104 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31105 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31107 var targetWidth = this.minWidth - 2 * x;
31108 var targetHeight = this.minHeight - 2 * y;
31112 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31113 scale = targetWidth / width;
31116 if(x > 0 && y == 0){
31117 scale = targetHeight / height;
31120 if(x > 0 && y > 0){
31121 scale = targetWidth / width;
31123 if(width < height){
31124 scale = targetHeight / height;
31128 context.scale(scale, scale);
31130 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31131 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31133 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31134 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31136 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31138 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31143 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31144 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31146 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31147 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31149 var targetWidth = this.minWidth - 2 * x;
31150 var targetHeight = this.minHeight - 2 * y;
31154 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31155 scale = targetWidth / width;
31158 if(x > 0 && y == 0){
31159 scale = targetHeight / height;
31162 if(x > 0 && y > 0){
31163 scale = targetWidth / width;
31165 if(width < height){
31166 scale = targetHeight / height;
31170 context.scale(scale, scale);
31172 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31173 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31175 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31176 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31178 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31179 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31181 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31186 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31187 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31189 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31190 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31192 var targetWidth = this.minWidth - 2 * x;
31193 var targetHeight = this.minHeight - 2 * y;
31197 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31198 scale = targetWidth / width;
31201 if(x > 0 && y == 0){
31202 scale = targetHeight / height;
31205 if(x > 0 && y > 0){
31206 scale = targetWidth / width;
31208 if(width < height){
31209 scale = targetHeight / height;
31213 context.scale(scale, scale);
31215 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31216 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31218 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31219 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31221 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31223 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31230 this.cropData = canvas.toDataURL(this.cropType);
31232 if(this.fireEvent('crop', this, this.cropData) !== false){
31233 this.process(this.file, this.cropData);
31240 setThumbBoxSize : function()
31244 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31245 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31246 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31248 this.minWidth = width;
31249 this.minHeight = height;
31251 if(this.rotate == 90 || this.rotate == 270){
31252 this.minWidth = height;
31253 this.minHeight = width;
31258 width = Math.ceil(this.minWidth * height / this.minHeight);
31260 if(this.minWidth > this.minHeight){
31262 height = Math.ceil(this.minHeight * width / this.minWidth);
31265 this.thumbEl.setStyle({
31266 width : width + 'px',
31267 height : height + 'px'
31274 setThumbBoxPosition : function()
31276 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31277 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31279 this.thumbEl.setLeft(x);
31280 this.thumbEl.setTop(y);
31284 baseRotateLevel : function()
31286 this.baseRotate = 1;
31289 typeof(this.exif) != 'undefined' &&
31290 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31291 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31293 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31296 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31300 baseScaleLevel : function()
31304 if(this.isDocument){
31306 if(this.baseRotate == 6 || this.baseRotate == 8){
31308 height = this.thumbEl.getHeight();
31309 this.baseScale = height / this.imageEl.OriginWidth;
31311 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31312 width = this.thumbEl.getWidth();
31313 this.baseScale = width / this.imageEl.OriginHeight;
31319 height = this.thumbEl.getHeight();
31320 this.baseScale = height / this.imageEl.OriginHeight;
31322 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31323 width = this.thumbEl.getWidth();
31324 this.baseScale = width / this.imageEl.OriginWidth;
31330 if(this.baseRotate == 6 || this.baseRotate == 8){
31332 width = this.thumbEl.getHeight();
31333 this.baseScale = width / this.imageEl.OriginHeight;
31335 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31336 height = this.thumbEl.getWidth();
31337 this.baseScale = height / this.imageEl.OriginHeight;
31340 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31341 height = this.thumbEl.getWidth();
31342 this.baseScale = height / this.imageEl.OriginHeight;
31344 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31345 width = this.thumbEl.getHeight();
31346 this.baseScale = width / this.imageEl.OriginWidth;
31353 width = this.thumbEl.getWidth();
31354 this.baseScale = width / this.imageEl.OriginWidth;
31356 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31357 height = this.thumbEl.getHeight();
31358 this.baseScale = height / this.imageEl.OriginHeight;
31361 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31363 height = this.thumbEl.getHeight();
31364 this.baseScale = height / this.imageEl.OriginHeight;
31366 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31367 width = this.thumbEl.getWidth();
31368 this.baseScale = width / this.imageEl.OriginWidth;
31376 getScaleLevel : function()
31378 return this.baseScale * Math.pow(1.1, this.scale);
31381 onTouchStart : function(e)
31383 if(!this.canvasLoaded){
31384 this.beforeSelectFile(e);
31388 var touches = e.browserEvent.touches;
31394 if(touches.length == 1){
31395 this.onMouseDown(e);
31399 if(touches.length != 2){
31405 for(var i = 0, finger; finger = touches[i]; i++){
31406 coords.push(finger.pageX, finger.pageY);
31409 var x = Math.pow(coords[0] - coords[2], 2);
31410 var y = Math.pow(coords[1] - coords[3], 2);
31412 this.startDistance = Math.sqrt(x + y);
31414 this.startScale = this.scale;
31416 this.pinching = true;
31417 this.dragable = false;
31421 onTouchMove : function(e)
31423 if(!this.pinching && !this.dragable){
31427 var touches = e.browserEvent.touches;
31434 this.onMouseMove(e);
31440 for(var i = 0, finger; finger = touches[i]; i++){
31441 coords.push(finger.pageX, finger.pageY);
31444 var x = Math.pow(coords[0] - coords[2], 2);
31445 var y = Math.pow(coords[1] - coords[3], 2);
31447 this.endDistance = Math.sqrt(x + y);
31449 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31451 if(!this.zoomable()){
31452 this.scale = this.startScale;
31460 onTouchEnd : function(e)
31462 this.pinching = false;
31463 this.dragable = false;
31467 process : function(file, crop)
31470 this.maskEl.mask(this.loadingText);
31473 this.xhr = new XMLHttpRequest();
31475 file.xhr = this.xhr;
31477 this.xhr.open(this.method, this.url, true);
31480 "Accept": "application/json",
31481 "Cache-Control": "no-cache",
31482 "X-Requested-With": "XMLHttpRequest"
31485 for (var headerName in headers) {
31486 var headerValue = headers[headerName];
31488 this.xhr.setRequestHeader(headerName, headerValue);
31494 this.xhr.onload = function()
31496 _this.xhrOnLoad(_this.xhr);
31499 this.xhr.onerror = function()
31501 _this.xhrOnError(_this.xhr);
31504 var formData = new FormData();
31506 formData.append('returnHTML', 'NO');
31509 formData.append('crop', crop);
31512 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31513 formData.append(this.paramName, file, file.name);
31516 if(typeof(file.filename) != 'undefined'){
31517 formData.append('filename', file.filename);
31520 if(typeof(file.mimetype) != 'undefined'){
31521 formData.append('mimetype', file.mimetype);
31524 if(this.fireEvent('arrange', this, formData) != false){
31525 this.xhr.send(formData);
31529 xhrOnLoad : function(xhr)
31532 this.maskEl.unmask();
31535 if (xhr.readyState !== 4) {
31536 this.fireEvent('exception', this, xhr);
31540 var response = Roo.decode(xhr.responseText);
31542 if(!response.success){
31543 this.fireEvent('exception', this, xhr);
31547 var response = Roo.decode(xhr.responseText);
31549 this.fireEvent('upload', this, response);
31553 xhrOnError : function()
31556 this.maskEl.unmask();
31559 Roo.log('xhr on error');
31561 var response = Roo.decode(xhr.responseText);
31567 prepare : function(file)
31570 this.maskEl.mask(this.loadingText);
31576 if(typeof(file) === 'string'){
31577 this.loadCanvas(file);
31581 if(!file || !this.urlAPI){
31586 this.cropType = file.type;
31590 if(this.fireEvent('prepare', this, this.file) != false){
31592 var reader = new FileReader();
31594 reader.onload = function (e) {
31595 if (e.target.error) {
31596 Roo.log(e.target.error);
31600 var buffer = e.target.result,
31601 dataView = new DataView(buffer),
31603 maxOffset = dataView.byteLength - 4,
31607 if (dataView.getUint16(0) === 0xffd8) {
31608 while (offset < maxOffset) {
31609 markerBytes = dataView.getUint16(offset);
31611 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31612 markerLength = dataView.getUint16(offset + 2) + 2;
31613 if (offset + markerLength > dataView.byteLength) {
31614 Roo.log('Invalid meta data: Invalid segment size.');
31618 if(markerBytes == 0xffe1){
31619 _this.parseExifData(
31626 offset += markerLength;
31636 var url = _this.urlAPI.createObjectURL(_this.file);
31638 _this.loadCanvas(url);
31643 reader.readAsArrayBuffer(this.file);
31649 parseExifData : function(dataView, offset, length)
31651 var tiffOffset = offset + 10,
31655 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31656 // No Exif data, might be XMP data instead
31660 // Check for the ASCII code for "Exif" (0x45786966):
31661 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31662 // No Exif data, might be XMP data instead
31665 if (tiffOffset + 8 > dataView.byteLength) {
31666 Roo.log('Invalid Exif data: Invalid segment size.');
31669 // Check for the two null bytes:
31670 if (dataView.getUint16(offset + 8) !== 0x0000) {
31671 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31674 // Check the byte alignment:
31675 switch (dataView.getUint16(tiffOffset)) {
31677 littleEndian = true;
31680 littleEndian = false;
31683 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31686 // Check for the TIFF tag marker (0x002A):
31687 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31688 Roo.log('Invalid Exif data: Missing TIFF marker.');
31691 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31692 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31694 this.parseExifTags(
31697 tiffOffset + dirOffset,
31702 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31707 if (dirOffset + 6 > dataView.byteLength) {
31708 Roo.log('Invalid Exif data: Invalid directory offset.');
31711 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31712 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31713 if (dirEndOffset + 4 > dataView.byteLength) {
31714 Roo.log('Invalid Exif data: Invalid directory size.');
31717 for (i = 0; i < tagsNumber; i += 1) {
31721 dirOffset + 2 + 12 * i, // tag offset
31725 // Return the offset to the next directory:
31726 return dataView.getUint32(dirEndOffset, littleEndian);
31729 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31731 var tag = dataView.getUint16(offset, littleEndian);
31733 this.exif[tag] = this.getExifValue(
31737 dataView.getUint16(offset + 2, littleEndian), // tag type
31738 dataView.getUint32(offset + 4, littleEndian), // tag length
31743 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31745 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31754 Roo.log('Invalid Exif data: Invalid tag type.');
31758 tagSize = tagType.size * length;
31759 // Determine if the value is contained in the dataOffset bytes,
31760 // or if the value at the dataOffset is a pointer to the actual data:
31761 dataOffset = tagSize > 4 ?
31762 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31763 if (dataOffset + tagSize > dataView.byteLength) {
31764 Roo.log('Invalid Exif data: Invalid data offset.');
31767 if (length === 1) {
31768 return tagType.getValue(dataView, dataOffset, littleEndian);
31771 for (i = 0; i < length; i += 1) {
31772 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31775 if (tagType.ascii) {
31777 // Concatenate the chars:
31778 for (i = 0; i < values.length; i += 1) {
31780 // Ignore the terminating NULL byte(s):
31781 if (c === '\u0000') {
31793 Roo.apply(Roo.bootstrap.UploadCropbox, {
31795 'Orientation': 0x0112
31799 1: 0, //'top-left',
31801 3: 180, //'bottom-right',
31802 // 4: 'bottom-left',
31804 6: 90, //'right-top',
31805 // 7: 'right-bottom',
31806 8: 270 //'left-bottom'
31810 // byte, 8-bit unsigned int:
31812 getValue: function (dataView, dataOffset) {
31813 return dataView.getUint8(dataOffset);
31817 // ascii, 8-bit byte:
31819 getValue: function (dataView, dataOffset) {
31820 return String.fromCharCode(dataView.getUint8(dataOffset));
31825 // short, 16 bit int:
31827 getValue: function (dataView, dataOffset, littleEndian) {
31828 return dataView.getUint16(dataOffset, littleEndian);
31832 // long, 32 bit int:
31834 getValue: function (dataView, dataOffset, littleEndian) {
31835 return dataView.getUint32(dataOffset, littleEndian);
31839 // rational = two long values, first is numerator, second is denominator:
31841 getValue: function (dataView, dataOffset, littleEndian) {
31842 return dataView.getUint32(dataOffset, littleEndian) /
31843 dataView.getUint32(dataOffset + 4, littleEndian);
31847 // slong, 32 bit signed int:
31849 getValue: function (dataView, dataOffset, littleEndian) {
31850 return dataView.getInt32(dataOffset, littleEndian);
31854 // srational, two slongs, first is numerator, second is denominator:
31856 getValue: function (dataView, dataOffset, littleEndian) {
31857 return dataView.getInt32(dataOffset, littleEndian) /
31858 dataView.getInt32(dataOffset + 4, littleEndian);
31868 cls : 'btn-group roo-upload-cropbox-rotate-left',
31869 action : 'rotate-left',
31873 cls : 'btn btn-default',
31874 html : '<i class="fa fa-undo"></i>'
31880 cls : 'btn-group roo-upload-cropbox-picture',
31881 action : 'picture',
31885 cls : 'btn btn-default',
31886 html : '<i class="fa fa-picture-o"></i>'
31892 cls : 'btn-group roo-upload-cropbox-rotate-right',
31893 action : 'rotate-right',
31897 cls : 'btn btn-default',
31898 html : '<i class="fa fa-repeat"></i>'
31906 cls : 'btn-group roo-upload-cropbox-rotate-left',
31907 action : 'rotate-left',
31911 cls : 'btn btn-default',
31912 html : '<i class="fa fa-undo"></i>'
31918 cls : 'btn-group roo-upload-cropbox-download',
31919 action : 'download',
31923 cls : 'btn btn-default',
31924 html : '<i class="fa fa-download"></i>'
31930 cls : 'btn-group roo-upload-cropbox-crop',
31935 cls : 'btn btn-default',
31936 html : '<i class="fa fa-crop"></i>'
31942 cls : 'btn-group roo-upload-cropbox-trash',
31947 cls : 'btn btn-default',
31948 html : '<i class="fa fa-trash"></i>'
31954 cls : 'btn-group roo-upload-cropbox-rotate-right',
31955 action : 'rotate-right',
31959 cls : 'btn btn-default',
31960 html : '<i class="fa fa-repeat"></i>'
31968 cls : 'btn-group roo-upload-cropbox-rotate-left',
31969 action : 'rotate-left',
31973 cls : 'btn btn-default',
31974 html : '<i class="fa fa-undo"></i>'
31980 cls : 'btn-group roo-upload-cropbox-rotate-right',
31981 action : 'rotate-right',
31985 cls : 'btn btn-default',
31986 html : '<i class="fa fa-repeat"></i>'
31999 * @class Roo.bootstrap.DocumentManager
32000 * @extends Roo.bootstrap.Component
32001 * Bootstrap DocumentManager class
32002 * @cfg {String} paramName default 'imageUpload'
32003 * @cfg {String} toolTipName default 'filename'
32004 * @cfg {String} method default POST
32005 * @cfg {String} url action url
32006 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32007 * @cfg {Boolean} multiple multiple upload default true
32008 * @cfg {Number} thumbSize default 300
32009 * @cfg {String} fieldLabel
32010 * @cfg {Number} labelWidth default 4
32011 * @cfg {String} labelAlign (left|top) default left
32012 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32013 * @cfg {Number} labellg set the width of label (1-12)
32014 * @cfg {Number} labelmd set the width of label (1-12)
32015 * @cfg {Number} labelsm set the width of label (1-12)
32016 * @cfg {Number} labelxs set the width of label (1-12)
32019 * Create a new DocumentManager
32020 * @param {Object} config The config object
32023 Roo.bootstrap.DocumentManager = function(config){
32024 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32027 this.delegates = [];
32032 * Fire when initial the DocumentManager
32033 * @param {Roo.bootstrap.DocumentManager} this
32038 * inspect selected file
32039 * @param {Roo.bootstrap.DocumentManager} this
32040 * @param {File} file
32045 * Fire when xhr load exception
32046 * @param {Roo.bootstrap.DocumentManager} this
32047 * @param {XMLHttpRequest} xhr
32049 "exception" : true,
32051 * @event afterupload
32052 * Fire when xhr load exception
32053 * @param {Roo.bootstrap.DocumentManager} this
32054 * @param {XMLHttpRequest} xhr
32056 "afterupload" : true,
32059 * prepare the form data
32060 * @param {Roo.bootstrap.DocumentManager} this
32061 * @param {Object} formData
32066 * Fire when remove the file
32067 * @param {Roo.bootstrap.DocumentManager} this
32068 * @param {Object} file
32073 * Fire after refresh the file
32074 * @param {Roo.bootstrap.DocumentManager} this
32079 * Fire after click the image
32080 * @param {Roo.bootstrap.DocumentManager} this
32081 * @param {Object} file
32086 * Fire when upload a image and editable set to true
32087 * @param {Roo.bootstrap.DocumentManager} this
32088 * @param {Object} file
32092 * @event beforeselectfile
32093 * Fire before select file
32094 * @param {Roo.bootstrap.DocumentManager} this
32096 "beforeselectfile" : true,
32099 * Fire before process file
32100 * @param {Roo.bootstrap.DocumentManager} this
32101 * @param {Object} file
32105 * @event previewrendered
32106 * Fire when preview rendered
32107 * @param {Roo.bootstrap.DocumentManager} this
32108 * @param {Object} file
32110 "previewrendered" : true,
32113 "previewResize" : true
32118 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32127 paramName : 'imageUpload',
32128 toolTipName : 'filename',
32131 labelAlign : 'left',
32141 getAutoCreate : function()
32143 var managerWidget = {
32145 cls : 'roo-document-manager',
32149 cls : 'roo-document-manager-selector',
32154 cls : 'roo-document-manager-uploader',
32158 cls : 'roo-document-manager-upload-btn',
32159 html : '<i class="fa fa-plus"></i>'
32170 cls : 'column col-md-12',
32175 if(this.fieldLabel.length){
32180 cls : 'column col-md-12',
32181 html : this.fieldLabel
32185 cls : 'column col-md-12',
32190 if(this.labelAlign == 'left'){
32195 html : this.fieldLabel
32204 if(this.labelWidth > 12){
32205 content[0].style = "width: " + this.labelWidth + 'px';
32208 if(this.labelWidth < 13 && this.labelmd == 0){
32209 this.labelmd = this.labelWidth;
32212 if(this.labellg > 0){
32213 content[0].cls += ' col-lg-' + this.labellg;
32214 content[1].cls += ' col-lg-' + (12 - this.labellg);
32217 if(this.labelmd > 0){
32218 content[0].cls += ' col-md-' + this.labelmd;
32219 content[1].cls += ' col-md-' + (12 - this.labelmd);
32222 if(this.labelsm > 0){
32223 content[0].cls += ' col-sm-' + this.labelsm;
32224 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32227 if(this.labelxs > 0){
32228 content[0].cls += ' col-xs-' + this.labelxs;
32229 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32237 cls : 'row clearfix',
32245 initEvents : function()
32247 this.managerEl = this.el.select('.roo-document-manager', true).first();
32248 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32250 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32251 this.selectorEl.hide();
32254 this.selectorEl.attr('multiple', 'multiple');
32257 this.selectorEl.on('change', this.onFileSelected, this);
32259 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32260 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32262 this.uploader.on('click', this.onUploaderClick, this);
32264 this.renderProgressDialog();
32268 window.addEventListener("resize", function() { _this.refresh(); } );
32270 this.fireEvent('initial', this);
32273 renderProgressDialog : function()
32277 this.progressDialog = new Roo.bootstrap.Modal({
32278 cls : 'roo-document-manager-progress-dialog',
32279 allow_close : false,
32290 btnclick : function() {
32291 _this.uploadCancel();
32297 this.progressDialog.render(Roo.get(document.body));
32299 this.progress = new Roo.bootstrap.Progress({
32300 cls : 'roo-document-manager-progress',
32305 this.progress.render(this.progressDialog.getChildContainer());
32307 this.progressBar = new Roo.bootstrap.ProgressBar({
32308 cls : 'roo-document-manager-progress-bar',
32311 aria_valuemax : 12,
32315 this.progressBar.render(this.progress.getChildContainer());
32318 onUploaderClick : function(e)
32320 e.preventDefault();
32322 if(this.fireEvent('beforeselectfile', this) != false){
32323 this.selectorEl.dom.click();
32328 onFileSelected : function(e)
32330 e.preventDefault();
32332 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32336 Roo.each(this.selectorEl.dom.files, function(file){
32337 if(this.fireEvent('inspect', this, file) != false){
32338 this.files.push(file);
32348 this.selectorEl.dom.value = '';
32350 if(!this.files || !this.files.length){
32354 if(this.boxes > 0 && this.files.length > this.boxes){
32355 this.files = this.files.slice(0, this.boxes);
32358 this.uploader.show();
32360 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32361 this.uploader.hide();
32370 Roo.each(this.files, function(file){
32372 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32373 var f = this.renderPreview(file);
32378 if(file.type.indexOf('image') != -1){
32379 this.delegates.push(
32381 _this.process(file);
32382 }).createDelegate(this)
32390 _this.process(file);
32391 }).createDelegate(this)
32396 this.files = files;
32398 this.delegates = this.delegates.concat(docs);
32400 if(!this.delegates.length){
32405 this.progressBar.aria_valuemax = this.delegates.length;
32412 arrange : function()
32414 if(!this.delegates.length){
32415 this.progressDialog.hide();
32420 var delegate = this.delegates.shift();
32422 this.progressDialog.show();
32424 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32426 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32431 refresh : function()
32433 this.uploader.show();
32435 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32436 this.uploader.hide();
32439 Roo.isTouch ? this.closable(false) : this.closable(true);
32441 this.fireEvent('refresh', this);
32444 onRemove : function(e, el, o)
32446 e.preventDefault();
32448 this.fireEvent('remove', this, o);
32452 remove : function(o)
32456 Roo.each(this.files, function(file){
32457 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32466 this.files = files;
32473 Roo.each(this.files, function(file){
32478 file.target.remove();
32487 onClick : function(e, el, o)
32489 e.preventDefault();
32491 this.fireEvent('click', this, o);
32495 closable : function(closable)
32497 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32499 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32511 xhrOnLoad : function(xhr)
32513 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32517 if (xhr.readyState !== 4) {
32519 this.fireEvent('exception', this, xhr);
32523 var response = Roo.decode(xhr.responseText);
32525 if(!response.success){
32527 this.fireEvent('exception', this, xhr);
32531 var file = this.renderPreview(response.data);
32533 this.files.push(file);
32537 this.fireEvent('afterupload', this, xhr);
32541 xhrOnError : function(xhr)
32543 Roo.log('xhr on error');
32545 var response = Roo.decode(xhr.responseText);
32552 process : function(file)
32554 if(this.fireEvent('process', this, file) !== false){
32555 if(this.editable && file.type.indexOf('image') != -1){
32556 this.fireEvent('edit', this, file);
32560 this.uploadStart(file, false);
32567 uploadStart : function(file, crop)
32569 this.xhr = new XMLHttpRequest();
32571 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32576 file.xhr = this.xhr;
32578 this.managerEl.createChild({
32580 cls : 'roo-document-manager-loading',
32584 tooltip : file.name,
32585 cls : 'roo-document-manager-thumb',
32586 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32592 this.xhr.open(this.method, this.url, true);
32595 "Accept": "application/json",
32596 "Cache-Control": "no-cache",
32597 "X-Requested-With": "XMLHttpRequest"
32600 for (var headerName in headers) {
32601 var headerValue = headers[headerName];
32603 this.xhr.setRequestHeader(headerName, headerValue);
32609 this.xhr.onload = function()
32611 _this.xhrOnLoad(_this.xhr);
32614 this.xhr.onerror = function()
32616 _this.xhrOnError(_this.xhr);
32619 var formData = new FormData();
32621 formData.append('returnHTML', 'NO');
32624 formData.append('crop', crop);
32627 formData.append(this.paramName, file, file.name);
32634 if(this.fireEvent('prepare', this, formData, options) != false){
32636 if(options.manually){
32640 this.xhr.send(formData);
32644 this.uploadCancel();
32647 uploadCancel : function()
32653 this.delegates = [];
32655 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32662 renderPreview : function(file)
32664 if(typeof(file.target) != 'undefined' && file.target){
32668 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32670 var previewEl = this.managerEl.createChild({
32672 cls : 'roo-document-manager-preview',
32676 tooltip : file[this.toolTipName],
32677 cls : 'roo-document-manager-thumb',
32678 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32683 html : '<i class="fa fa-times-circle"></i>'
32688 var close = previewEl.select('button.close', true).first();
32690 close.on('click', this.onRemove, this, file);
32692 file.target = previewEl;
32694 var image = previewEl.select('img', true).first();
32698 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32700 image.on('click', this.onClick, this, file);
32702 this.fireEvent('previewrendered', this, file);
32708 onPreviewLoad : function(file, image)
32710 if(typeof(file.target) == 'undefined' || !file.target){
32714 var width = image.dom.naturalWidth || image.dom.width;
32715 var height = image.dom.naturalHeight || image.dom.height;
32717 if(!this.previewResize) {
32721 if(width > height){
32722 file.target.addClass('wide');
32726 file.target.addClass('tall');
32731 uploadFromSource : function(file, crop)
32733 this.xhr = new XMLHttpRequest();
32735 this.managerEl.createChild({
32737 cls : 'roo-document-manager-loading',
32741 tooltip : file.name,
32742 cls : 'roo-document-manager-thumb',
32743 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32749 this.xhr.open(this.method, this.url, true);
32752 "Accept": "application/json",
32753 "Cache-Control": "no-cache",
32754 "X-Requested-With": "XMLHttpRequest"
32757 for (var headerName in headers) {
32758 var headerValue = headers[headerName];
32760 this.xhr.setRequestHeader(headerName, headerValue);
32766 this.xhr.onload = function()
32768 _this.xhrOnLoad(_this.xhr);
32771 this.xhr.onerror = function()
32773 _this.xhrOnError(_this.xhr);
32776 var formData = new FormData();
32778 formData.append('returnHTML', 'NO');
32780 formData.append('crop', crop);
32782 if(typeof(file.filename) != 'undefined'){
32783 formData.append('filename', file.filename);
32786 if(typeof(file.mimetype) != 'undefined'){
32787 formData.append('mimetype', file.mimetype);
32792 if(this.fireEvent('prepare', this, formData) != false){
32793 this.xhr.send(formData);
32803 * @class Roo.bootstrap.DocumentViewer
32804 * @extends Roo.bootstrap.Component
32805 * Bootstrap DocumentViewer class
32806 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32807 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32810 * Create a new DocumentViewer
32811 * @param {Object} config The config object
32814 Roo.bootstrap.DocumentViewer = function(config){
32815 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32820 * Fire after initEvent
32821 * @param {Roo.bootstrap.DocumentViewer} this
32827 * @param {Roo.bootstrap.DocumentViewer} this
32832 * Fire after download button
32833 * @param {Roo.bootstrap.DocumentViewer} this
32838 * Fire after trash button
32839 * @param {Roo.bootstrap.DocumentViewer} this
32846 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32848 showDownload : true,
32852 getAutoCreate : function()
32856 cls : 'roo-document-viewer',
32860 cls : 'roo-document-viewer-body',
32864 cls : 'roo-document-viewer-thumb',
32868 cls : 'roo-document-viewer-image'
32876 cls : 'roo-document-viewer-footer',
32879 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32883 cls : 'btn-group roo-document-viewer-download',
32887 cls : 'btn btn-default',
32888 html : '<i class="fa fa-download"></i>'
32894 cls : 'btn-group roo-document-viewer-trash',
32898 cls : 'btn btn-default',
32899 html : '<i class="fa fa-trash"></i>'
32912 initEvents : function()
32914 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32915 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32917 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32918 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32920 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32921 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32923 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32924 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32926 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32927 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32929 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32930 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32932 this.bodyEl.on('click', this.onClick, this);
32933 this.downloadBtn.on('click', this.onDownload, this);
32934 this.trashBtn.on('click', this.onTrash, this);
32936 this.downloadBtn.hide();
32937 this.trashBtn.hide();
32939 if(this.showDownload){
32940 this.downloadBtn.show();
32943 if(this.showTrash){
32944 this.trashBtn.show();
32947 if(!this.showDownload && !this.showTrash) {
32948 this.footerEl.hide();
32953 initial : function()
32955 this.fireEvent('initial', this);
32959 onClick : function(e)
32961 e.preventDefault();
32963 this.fireEvent('click', this);
32966 onDownload : function(e)
32968 e.preventDefault();
32970 this.fireEvent('download', this);
32973 onTrash : function(e)
32975 e.preventDefault();
32977 this.fireEvent('trash', this);
32989 * @class Roo.bootstrap.NavProgressBar
32990 * @extends Roo.bootstrap.Component
32991 * Bootstrap NavProgressBar class
32994 * Create a new nav progress bar
32995 * @param {Object} config The config object
32998 Roo.bootstrap.NavProgressBar = function(config){
32999 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33001 this.bullets = this.bullets || [];
33003 // Roo.bootstrap.NavProgressBar.register(this);
33007 * Fires when the active item changes
33008 * @param {Roo.bootstrap.NavProgressBar} this
33009 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33010 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33017 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33022 getAutoCreate : function()
33024 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33028 cls : 'roo-navigation-bar-group',
33032 cls : 'roo-navigation-top-bar'
33036 cls : 'roo-navigation-bullets-bar',
33040 cls : 'roo-navigation-bar'
33047 cls : 'roo-navigation-bottom-bar'
33057 initEvents: function()
33062 onRender : function(ct, position)
33064 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33066 if(this.bullets.length){
33067 Roo.each(this.bullets, function(b){
33076 addItem : function(cfg)
33078 var item = new Roo.bootstrap.NavProgressItem(cfg);
33080 item.parentId = this.id;
33081 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33084 var top = new Roo.bootstrap.Element({
33086 cls : 'roo-navigation-bar-text'
33089 var bottom = new Roo.bootstrap.Element({
33091 cls : 'roo-navigation-bar-text'
33094 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33095 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33097 var topText = new Roo.bootstrap.Element({
33099 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33102 var bottomText = new Roo.bootstrap.Element({
33104 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33107 topText.onRender(top.el, null);
33108 bottomText.onRender(bottom.el, null);
33111 item.bottomEl = bottom;
33114 this.barItems.push(item);
33119 getActive : function()
33121 var active = false;
33123 Roo.each(this.barItems, function(v){
33125 if (!v.isActive()) {
33137 setActiveItem : function(item)
33141 Roo.each(this.barItems, function(v){
33142 if (v.rid == item.rid) {
33146 if (v.isActive()) {
33147 v.setActive(false);
33152 item.setActive(true);
33154 this.fireEvent('changed', this, item, prev);
33157 getBarItem: function(rid)
33161 Roo.each(this.barItems, function(e) {
33162 if (e.rid != rid) {
33173 indexOfItem : function(item)
33177 Roo.each(this.barItems, function(v, i){
33179 if (v.rid != item.rid) {
33190 setActiveNext : function()
33192 var i = this.indexOfItem(this.getActive());
33194 if (i > this.barItems.length) {
33198 this.setActiveItem(this.barItems[i+1]);
33201 setActivePrev : function()
33203 var i = this.indexOfItem(this.getActive());
33209 this.setActiveItem(this.barItems[i-1]);
33212 format : function()
33214 if(!this.barItems.length){
33218 var width = 100 / this.barItems.length;
33220 Roo.each(this.barItems, function(i){
33221 i.el.setStyle('width', width + '%');
33222 i.topEl.el.setStyle('width', width + '%');
33223 i.bottomEl.el.setStyle('width', width + '%');
33232 * Nav Progress Item
33237 * @class Roo.bootstrap.NavProgressItem
33238 * @extends Roo.bootstrap.Component
33239 * Bootstrap NavProgressItem class
33240 * @cfg {String} rid the reference id
33241 * @cfg {Boolean} active (true|false) Is item active default false
33242 * @cfg {Boolean} disabled (true|false) Is item active default false
33243 * @cfg {String} html
33244 * @cfg {String} position (top|bottom) text position default bottom
33245 * @cfg {String} icon show icon instead of number
33248 * Create a new NavProgressItem
33249 * @param {Object} config The config object
33251 Roo.bootstrap.NavProgressItem = function(config){
33252 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33257 * The raw click event for the entire grid.
33258 * @param {Roo.bootstrap.NavProgressItem} this
33259 * @param {Roo.EventObject} e
33266 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33272 position : 'bottom',
33275 getAutoCreate : function()
33277 var iconCls = 'roo-navigation-bar-item-icon';
33279 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33283 cls: 'roo-navigation-bar-item',
33293 cfg.cls += ' active';
33296 cfg.cls += ' disabled';
33302 disable : function()
33304 this.setDisabled(true);
33307 enable : function()
33309 this.setDisabled(false);
33312 initEvents: function()
33314 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33316 this.iconEl.on('click', this.onClick, this);
33319 onClick : function(e)
33321 e.preventDefault();
33327 if(this.fireEvent('click', this, e) === false){
33331 this.parent().setActiveItem(this);
33334 isActive: function ()
33336 return this.active;
33339 setActive : function(state)
33341 if(this.active == state){
33345 this.active = state;
33348 this.el.addClass('active');
33352 this.el.removeClass('active');
33357 setDisabled : function(state)
33359 if(this.disabled == state){
33363 this.disabled = state;
33366 this.el.addClass('disabled');
33370 this.el.removeClass('disabled');
33373 tooltipEl : function()
33375 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33388 * @class Roo.bootstrap.FieldLabel
33389 * @extends Roo.bootstrap.Component
33390 * Bootstrap FieldLabel class
33391 * @cfg {String} html contents of the element
33392 * @cfg {String} tag tag of the element default label
33393 * @cfg {String} cls class of the element
33394 * @cfg {String} target label target
33395 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33396 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33397 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33398 * @cfg {String} iconTooltip default "This field is required"
33399 * @cfg {String} indicatorpos (left|right) default left
33402 * Create a new FieldLabel
33403 * @param {Object} config The config object
33406 Roo.bootstrap.FieldLabel = function(config){
33407 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33412 * Fires after the field has been marked as invalid.
33413 * @param {Roo.form.FieldLabel} this
33414 * @param {String} msg The validation message
33419 * Fires after the field has been validated with no errors.
33420 * @param {Roo.form.FieldLabel} this
33426 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33433 invalidClass : 'has-warning',
33434 validClass : 'has-success',
33435 iconTooltip : 'This field is required',
33436 indicatorpos : 'left',
33438 getAutoCreate : function(){
33441 if (!this.allowBlank) {
33447 cls : 'roo-bootstrap-field-label ' + this.cls,
33452 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33453 tooltip : this.iconTooltip
33462 if(this.indicatorpos == 'right'){
33465 cls : 'roo-bootstrap-field-label ' + this.cls,
33474 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33475 tooltip : this.iconTooltip
33484 initEvents: function()
33486 Roo.bootstrap.Element.superclass.initEvents.call(this);
33488 this.indicator = this.indicatorEl();
33490 if(this.indicator){
33491 this.indicator.removeClass('visible');
33492 this.indicator.addClass('invisible');
33495 Roo.bootstrap.FieldLabel.register(this);
33498 indicatorEl : function()
33500 var indicator = this.el.select('i.roo-required-indicator',true).first();
33511 * Mark this field as valid
33513 markValid : function()
33515 if(this.indicator){
33516 this.indicator.removeClass('visible');
33517 this.indicator.addClass('invisible');
33519 if (Roo.bootstrap.version == 3) {
33520 this.el.removeClass(this.invalidClass);
33521 this.el.addClass(this.validClass);
33523 this.el.removeClass('is-invalid');
33524 this.el.addClass('is-valid');
33528 this.fireEvent('valid', this);
33532 * Mark this field as invalid
33533 * @param {String} msg The validation message
33535 markInvalid : function(msg)
33537 if(this.indicator){
33538 this.indicator.removeClass('invisible');
33539 this.indicator.addClass('visible');
33541 if (Roo.bootstrap.version == 3) {
33542 this.el.removeClass(this.validClass);
33543 this.el.addClass(this.invalidClass);
33545 this.el.removeClass('is-valid');
33546 this.el.addClass('is-invalid');
33550 this.fireEvent('invalid', this, msg);
33556 Roo.apply(Roo.bootstrap.FieldLabel, {
33561 * register a FieldLabel Group
33562 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33564 register : function(label)
33566 if(this.groups.hasOwnProperty(label.target)){
33570 this.groups[label.target] = label;
33574 * fetch a FieldLabel Group based on the target
33575 * @param {string} target
33576 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33578 get: function(target) {
33579 if (typeof(this.groups[target]) == 'undefined') {
33583 return this.groups[target] ;
33592 * page DateSplitField.
33598 * @class Roo.bootstrap.DateSplitField
33599 * @extends Roo.bootstrap.Component
33600 * Bootstrap DateSplitField class
33601 * @cfg {string} fieldLabel - the label associated
33602 * @cfg {Number} labelWidth set the width of label (0-12)
33603 * @cfg {String} labelAlign (top|left)
33604 * @cfg {Boolean} dayAllowBlank (true|false) default false
33605 * @cfg {Boolean} monthAllowBlank (true|false) default false
33606 * @cfg {Boolean} yearAllowBlank (true|false) default false
33607 * @cfg {string} dayPlaceholder
33608 * @cfg {string} monthPlaceholder
33609 * @cfg {string} yearPlaceholder
33610 * @cfg {string} dayFormat default 'd'
33611 * @cfg {string} monthFormat default 'm'
33612 * @cfg {string} yearFormat default 'Y'
33613 * @cfg {Number} labellg set the width of label (1-12)
33614 * @cfg {Number} labelmd set the width of label (1-12)
33615 * @cfg {Number} labelsm set the width of label (1-12)
33616 * @cfg {Number} labelxs set the width of label (1-12)
33620 * Create a new DateSplitField
33621 * @param {Object} config The config object
33624 Roo.bootstrap.DateSplitField = function(config){
33625 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33631 * getting the data of years
33632 * @param {Roo.bootstrap.DateSplitField} this
33633 * @param {Object} years
33638 * getting the data of days
33639 * @param {Roo.bootstrap.DateSplitField} this
33640 * @param {Object} days
33645 * Fires after the field has been marked as invalid.
33646 * @param {Roo.form.Field} this
33647 * @param {String} msg The validation message
33652 * Fires after the field has been validated with no errors.
33653 * @param {Roo.form.Field} this
33659 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33662 labelAlign : 'top',
33664 dayAllowBlank : false,
33665 monthAllowBlank : false,
33666 yearAllowBlank : false,
33667 dayPlaceholder : '',
33668 monthPlaceholder : '',
33669 yearPlaceholder : '',
33673 isFormField : true,
33679 getAutoCreate : function()
33683 cls : 'row roo-date-split-field-group',
33688 cls : 'form-hidden-field roo-date-split-field-group-value',
33694 var labelCls = 'col-md-12';
33695 var contentCls = 'col-md-4';
33697 if(this.fieldLabel){
33701 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33705 html : this.fieldLabel
33710 if(this.labelAlign == 'left'){
33712 if(this.labelWidth > 12){
33713 label.style = "width: " + this.labelWidth + 'px';
33716 if(this.labelWidth < 13 && this.labelmd == 0){
33717 this.labelmd = this.labelWidth;
33720 if(this.labellg > 0){
33721 labelCls = ' col-lg-' + this.labellg;
33722 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33725 if(this.labelmd > 0){
33726 labelCls = ' col-md-' + this.labelmd;
33727 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33730 if(this.labelsm > 0){
33731 labelCls = ' col-sm-' + this.labelsm;
33732 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33735 if(this.labelxs > 0){
33736 labelCls = ' col-xs-' + this.labelxs;
33737 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33741 label.cls += ' ' + labelCls;
33743 cfg.cn.push(label);
33746 Roo.each(['day', 'month', 'year'], function(t){
33749 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33756 inputEl: function ()
33758 return this.el.select('.roo-date-split-field-group-value', true).first();
33761 onRender : function(ct, position)
33765 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33767 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33769 this.dayField = new Roo.bootstrap.ComboBox({
33770 allowBlank : this.dayAllowBlank,
33771 alwaysQuery : true,
33772 displayField : 'value',
33775 forceSelection : true,
33777 placeholder : this.dayPlaceholder,
33778 selectOnFocus : true,
33779 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33780 triggerAction : 'all',
33782 valueField : 'value',
33783 store : new Roo.data.SimpleStore({
33784 data : (function() {
33786 _this.fireEvent('days', _this, days);
33789 fields : [ 'value' ]
33792 select : function (_self, record, index)
33794 _this.setValue(_this.getValue());
33799 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33801 this.monthField = new Roo.bootstrap.MonthField({
33802 after : '<i class=\"fa fa-calendar\"></i>',
33803 allowBlank : this.monthAllowBlank,
33804 placeholder : this.monthPlaceholder,
33807 render : function (_self)
33809 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33810 e.preventDefault();
33814 select : function (_self, oldvalue, newvalue)
33816 _this.setValue(_this.getValue());
33821 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33823 this.yearField = new Roo.bootstrap.ComboBox({
33824 allowBlank : this.yearAllowBlank,
33825 alwaysQuery : true,
33826 displayField : 'value',
33829 forceSelection : true,
33831 placeholder : this.yearPlaceholder,
33832 selectOnFocus : true,
33833 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33834 triggerAction : 'all',
33836 valueField : 'value',
33837 store : new Roo.data.SimpleStore({
33838 data : (function() {
33840 _this.fireEvent('years', _this, years);
33843 fields : [ 'value' ]
33846 select : function (_self, record, index)
33848 _this.setValue(_this.getValue());
33853 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33856 setValue : function(v, format)
33858 this.inputEl.dom.value = v;
33860 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33862 var d = Date.parseDate(v, f);
33869 this.setDay(d.format(this.dayFormat));
33870 this.setMonth(d.format(this.monthFormat));
33871 this.setYear(d.format(this.yearFormat));
33878 setDay : function(v)
33880 this.dayField.setValue(v);
33881 this.inputEl.dom.value = this.getValue();
33886 setMonth : function(v)
33888 this.monthField.setValue(v, true);
33889 this.inputEl.dom.value = this.getValue();
33894 setYear : function(v)
33896 this.yearField.setValue(v);
33897 this.inputEl.dom.value = this.getValue();
33902 getDay : function()
33904 return this.dayField.getValue();
33907 getMonth : function()
33909 return this.monthField.getValue();
33912 getYear : function()
33914 return this.yearField.getValue();
33917 getValue : function()
33919 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33921 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33931 this.inputEl.dom.value = '';
33936 validate : function()
33938 var d = this.dayField.validate();
33939 var m = this.monthField.validate();
33940 var y = this.yearField.validate();
33945 (!this.dayAllowBlank && !d) ||
33946 (!this.monthAllowBlank && !m) ||
33947 (!this.yearAllowBlank && !y)
33952 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33961 this.markInvalid();
33966 markValid : function()
33969 var label = this.el.select('label', true).first();
33970 var icon = this.el.select('i.fa-star', true).first();
33976 this.fireEvent('valid', this);
33980 * Mark this field as invalid
33981 * @param {String} msg The validation message
33983 markInvalid : function(msg)
33986 var label = this.el.select('label', true).first();
33987 var icon = this.el.select('i.fa-star', true).first();
33989 if(label && !icon){
33990 this.el.select('.roo-date-split-field-label', true).createChild({
33992 cls : 'text-danger fa fa-lg fa-star',
33993 tooltip : 'This field is required',
33994 style : 'margin-right:5px;'
33998 this.fireEvent('invalid', this, msg);
34001 clearInvalid : function()
34003 var label = this.el.select('label', true).first();
34004 var icon = this.el.select('i.fa-star', true).first();
34010 this.fireEvent('valid', this);
34013 getName: function()
34023 * http://masonry.desandro.com
34025 * The idea is to render all the bricks based on vertical width...
34027 * The original code extends 'outlayer' - we might need to use that....
34033 * @class Roo.bootstrap.LayoutMasonry
34034 * @extends Roo.bootstrap.Component
34035 * Bootstrap Layout Masonry class
34038 * Create a new Element
34039 * @param {Object} config The config object
34042 Roo.bootstrap.LayoutMasonry = function(config){
34044 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34048 Roo.bootstrap.LayoutMasonry.register(this);
34054 * Fire after layout the items
34055 * @param {Roo.bootstrap.LayoutMasonry} this
34056 * @param {Roo.EventObject} e
34063 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34066 * @cfg {Boolean} isLayoutInstant = no animation?
34068 isLayoutInstant : false, // needed?
34071 * @cfg {Number} boxWidth width of the columns
34076 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34081 * @cfg {Number} padWidth padding below box..
34086 * @cfg {Number} gutter gutter width..
34091 * @cfg {Number} maxCols maximum number of columns
34097 * @cfg {Boolean} isAutoInitial defalut true
34099 isAutoInitial : true,
34104 * @cfg {Boolean} isHorizontal defalut false
34106 isHorizontal : false,
34108 currentSize : null,
34114 bricks: null, //CompositeElement
34118 _isLayoutInited : false,
34120 // isAlternative : false, // only use for vertical layout...
34123 * @cfg {Number} alternativePadWidth padding below box..
34125 alternativePadWidth : 50,
34127 selectedBrick : [],
34129 getAutoCreate : function(){
34131 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34135 cls: 'blog-masonary-wrapper ' + this.cls,
34137 cls : 'mas-boxes masonary'
34144 getChildContainer: function( )
34146 if (this.boxesEl) {
34147 return this.boxesEl;
34150 this.boxesEl = this.el.select('.mas-boxes').first();
34152 return this.boxesEl;
34156 initEvents : function()
34160 if(this.isAutoInitial){
34161 Roo.log('hook children rendered');
34162 this.on('childrenrendered', function() {
34163 Roo.log('children rendered');
34169 initial : function()
34171 this.selectedBrick = [];
34173 this.currentSize = this.el.getBox(true);
34175 Roo.EventManager.onWindowResize(this.resize, this);
34177 if(!this.isAutoInitial){
34185 //this.layout.defer(500,this);
34189 resize : function()
34191 var cs = this.el.getBox(true);
34194 this.currentSize.width == cs.width &&
34195 this.currentSize.x == cs.x &&
34196 this.currentSize.height == cs.height &&
34197 this.currentSize.y == cs.y
34199 Roo.log("no change in with or X or Y");
34203 this.currentSize = cs;
34209 layout : function()
34211 this._resetLayout();
34213 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34215 this.layoutItems( isInstant );
34217 this._isLayoutInited = true;
34219 this.fireEvent('layout', this);
34223 _resetLayout : function()
34225 if(this.isHorizontal){
34226 this.horizontalMeasureColumns();
34230 this.verticalMeasureColumns();
34234 verticalMeasureColumns : function()
34236 this.getContainerWidth();
34238 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34239 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34243 var boxWidth = this.boxWidth + this.padWidth;
34245 if(this.containerWidth < this.boxWidth){
34246 boxWidth = this.containerWidth
34249 var containerWidth = this.containerWidth;
34251 var cols = Math.floor(containerWidth / boxWidth);
34253 this.cols = Math.max( cols, 1 );
34255 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34257 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34259 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34261 this.colWidth = boxWidth + avail - this.padWidth;
34263 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34264 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34267 horizontalMeasureColumns : function()
34269 this.getContainerWidth();
34271 var boxWidth = this.boxWidth;
34273 if(this.containerWidth < boxWidth){
34274 boxWidth = this.containerWidth;
34277 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34279 this.el.setHeight(boxWidth);
34283 getContainerWidth : function()
34285 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34288 layoutItems : function( isInstant )
34290 Roo.log(this.bricks);
34292 var items = Roo.apply([], this.bricks);
34294 if(this.isHorizontal){
34295 this._horizontalLayoutItems( items , isInstant );
34299 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34300 // this._verticalAlternativeLayoutItems( items , isInstant );
34304 this._verticalLayoutItems( items , isInstant );
34308 _verticalLayoutItems : function ( items , isInstant)
34310 if ( !items || !items.length ) {
34315 ['xs', 'xs', 'xs', 'tall'],
34316 ['xs', 'xs', 'tall'],
34317 ['xs', 'xs', 'sm'],
34318 ['xs', 'xs', 'xs'],
34324 ['sm', 'xs', 'xs'],
34328 ['tall', 'xs', 'xs', 'xs'],
34329 ['tall', 'xs', 'xs'],
34341 Roo.each(items, function(item, k){
34343 switch (item.size) {
34344 // these layouts take up a full box,
34355 boxes.push([item]);
34378 var filterPattern = function(box, length)
34386 var pattern = box.slice(0, length);
34390 Roo.each(pattern, function(i){
34391 format.push(i.size);
34394 Roo.each(standard, function(s){
34396 if(String(s) != String(format)){
34405 if(!match && length == 1){
34410 filterPattern(box, length - 1);
34414 queue.push(pattern);
34416 box = box.slice(length, box.length);
34418 filterPattern(box, 4);
34424 Roo.each(boxes, function(box, k){
34430 if(box.length == 1){
34435 filterPattern(box, 4);
34439 this._processVerticalLayoutQueue( queue, isInstant );
34443 // _verticalAlternativeLayoutItems : function( items , isInstant )
34445 // if ( !items || !items.length ) {
34449 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34453 _horizontalLayoutItems : function ( items , isInstant)
34455 if ( !items || !items.length || items.length < 3) {
34461 var eItems = items.slice(0, 3);
34463 items = items.slice(3, items.length);
34466 ['xs', 'xs', 'xs', 'wide'],
34467 ['xs', 'xs', 'wide'],
34468 ['xs', 'xs', 'sm'],
34469 ['xs', 'xs', 'xs'],
34475 ['sm', 'xs', 'xs'],
34479 ['wide', 'xs', 'xs', 'xs'],
34480 ['wide', 'xs', 'xs'],
34493 Roo.each(items, function(item, k){
34495 switch (item.size) {
34506 boxes.push([item]);
34530 var filterPattern = function(box, length)
34538 var pattern = box.slice(0, length);
34542 Roo.each(pattern, function(i){
34543 format.push(i.size);
34546 Roo.each(standard, function(s){
34548 if(String(s) != String(format)){
34557 if(!match && length == 1){
34562 filterPattern(box, length - 1);
34566 queue.push(pattern);
34568 box = box.slice(length, box.length);
34570 filterPattern(box, 4);
34576 Roo.each(boxes, function(box, k){
34582 if(box.length == 1){
34587 filterPattern(box, 4);
34594 var pos = this.el.getBox(true);
34598 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34600 var hit_end = false;
34602 Roo.each(queue, function(box){
34606 Roo.each(box, function(b){
34608 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34618 Roo.each(box, function(b){
34620 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34623 mx = Math.max(mx, b.x);
34627 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34631 Roo.each(box, function(b){
34633 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34647 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34650 /** Sets position of item in DOM
34651 * @param {Element} item
34652 * @param {Number} x - horizontal position
34653 * @param {Number} y - vertical position
34654 * @param {Boolean} isInstant - disables transitions
34656 _processVerticalLayoutQueue : function( queue, isInstant )
34658 var pos = this.el.getBox(true);
34663 for (var i = 0; i < this.cols; i++){
34667 Roo.each(queue, function(box, k){
34669 var col = k % this.cols;
34671 Roo.each(box, function(b,kk){
34673 b.el.position('absolute');
34675 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34676 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34678 if(b.size == 'md-left' || b.size == 'md-right'){
34679 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34680 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34683 b.el.setWidth(width);
34684 b.el.setHeight(height);
34686 b.el.select('iframe',true).setSize(width,height);
34690 for (var i = 0; i < this.cols; i++){
34692 if(maxY[i] < maxY[col]){
34697 col = Math.min(col, i);
34701 x = pos.x + col * (this.colWidth + this.padWidth);
34705 var positions = [];
34707 switch (box.length){
34709 positions = this.getVerticalOneBoxColPositions(x, y, box);
34712 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34715 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34718 positions = this.getVerticalFourBoxColPositions(x, y, box);
34724 Roo.each(box, function(b,kk){
34726 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34728 var sz = b.el.getSize();
34730 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34738 for (var i = 0; i < this.cols; i++){
34739 mY = Math.max(mY, maxY[i]);
34742 this.el.setHeight(mY - pos.y);
34746 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34748 // var pos = this.el.getBox(true);
34751 // var maxX = pos.right;
34753 // var maxHeight = 0;
34755 // Roo.each(items, function(item, k){
34759 // item.el.position('absolute');
34761 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34763 // item.el.setWidth(width);
34765 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34767 // item.el.setHeight(height);
34770 // item.el.setXY([x, y], isInstant ? false : true);
34772 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34775 // y = y + height + this.alternativePadWidth;
34777 // maxHeight = maxHeight + height + this.alternativePadWidth;
34781 // this.el.setHeight(maxHeight);
34785 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34787 var pos = this.el.getBox(true);
34792 var maxX = pos.right;
34794 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34796 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34798 Roo.each(queue, function(box, k){
34800 Roo.each(box, function(b, kk){
34802 b.el.position('absolute');
34804 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34805 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34807 if(b.size == 'md-left' || b.size == 'md-right'){
34808 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34809 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34812 b.el.setWidth(width);
34813 b.el.setHeight(height);
34821 var positions = [];
34823 switch (box.length){
34825 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34828 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34831 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34834 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34840 Roo.each(box, function(b,kk){
34842 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34844 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34852 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34854 Roo.each(eItems, function(b,k){
34856 b.size = (k == 0) ? 'sm' : 'xs';
34857 b.x = (k == 0) ? 2 : 1;
34858 b.y = (k == 0) ? 2 : 1;
34860 b.el.position('absolute');
34862 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34864 b.el.setWidth(width);
34866 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34868 b.el.setHeight(height);
34872 var positions = [];
34875 x : maxX - this.unitWidth * 2 - this.gutter,
34880 x : maxX - this.unitWidth,
34881 y : minY + (this.unitWidth + this.gutter) * 2
34885 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34889 Roo.each(eItems, function(b,k){
34891 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34897 getVerticalOneBoxColPositions : function(x, y, box)
34901 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34903 if(box[0].size == 'md-left'){
34907 if(box[0].size == 'md-right'){
34912 x : x + (this.unitWidth + this.gutter) * rand,
34919 getVerticalTwoBoxColPositions : function(x, y, box)
34923 if(box[0].size == 'xs'){
34927 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34931 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34945 x : x + (this.unitWidth + this.gutter) * 2,
34946 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34953 getVerticalThreeBoxColPositions : function(x, y, box)
34957 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34965 x : x + (this.unitWidth + this.gutter) * 1,
34970 x : x + (this.unitWidth + this.gutter) * 2,
34978 if(box[0].size == 'xs' && box[1].size == 'xs'){
34987 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34991 x : x + (this.unitWidth + this.gutter) * 1,
35005 x : x + (this.unitWidth + this.gutter) * 2,
35010 x : x + (this.unitWidth + this.gutter) * 2,
35011 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35018 getVerticalFourBoxColPositions : function(x, y, box)
35022 if(box[0].size == 'xs'){
35031 y : y + (this.unitHeight + this.gutter) * 1
35036 y : y + (this.unitHeight + this.gutter) * 2
35040 x : x + (this.unitWidth + this.gutter) * 1,
35054 x : x + (this.unitWidth + this.gutter) * 2,
35059 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35060 y : y + (this.unitHeight + this.gutter) * 1
35064 x : x + (this.unitWidth + this.gutter) * 2,
35065 y : y + (this.unitWidth + this.gutter) * 2
35072 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35076 if(box[0].size == 'md-left'){
35078 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35085 if(box[0].size == 'md-right'){
35087 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35088 y : minY + (this.unitWidth + this.gutter) * 1
35094 var rand = Math.floor(Math.random() * (4 - box[0].y));
35097 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35098 y : minY + (this.unitWidth + this.gutter) * rand
35105 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35109 if(box[0].size == 'xs'){
35112 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35117 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35118 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35132 y : minY + (this.unitWidth + this.gutter) * 2
35139 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35143 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35146 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35151 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35152 y : minY + (this.unitWidth + this.gutter) * 1
35156 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35157 y : minY + (this.unitWidth + this.gutter) * 2
35164 if(box[0].size == 'xs' && box[1].size == 'xs'){
35167 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35172 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35177 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35178 y : minY + (this.unitWidth + this.gutter) * 1
35186 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35191 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35192 y : minY + (this.unitWidth + this.gutter) * 2
35196 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35197 y : minY + (this.unitWidth + this.gutter) * 2
35204 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35208 if(box[0].size == 'xs'){
35211 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35216 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35221 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),
35226 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35227 y : minY + (this.unitWidth + this.gutter) * 1
35235 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35240 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35241 y : minY + (this.unitWidth + this.gutter) * 2
35245 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35246 y : minY + (this.unitWidth + this.gutter) * 2
35250 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),
35251 y : minY + (this.unitWidth + this.gutter) * 2
35259 * remove a Masonry Brick
35260 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35262 removeBrick : function(brick_id)
35268 for (var i = 0; i<this.bricks.length; i++) {
35269 if (this.bricks[i].id == brick_id) {
35270 this.bricks.splice(i,1);
35271 this.el.dom.removeChild(Roo.get(brick_id).dom);
35278 * adds a Masonry Brick
35279 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35281 addBrick : function(cfg)
35283 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35284 //this.register(cn);
35285 cn.parentId = this.id;
35286 cn.render(this.el);
35291 * register a Masonry Brick
35292 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35295 register : function(brick)
35297 this.bricks.push(brick);
35298 brick.masonryId = this.id;
35302 * clear all the Masonry Brick
35304 clearAll : function()
35307 //this.getChildContainer().dom.innerHTML = "";
35308 this.el.dom.innerHTML = '';
35311 getSelected : function()
35313 if (!this.selectedBrick) {
35317 return this.selectedBrick;
35321 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35325 * register a Masonry Layout
35326 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35329 register : function(layout)
35331 this.groups[layout.id] = layout;
35334 * fetch a Masonry Layout based on the masonry layout ID
35335 * @param {string} the masonry layout to add
35336 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35339 get: function(layout_id) {
35340 if (typeof(this.groups[layout_id]) == 'undefined') {
35343 return this.groups[layout_id] ;
35355 * http://masonry.desandro.com
35357 * The idea is to render all the bricks based on vertical width...
35359 * The original code extends 'outlayer' - we might need to use that....
35365 * @class Roo.bootstrap.LayoutMasonryAuto
35366 * @extends Roo.bootstrap.Component
35367 * Bootstrap Layout Masonry class
35370 * Create a new Element
35371 * @param {Object} config The config object
35374 Roo.bootstrap.LayoutMasonryAuto = function(config){
35375 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35378 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35381 * @cfg {Boolean} isFitWidth - resize the width..
35383 isFitWidth : false, // options..
35385 * @cfg {Boolean} isOriginLeft = left align?
35387 isOriginLeft : true,
35389 * @cfg {Boolean} isOriginTop = top align?
35391 isOriginTop : false,
35393 * @cfg {Boolean} isLayoutInstant = no animation?
35395 isLayoutInstant : false, // needed?
35397 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35399 isResizingContainer : true,
35401 * @cfg {Number} columnWidth width of the columns
35407 * @cfg {Number} maxCols maximum number of columns
35412 * @cfg {Number} padHeight padding below box..
35418 * @cfg {Boolean} isAutoInitial defalut true
35421 isAutoInitial : true,
35427 initialColumnWidth : 0,
35428 currentSize : null,
35430 colYs : null, // array.
35437 bricks: null, //CompositeElement
35438 cols : 0, // array?
35439 // element : null, // wrapped now this.el
35440 _isLayoutInited : null,
35443 getAutoCreate : function(){
35447 cls: 'blog-masonary-wrapper ' + this.cls,
35449 cls : 'mas-boxes masonary'
35456 getChildContainer: function( )
35458 if (this.boxesEl) {
35459 return this.boxesEl;
35462 this.boxesEl = this.el.select('.mas-boxes').first();
35464 return this.boxesEl;
35468 initEvents : function()
35472 if(this.isAutoInitial){
35473 Roo.log('hook children rendered');
35474 this.on('childrenrendered', function() {
35475 Roo.log('children rendered');
35482 initial : function()
35484 this.reloadItems();
35486 this.currentSize = this.el.getBox(true);
35488 /// was window resize... - let's see if this works..
35489 Roo.EventManager.onWindowResize(this.resize, this);
35491 if(!this.isAutoInitial){
35496 this.layout.defer(500,this);
35499 reloadItems: function()
35501 this.bricks = this.el.select('.masonry-brick', true);
35503 this.bricks.each(function(b) {
35504 //Roo.log(b.getSize());
35505 if (!b.attr('originalwidth')) {
35506 b.attr('originalwidth', b.getSize().width);
35511 Roo.log(this.bricks.elements.length);
35514 resize : function()
35517 var cs = this.el.getBox(true);
35519 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35520 Roo.log("no change in with or X");
35523 this.currentSize = cs;
35527 layout : function()
35530 this._resetLayout();
35531 //this._manageStamps();
35533 // don't animate first layout
35534 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35535 this.layoutItems( isInstant );
35537 // flag for initalized
35538 this._isLayoutInited = true;
35541 layoutItems : function( isInstant )
35543 //var items = this._getItemsForLayout( this.items );
35544 // original code supports filtering layout items.. we just ignore it..
35546 this._layoutItems( this.bricks , isInstant );
35548 this._postLayout();
35550 _layoutItems : function ( items , isInstant)
35552 //this.fireEvent( 'layout', this, items );
35555 if ( !items || !items.elements.length ) {
35556 // no items, emit event with empty array
35561 items.each(function(item) {
35562 Roo.log("layout item");
35564 // get x/y object from method
35565 var position = this._getItemLayoutPosition( item );
35567 position.item = item;
35568 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35569 queue.push( position );
35572 this._processLayoutQueue( queue );
35574 /** Sets position of item in DOM
35575 * @param {Element} item
35576 * @param {Number} x - horizontal position
35577 * @param {Number} y - vertical position
35578 * @param {Boolean} isInstant - disables transitions
35580 _processLayoutQueue : function( queue )
35582 for ( var i=0, len = queue.length; i < len; i++ ) {
35583 var obj = queue[i];
35584 obj.item.position('absolute');
35585 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35591 * Any logic you want to do after each layout,
35592 * i.e. size the container
35594 _postLayout : function()
35596 this.resizeContainer();
35599 resizeContainer : function()
35601 if ( !this.isResizingContainer ) {
35604 var size = this._getContainerSize();
35606 this.el.setSize(size.width,size.height);
35607 this.boxesEl.setSize(size.width,size.height);
35613 _resetLayout : function()
35615 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35616 this.colWidth = this.el.getWidth();
35617 //this.gutter = this.el.getWidth();
35619 this.measureColumns();
35625 this.colYs.push( 0 );
35631 measureColumns : function()
35633 this.getContainerWidth();
35634 // if columnWidth is 0, default to outerWidth of first item
35635 if ( !this.columnWidth ) {
35636 var firstItem = this.bricks.first();
35637 Roo.log(firstItem);
35638 this.columnWidth = this.containerWidth;
35639 if (firstItem && firstItem.attr('originalwidth') ) {
35640 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35642 // columnWidth fall back to item of first element
35643 Roo.log("set column width?");
35644 this.initialColumnWidth = this.columnWidth ;
35646 // if first elem has no width, default to size of container
35651 if (this.initialColumnWidth) {
35652 this.columnWidth = this.initialColumnWidth;
35657 // column width is fixed at the top - however if container width get's smaller we should
35660 // this bit calcs how man columns..
35662 var columnWidth = this.columnWidth += this.gutter;
35664 // calculate columns
35665 var containerWidth = this.containerWidth + this.gutter;
35667 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35668 // fix rounding errors, typically with gutters
35669 var excess = columnWidth - containerWidth % columnWidth;
35672 // if overshoot is less than a pixel, round up, otherwise floor it
35673 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35674 cols = Math[ mathMethod ]( cols );
35675 this.cols = Math.max( cols, 1 );
35676 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35678 // padding positioning..
35679 var totalColWidth = this.cols * this.columnWidth;
35680 var padavail = this.containerWidth - totalColWidth;
35681 // so for 2 columns - we need 3 'pads'
35683 var padNeeded = (1+this.cols) * this.padWidth;
35685 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35687 this.columnWidth += padExtra
35688 //this.padWidth = Math.floor(padavail / ( this.cols));
35690 // adjust colum width so that padding is fixed??
35692 // we have 3 columns ... total = width * 3
35693 // we have X left over... that should be used by
35695 //if (this.expandC) {
35703 getContainerWidth : function()
35705 /* // container is parent if fit width
35706 var container = this.isFitWidth ? this.element.parentNode : this.element;
35707 // check that this.size and size are there
35708 // IE8 triggers resize on body size change, so they might not be
35710 var size = getSize( container ); //FIXME
35711 this.containerWidth = size && size.innerWidth; //FIXME
35714 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35718 _getItemLayoutPosition : function( item ) // what is item?
35720 // we resize the item to our columnWidth..
35722 item.setWidth(this.columnWidth);
35723 item.autoBoxAdjust = false;
35725 var sz = item.getSize();
35727 // how many columns does this brick span
35728 var remainder = this.containerWidth % this.columnWidth;
35730 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35731 // round if off by 1 pixel, otherwise use ceil
35732 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35733 colSpan = Math.min( colSpan, this.cols );
35735 // normally this should be '1' as we dont' currently allow multi width columns..
35737 var colGroup = this._getColGroup( colSpan );
35738 // get the minimum Y value from the columns
35739 var minimumY = Math.min.apply( Math, colGroup );
35740 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35742 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35744 // position the brick
35746 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35747 y: this.currentSize.y + minimumY + this.padHeight
35751 // apply setHeight to necessary columns
35752 var setHeight = minimumY + sz.height + this.padHeight;
35753 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35755 var setSpan = this.cols + 1 - colGroup.length;
35756 for ( var i = 0; i < setSpan; i++ ) {
35757 this.colYs[ shortColIndex + i ] = setHeight ;
35764 * @param {Number} colSpan - number of columns the element spans
35765 * @returns {Array} colGroup
35767 _getColGroup : function( colSpan )
35769 if ( colSpan < 2 ) {
35770 // if brick spans only one column, use all the column Ys
35775 // how many different places could this brick fit horizontally
35776 var groupCount = this.cols + 1 - colSpan;
35777 // for each group potential horizontal position
35778 for ( var i = 0; i < groupCount; i++ ) {
35779 // make an array of colY values for that one group
35780 var groupColYs = this.colYs.slice( i, i + colSpan );
35781 // and get the max value of the array
35782 colGroup[i] = Math.max.apply( Math, groupColYs );
35787 _manageStamp : function( stamp )
35789 var stampSize = stamp.getSize();
35790 var offset = stamp.getBox();
35791 // get the columns that this stamp affects
35792 var firstX = this.isOriginLeft ? offset.x : offset.right;
35793 var lastX = firstX + stampSize.width;
35794 var firstCol = Math.floor( firstX / this.columnWidth );
35795 firstCol = Math.max( 0, firstCol );
35797 var lastCol = Math.floor( lastX / this.columnWidth );
35798 // lastCol should not go over if multiple of columnWidth #425
35799 lastCol -= lastX % this.columnWidth ? 0 : 1;
35800 lastCol = Math.min( this.cols - 1, lastCol );
35802 // set colYs to bottom of the stamp
35803 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35806 for ( var i = firstCol; i <= lastCol; i++ ) {
35807 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35812 _getContainerSize : function()
35814 this.maxY = Math.max.apply( Math, this.colYs );
35819 if ( this.isFitWidth ) {
35820 size.width = this._getContainerFitWidth();
35826 _getContainerFitWidth : function()
35828 var unusedCols = 0;
35829 // count unused columns
35832 if ( this.colYs[i] !== 0 ) {
35837 // fit container to columns that have been used
35838 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35841 needsResizeLayout : function()
35843 var previousWidth = this.containerWidth;
35844 this.getContainerWidth();
35845 return previousWidth !== this.containerWidth;
35860 * @class Roo.bootstrap.MasonryBrick
35861 * @extends Roo.bootstrap.Component
35862 * Bootstrap MasonryBrick class
35865 * Create a new MasonryBrick
35866 * @param {Object} config The config object
35869 Roo.bootstrap.MasonryBrick = function(config){
35871 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35873 Roo.bootstrap.MasonryBrick.register(this);
35879 * When a MasonryBrick is clcik
35880 * @param {Roo.bootstrap.MasonryBrick} this
35881 * @param {Roo.EventObject} e
35887 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35890 * @cfg {String} title
35894 * @cfg {String} html
35898 * @cfg {String} bgimage
35902 * @cfg {String} videourl
35906 * @cfg {String} cls
35910 * @cfg {String} href
35914 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35919 * @cfg {String} placetitle (center|bottom)
35924 * @cfg {Boolean} isFitContainer defalut true
35926 isFitContainer : true,
35929 * @cfg {Boolean} preventDefault defalut false
35931 preventDefault : false,
35934 * @cfg {Boolean} inverse defalut false
35936 maskInverse : false,
35938 getAutoCreate : function()
35940 if(!this.isFitContainer){
35941 return this.getSplitAutoCreate();
35944 var cls = 'masonry-brick masonry-brick-full';
35946 if(this.href.length){
35947 cls += ' masonry-brick-link';
35950 if(this.bgimage.length){
35951 cls += ' masonry-brick-image';
35954 if(this.maskInverse){
35955 cls += ' mask-inverse';
35958 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35959 cls += ' enable-mask';
35963 cls += ' masonry-' + this.size + '-brick';
35966 if(this.placetitle.length){
35968 switch (this.placetitle) {
35970 cls += ' masonry-center-title';
35973 cls += ' masonry-bottom-title';
35980 if(!this.html.length && !this.bgimage.length){
35981 cls += ' masonry-center-title';
35984 if(!this.html.length && this.bgimage.length){
35985 cls += ' masonry-bottom-title';
35990 cls += ' ' + this.cls;
35994 tag: (this.href.length) ? 'a' : 'div',
35999 cls: 'masonry-brick-mask'
36003 cls: 'masonry-brick-paragraph',
36009 if(this.href.length){
36010 cfg.href = this.href;
36013 var cn = cfg.cn[1].cn;
36015 if(this.title.length){
36018 cls: 'masonry-brick-title',
36023 if(this.html.length){
36026 cls: 'masonry-brick-text',
36031 if (!this.title.length && !this.html.length) {
36032 cfg.cn[1].cls += ' hide';
36035 if(this.bgimage.length){
36038 cls: 'masonry-brick-image-view',
36043 if(this.videourl.length){
36044 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36045 // youtube support only?
36048 cls: 'masonry-brick-image-view',
36051 allowfullscreen : true
36059 getSplitAutoCreate : function()
36061 var cls = 'masonry-brick masonry-brick-split';
36063 if(this.href.length){
36064 cls += ' masonry-brick-link';
36067 if(this.bgimage.length){
36068 cls += ' masonry-brick-image';
36072 cls += ' masonry-' + this.size + '-brick';
36075 switch (this.placetitle) {
36077 cls += ' masonry-center-title';
36080 cls += ' masonry-bottom-title';
36083 if(!this.bgimage.length){
36084 cls += ' masonry-center-title';
36087 if(this.bgimage.length){
36088 cls += ' masonry-bottom-title';
36094 cls += ' ' + this.cls;
36098 tag: (this.href.length) ? 'a' : 'div',
36103 cls: 'masonry-brick-split-head',
36107 cls: 'masonry-brick-paragraph',
36114 cls: 'masonry-brick-split-body',
36120 if(this.href.length){
36121 cfg.href = this.href;
36124 if(this.title.length){
36125 cfg.cn[0].cn[0].cn.push({
36127 cls: 'masonry-brick-title',
36132 if(this.html.length){
36133 cfg.cn[1].cn.push({
36135 cls: 'masonry-brick-text',
36140 if(this.bgimage.length){
36141 cfg.cn[0].cn.push({
36143 cls: 'masonry-brick-image-view',
36148 if(this.videourl.length){
36149 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36150 // youtube support only?
36151 cfg.cn[0].cn.cn.push({
36153 cls: 'masonry-brick-image-view',
36156 allowfullscreen : true
36163 initEvents: function()
36165 switch (this.size) {
36198 this.el.on('touchstart', this.onTouchStart, this);
36199 this.el.on('touchmove', this.onTouchMove, this);
36200 this.el.on('touchend', this.onTouchEnd, this);
36201 this.el.on('contextmenu', this.onContextMenu, this);
36203 this.el.on('mouseenter' ,this.enter, this);
36204 this.el.on('mouseleave', this.leave, this);
36205 this.el.on('click', this.onClick, this);
36208 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36209 this.parent().bricks.push(this);
36214 onClick: function(e, el)
36216 var time = this.endTimer - this.startTimer;
36217 // Roo.log(e.preventDefault());
36220 e.preventDefault();
36225 if(!this.preventDefault){
36229 e.preventDefault();
36231 if (this.activeClass != '') {
36232 this.selectBrick();
36235 this.fireEvent('click', this, e);
36238 enter: function(e, el)
36240 e.preventDefault();
36242 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36246 if(this.bgimage.length && this.html.length){
36247 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36251 leave: function(e, el)
36253 e.preventDefault();
36255 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36259 if(this.bgimage.length && this.html.length){
36260 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36264 onTouchStart: function(e, el)
36266 // e.preventDefault();
36268 this.touchmoved = false;
36270 if(!this.isFitContainer){
36274 if(!this.bgimage.length || !this.html.length){
36278 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36280 this.timer = new Date().getTime();
36284 onTouchMove: function(e, el)
36286 this.touchmoved = true;
36289 onContextMenu : function(e,el)
36291 e.preventDefault();
36292 e.stopPropagation();
36296 onTouchEnd: function(e, el)
36298 // e.preventDefault();
36300 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36307 if(!this.bgimage.length || !this.html.length){
36309 if(this.href.length){
36310 window.location.href = this.href;
36316 if(!this.isFitContainer){
36320 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36322 window.location.href = this.href;
36325 //selection on single brick only
36326 selectBrick : function() {
36328 if (!this.parentId) {
36332 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36333 var index = m.selectedBrick.indexOf(this.id);
36336 m.selectedBrick.splice(index,1);
36337 this.el.removeClass(this.activeClass);
36341 for(var i = 0; i < m.selectedBrick.length; i++) {
36342 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36343 b.el.removeClass(b.activeClass);
36346 m.selectedBrick = [];
36348 m.selectedBrick.push(this.id);
36349 this.el.addClass(this.activeClass);
36353 isSelected : function(){
36354 return this.el.hasClass(this.activeClass);
36359 Roo.apply(Roo.bootstrap.MasonryBrick, {
36362 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36364 * register a Masonry Brick
36365 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36368 register : function(brick)
36370 //this.groups[brick.id] = brick;
36371 this.groups.add(brick.id, brick);
36374 * fetch a masonry brick based on the masonry brick ID
36375 * @param {string} the masonry brick to add
36376 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36379 get: function(brick_id)
36381 // if (typeof(this.groups[brick_id]) == 'undefined') {
36384 // return this.groups[brick_id] ;
36386 if(this.groups.key(brick_id)) {
36387 return this.groups.key(brick_id);
36405 * @class Roo.bootstrap.Brick
36406 * @extends Roo.bootstrap.Component
36407 * Bootstrap Brick class
36410 * Create a new Brick
36411 * @param {Object} config The config object
36414 Roo.bootstrap.Brick = function(config){
36415 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36421 * When a Brick is click
36422 * @param {Roo.bootstrap.Brick} this
36423 * @param {Roo.EventObject} e
36429 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36432 * @cfg {String} title
36436 * @cfg {String} html
36440 * @cfg {String} bgimage
36444 * @cfg {String} cls
36448 * @cfg {String} href
36452 * @cfg {String} video
36456 * @cfg {Boolean} square
36460 getAutoCreate : function()
36462 var cls = 'roo-brick';
36464 if(this.href.length){
36465 cls += ' roo-brick-link';
36468 if(this.bgimage.length){
36469 cls += ' roo-brick-image';
36472 if(!this.html.length && !this.bgimage.length){
36473 cls += ' roo-brick-center-title';
36476 if(!this.html.length && this.bgimage.length){
36477 cls += ' roo-brick-bottom-title';
36481 cls += ' ' + this.cls;
36485 tag: (this.href.length) ? 'a' : 'div',
36490 cls: 'roo-brick-paragraph',
36496 if(this.href.length){
36497 cfg.href = this.href;
36500 var cn = cfg.cn[0].cn;
36502 if(this.title.length){
36505 cls: 'roo-brick-title',
36510 if(this.html.length){
36513 cls: 'roo-brick-text',
36520 if(this.bgimage.length){
36523 cls: 'roo-brick-image-view',
36531 initEvents: function()
36533 if(this.title.length || this.html.length){
36534 this.el.on('mouseenter' ,this.enter, this);
36535 this.el.on('mouseleave', this.leave, this);
36538 Roo.EventManager.onWindowResize(this.resize, this);
36540 if(this.bgimage.length){
36541 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36542 this.imageEl.on('load', this.onImageLoad, this);
36549 onImageLoad : function()
36554 resize : function()
36556 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36558 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36560 if(this.bgimage.length){
36561 var image = this.el.select('.roo-brick-image-view', true).first();
36563 image.setWidth(paragraph.getWidth());
36566 image.setHeight(paragraph.getWidth());
36569 this.el.setHeight(image.getHeight());
36570 paragraph.setHeight(image.getHeight());
36576 enter: function(e, el)
36578 e.preventDefault();
36580 if(this.bgimage.length){
36581 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36582 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36586 leave: function(e, el)
36588 e.preventDefault();
36590 if(this.bgimage.length){
36591 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36592 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36607 * @class Roo.bootstrap.NumberField
36608 * @extends Roo.bootstrap.Input
36609 * Bootstrap NumberField class
36615 * Create a new NumberField
36616 * @param {Object} config The config object
36619 Roo.bootstrap.NumberField = function(config){
36620 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36623 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36626 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36628 allowDecimals : true,
36630 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36632 decimalSeparator : ".",
36634 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36636 decimalPrecision : 2,
36638 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36640 allowNegative : true,
36643 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36647 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36649 minValue : Number.NEGATIVE_INFINITY,
36651 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36653 maxValue : Number.MAX_VALUE,
36655 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36657 minText : "The minimum value for this field is {0}",
36659 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36661 maxText : "The maximum value for this field is {0}",
36663 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36664 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36666 nanText : "{0} is not a valid number",
36668 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36670 thousandsDelimiter : false,
36672 * @cfg {String} valueAlign alignment of value
36674 valueAlign : "left",
36676 getAutoCreate : function()
36678 var hiddenInput = {
36682 cls: 'hidden-number-input'
36686 hiddenInput.name = this.name;
36691 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36693 this.name = hiddenInput.name;
36695 if(cfg.cn.length > 0) {
36696 cfg.cn.push(hiddenInput);
36703 initEvents : function()
36705 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36707 var allowed = "0123456789";
36709 if(this.allowDecimals){
36710 allowed += this.decimalSeparator;
36713 if(this.allowNegative){
36717 if(this.thousandsDelimiter) {
36721 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36723 var keyPress = function(e){
36725 var k = e.getKey();
36727 var c = e.getCharCode();
36730 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36731 allowed.indexOf(String.fromCharCode(c)) === -1
36737 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36741 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36746 this.el.on("keypress", keyPress, this);
36749 validateValue : function(value)
36752 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36756 var num = this.parseValue(value);
36759 this.markInvalid(String.format(this.nanText, value));
36763 if(num < this.minValue){
36764 this.markInvalid(String.format(this.minText, this.minValue));
36768 if(num > this.maxValue){
36769 this.markInvalid(String.format(this.maxText, this.maxValue));
36776 getValue : function()
36778 var v = this.hiddenEl().getValue();
36780 return this.fixPrecision(this.parseValue(v));
36783 parseValue : function(value)
36785 if(this.thousandsDelimiter) {
36787 r = new RegExp(",", "g");
36788 value = value.replace(r, "");
36791 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36792 return isNaN(value) ? '' : value;
36795 fixPrecision : function(value)
36797 if(this.thousandsDelimiter) {
36799 r = new RegExp(",", "g");
36800 value = value.replace(r, "");
36803 var nan = isNaN(value);
36805 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36806 return nan ? '' : value;
36808 return parseFloat(value).toFixed(this.decimalPrecision);
36811 setValue : function(v)
36813 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36819 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36821 this.inputEl().dom.value = (v == '') ? '' :
36822 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36824 if(!this.allowZero && v === '0') {
36825 this.hiddenEl().dom.value = '';
36826 this.inputEl().dom.value = '';
36833 decimalPrecisionFcn : function(v)
36835 return Math.floor(v);
36838 beforeBlur : function()
36840 var v = this.parseValue(this.getRawValue());
36842 if(v || v === 0 || v === ''){
36847 hiddenEl : function()
36849 return this.el.select('input.hidden-number-input',true).first();
36861 * @class Roo.bootstrap.DocumentSlider
36862 * @extends Roo.bootstrap.Component
36863 * Bootstrap DocumentSlider class
36866 * Create a new DocumentViewer
36867 * @param {Object} config The config object
36870 Roo.bootstrap.DocumentSlider = function(config){
36871 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36878 * Fire after initEvent
36879 * @param {Roo.bootstrap.DocumentSlider} this
36884 * Fire after update
36885 * @param {Roo.bootstrap.DocumentSlider} this
36891 * @param {Roo.bootstrap.DocumentSlider} this
36897 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36903 getAutoCreate : function()
36907 cls : 'roo-document-slider',
36911 cls : 'roo-document-slider-header',
36915 cls : 'roo-document-slider-header-title'
36921 cls : 'roo-document-slider-body',
36925 cls : 'roo-document-slider-prev',
36929 cls : 'fa fa-chevron-left'
36935 cls : 'roo-document-slider-thumb',
36939 cls : 'roo-document-slider-image'
36945 cls : 'roo-document-slider-next',
36949 cls : 'fa fa-chevron-right'
36961 initEvents : function()
36963 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36964 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36966 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36967 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36969 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36970 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36972 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36973 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36975 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36976 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36978 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36979 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36981 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36982 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36984 this.thumbEl.on('click', this.onClick, this);
36986 this.prevIndicator.on('click', this.prev, this);
36988 this.nextIndicator.on('click', this.next, this);
36992 initial : function()
36994 if(this.files.length){
36995 this.indicator = 1;
36999 this.fireEvent('initial', this);
37002 update : function()
37004 this.imageEl.attr('src', this.files[this.indicator - 1]);
37006 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37008 this.prevIndicator.show();
37010 if(this.indicator == 1){
37011 this.prevIndicator.hide();
37014 this.nextIndicator.show();
37016 if(this.indicator == this.files.length){
37017 this.nextIndicator.hide();
37020 this.thumbEl.scrollTo('top');
37022 this.fireEvent('update', this);
37025 onClick : function(e)
37027 e.preventDefault();
37029 this.fireEvent('click', this);
37034 e.preventDefault();
37036 this.indicator = Math.max(1, this.indicator - 1);
37043 e.preventDefault();
37045 this.indicator = Math.min(this.files.length, this.indicator + 1);
37059 * @class Roo.bootstrap.RadioSet
37060 * @extends Roo.bootstrap.Input
37061 * Bootstrap RadioSet class
37062 * @cfg {String} indicatorpos (left|right) default left
37063 * @cfg {Boolean} inline (true|false) inline the element (default true)
37064 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37066 * Create a new RadioSet
37067 * @param {Object} config The config object
37070 Roo.bootstrap.RadioSet = function(config){
37072 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37076 Roo.bootstrap.RadioSet.register(this);
37081 * Fires when the element is checked or unchecked.
37082 * @param {Roo.bootstrap.RadioSet} this This radio
37083 * @param {Roo.bootstrap.Radio} item The checked item
37088 * Fires when the element is click.
37089 * @param {Roo.bootstrap.RadioSet} this This radio set
37090 * @param {Roo.bootstrap.Radio} item The checked item
37091 * @param {Roo.EventObject} e The event object
37098 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37106 indicatorpos : 'left',
37108 getAutoCreate : function()
37112 cls : 'roo-radio-set-label',
37116 html : this.fieldLabel
37120 if (Roo.bootstrap.version == 3) {
37123 if(this.indicatorpos == 'left'){
37126 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37127 tooltip : 'This field is required'
37132 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37133 tooltip : 'This field is required'
37139 cls : 'roo-radio-set-items'
37142 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37144 if (align === 'left' && this.fieldLabel.length) {
37147 cls : "roo-radio-set-right",
37153 if(this.labelWidth > 12){
37154 label.style = "width: " + this.labelWidth + 'px';
37157 if(this.labelWidth < 13 && this.labelmd == 0){
37158 this.labelmd = this.labelWidth;
37161 if(this.labellg > 0){
37162 label.cls += ' col-lg-' + this.labellg;
37163 items.cls += ' col-lg-' + (12 - this.labellg);
37166 if(this.labelmd > 0){
37167 label.cls += ' col-md-' + this.labelmd;
37168 items.cls += ' col-md-' + (12 - this.labelmd);
37171 if(this.labelsm > 0){
37172 label.cls += ' col-sm-' + this.labelsm;
37173 items.cls += ' col-sm-' + (12 - this.labelsm);
37176 if(this.labelxs > 0){
37177 label.cls += ' col-xs-' + this.labelxs;
37178 items.cls += ' col-xs-' + (12 - this.labelxs);
37184 cls : 'roo-radio-set',
37188 cls : 'roo-radio-set-input',
37191 value : this.value ? this.value : ''
37198 if(this.weight.length){
37199 cfg.cls += ' roo-radio-' + this.weight;
37203 cfg.cls += ' roo-radio-set-inline';
37207 ['xs','sm','md','lg'].map(function(size){
37208 if (settings[size]) {
37209 cfg.cls += ' col-' + size + '-' + settings[size];
37217 initEvents : function()
37219 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37220 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37222 if(!this.fieldLabel.length){
37223 this.labelEl.hide();
37226 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37227 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37229 this.indicator = this.indicatorEl();
37231 if(this.indicator){
37232 this.indicator.addClass('invisible');
37235 this.originalValue = this.getValue();
37239 inputEl: function ()
37241 return this.el.select('.roo-radio-set-input', true).first();
37244 getChildContainer : function()
37246 return this.itemsEl;
37249 register : function(item)
37251 this.radioes.push(item);
37255 validate : function()
37257 if(this.getVisibilityEl().hasClass('hidden')){
37263 Roo.each(this.radioes, function(i){
37272 if(this.allowBlank) {
37276 if(this.disabled || valid){
37281 this.markInvalid();
37286 markValid : function()
37288 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37289 this.indicatorEl().removeClass('visible');
37290 this.indicatorEl().addClass('invisible');
37294 if (Roo.bootstrap.version == 3) {
37295 this.el.removeClass([this.invalidClass, this.validClass]);
37296 this.el.addClass(this.validClass);
37298 this.el.removeClass(['is-invalid','is-valid']);
37299 this.el.addClass(['is-valid']);
37301 this.fireEvent('valid', this);
37304 markInvalid : function(msg)
37306 if(this.allowBlank || this.disabled){
37310 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37311 this.indicatorEl().removeClass('invisible');
37312 this.indicatorEl().addClass('visible');
37314 if (Roo.bootstrap.version == 3) {
37315 this.el.removeClass([this.invalidClass, this.validClass]);
37316 this.el.addClass(this.invalidClass);
37318 this.el.removeClass(['is-invalid','is-valid']);
37319 this.el.addClass(['is-invalid']);
37322 this.fireEvent('invalid', this, msg);
37326 setValue : function(v, suppressEvent)
37328 if(this.value === v){
37335 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37338 Roo.each(this.radioes, function(i){
37340 i.el.removeClass('checked');
37343 Roo.each(this.radioes, function(i){
37345 if(i.value === v || i.value.toString() === v.toString()){
37347 i.el.addClass('checked');
37349 if(suppressEvent !== true){
37350 this.fireEvent('check', this, i);
37361 clearInvalid : function(){
37363 if(!this.el || this.preventMark){
37367 this.el.removeClass([this.invalidClass]);
37369 this.fireEvent('valid', this);
37374 Roo.apply(Roo.bootstrap.RadioSet, {
37378 register : function(set)
37380 this.groups[set.name] = set;
37383 get: function(name)
37385 if (typeof(this.groups[name]) == 'undefined') {
37389 return this.groups[name] ;
37395 * Ext JS Library 1.1.1
37396 * Copyright(c) 2006-2007, Ext JS, LLC.
37398 * Originally Released Under LGPL - original licence link has changed is not relivant.
37401 * <script type="text/javascript">
37406 * @class Roo.bootstrap.SplitBar
37407 * @extends Roo.util.Observable
37408 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37412 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37413 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37414 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37415 split.minSize = 100;
37416 split.maxSize = 600;
37417 split.animate = true;
37418 split.on('moved', splitterMoved);
37421 * Create a new SplitBar
37422 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37423 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37424 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37425 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37426 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37427 position of the SplitBar).
37429 Roo.bootstrap.SplitBar = function(cfg){
37434 // dragElement : elm
37435 // resizingElement: el,
37437 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37438 // placement : Roo.bootstrap.SplitBar.LEFT ,
37439 // existingProxy ???
37442 this.el = Roo.get(cfg.dragElement, true);
37443 this.el.dom.unselectable = "on";
37445 this.resizingEl = Roo.get(cfg.resizingElement, true);
37449 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37450 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37453 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37456 * The minimum size of the resizing element. (Defaults to 0)
37462 * The maximum size of the resizing element. (Defaults to 2000)
37465 this.maxSize = 2000;
37468 * Whether to animate the transition to the new size
37471 this.animate = false;
37474 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37477 this.useShim = false;
37482 if(!cfg.existingProxy){
37484 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37486 this.proxy = Roo.get(cfg.existingProxy).dom;
37489 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37492 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37495 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37498 this.dragSpecs = {};
37501 * @private The adapter to use to positon and resize elements
37503 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37504 this.adapter.init(this);
37506 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37508 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37509 this.el.addClass("roo-splitbar-h");
37512 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37513 this.el.addClass("roo-splitbar-v");
37519 * Fires when the splitter is moved (alias for {@link #event-moved})
37520 * @param {Roo.bootstrap.SplitBar} this
37521 * @param {Number} newSize the new width or height
37526 * Fires when the splitter is moved
37527 * @param {Roo.bootstrap.SplitBar} this
37528 * @param {Number} newSize the new width or height
37532 * @event beforeresize
37533 * Fires before the splitter is dragged
37534 * @param {Roo.bootstrap.SplitBar} this
37536 "beforeresize" : true,
37538 "beforeapply" : true
37541 Roo.util.Observable.call(this);
37544 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37545 onStartProxyDrag : function(x, y){
37546 this.fireEvent("beforeresize", this);
37548 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37550 o.enableDisplayMode("block");
37551 // all splitbars share the same overlay
37552 Roo.bootstrap.SplitBar.prototype.overlay = o;
37554 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37555 this.overlay.show();
37556 Roo.get(this.proxy).setDisplayed("block");
37557 var size = this.adapter.getElementSize(this);
37558 this.activeMinSize = this.getMinimumSize();;
37559 this.activeMaxSize = this.getMaximumSize();;
37560 var c1 = size - this.activeMinSize;
37561 var c2 = Math.max(this.activeMaxSize - size, 0);
37562 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37563 this.dd.resetConstraints();
37564 this.dd.setXConstraint(
37565 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37566 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37568 this.dd.setYConstraint(0, 0);
37570 this.dd.resetConstraints();
37571 this.dd.setXConstraint(0, 0);
37572 this.dd.setYConstraint(
37573 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37574 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37577 this.dragSpecs.startSize = size;
37578 this.dragSpecs.startPoint = [x, y];
37579 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37583 * @private Called after the drag operation by the DDProxy
37585 onEndProxyDrag : function(e){
37586 Roo.get(this.proxy).setDisplayed(false);
37587 var endPoint = Roo.lib.Event.getXY(e);
37589 this.overlay.hide();
37592 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37593 newSize = this.dragSpecs.startSize +
37594 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37595 endPoint[0] - this.dragSpecs.startPoint[0] :
37596 this.dragSpecs.startPoint[0] - endPoint[0]
37599 newSize = this.dragSpecs.startSize +
37600 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37601 endPoint[1] - this.dragSpecs.startPoint[1] :
37602 this.dragSpecs.startPoint[1] - endPoint[1]
37605 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37606 if(newSize != this.dragSpecs.startSize){
37607 if(this.fireEvent('beforeapply', this, newSize) !== false){
37608 this.adapter.setElementSize(this, newSize);
37609 this.fireEvent("moved", this, newSize);
37610 this.fireEvent("resize", this, newSize);
37616 * Get the adapter this SplitBar uses
37617 * @return The adapter object
37619 getAdapter : function(){
37620 return this.adapter;
37624 * Set the adapter this SplitBar uses
37625 * @param {Object} adapter A SplitBar adapter object
37627 setAdapter : function(adapter){
37628 this.adapter = adapter;
37629 this.adapter.init(this);
37633 * Gets the minimum size for the resizing element
37634 * @return {Number} The minimum size
37636 getMinimumSize : function(){
37637 return this.minSize;
37641 * Sets the minimum size for the resizing element
37642 * @param {Number} minSize The minimum size
37644 setMinimumSize : function(minSize){
37645 this.minSize = minSize;
37649 * Gets the maximum size for the resizing element
37650 * @return {Number} The maximum size
37652 getMaximumSize : function(){
37653 return this.maxSize;
37657 * Sets the maximum size for the resizing element
37658 * @param {Number} maxSize The maximum size
37660 setMaximumSize : function(maxSize){
37661 this.maxSize = maxSize;
37665 * Sets the initialize size for the resizing element
37666 * @param {Number} size The initial size
37668 setCurrentSize : function(size){
37669 var oldAnimate = this.animate;
37670 this.animate = false;
37671 this.adapter.setElementSize(this, size);
37672 this.animate = oldAnimate;
37676 * Destroy this splitbar.
37677 * @param {Boolean} removeEl True to remove the element
37679 destroy : function(removeEl){
37681 this.shim.remove();
37684 this.proxy.parentNode.removeChild(this.proxy);
37692 * @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.
37694 Roo.bootstrap.SplitBar.createProxy = function(dir){
37695 var proxy = new Roo.Element(document.createElement("div"));
37696 proxy.unselectable();
37697 var cls = 'roo-splitbar-proxy';
37698 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37699 document.body.appendChild(proxy.dom);
37704 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37705 * Default Adapter. It assumes the splitter and resizing element are not positioned
37706 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37708 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37711 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37712 // do nothing for now
37713 init : function(s){
37717 * Called before drag operations to get the current size of the resizing element.
37718 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37720 getElementSize : function(s){
37721 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37722 return s.resizingEl.getWidth();
37724 return s.resizingEl.getHeight();
37729 * Called after drag operations to set the size of the resizing element.
37730 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37731 * @param {Number} newSize The new size to set
37732 * @param {Function} onComplete A function to be invoked when resizing is complete
37734 setElementSize : function(s, newSize, onComplete){
37735 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37737 s.resizingEl.setWidth(newSize);
37739 onComplete(s, newSize);
37742 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37747 s.resizingEl.setHeight(newSize);
37749 onComplete(s, newSize);
37752 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37759 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37760 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37761 * Adapter that moves the splitter element to align with the resized sizing element.
37762 * Used with an absolute positioned SplitBar.
37763 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37764 * document.body, make sure you assign an id to the body element.
37766 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37767 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37768 this.container = Roo.get(container);
37771 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37772 init : function(s){
37773 this.basic.init(s);
37776 getElementSize : function(s){
37777 return this.basic.getElementSize(s);
37780 setElementSize : function(s, newSize, onComplete){
37781 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37784 moveSplitter : function(s){
37785 var yes = Roo.bootstrap.SplitBar;
37786 switch(s.placement){
37788 s.el.setX(s.resizingEl.getRight());
37791 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37794 s.el.setY(s.resizingEl.getBottom());
37797 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37804 * Orientation constant - Create a vertical SplitBar
37808 Roo.bootstrap.SplitBar.VERTICAL = 1;
37811 * Orientation constant - Create a horizontal SplitBar
37815 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37818 * Placement constant - The resizing element is to the left of the splitter element
37822 Roo.bootstrap.SplitBar.LEFT = 1;
37825 * Placement constant - The resizing element is to the right of the splitter element
37829 Roo.bootstrap.SplitBar.RIGHT = 2;
37832 * Placement constant - The resizing element is positioned above the splitter element
37836 Roo.bootstrap.SplitBar.TOP = 3;
37839 * Placement constant - The resizing element is positioned under splitter element
37843 Roo.bootstrap.SplitBar.BOTTOM = 4;
37844 Roo.namespace("Roo.bootstrap.layout");/*
37846 * Ext JS Library 1.1.1
37847 * Copyright(c) 2006-2007, Ext JS, LLC.
37849 * Originally Released Under LGPL - original licence link has changed is not relivant.
37852 * <script type="text/javascript">
37856 * @class Roo.bootstrap.layout.Manager
37857 * @extends Roo.bootstrap.Component
37858 * Base class for layout managers.
37860 Roo.bootstrap.layout.Manager = function(config)
37862 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37868 /** false to disable window resize monitoring @type Boolean */
37869 this.monitorWindowResize = true;
37874 * Fires when a layout is performed.
37875 * @param {Roo.LayoutManager} this
37879 * @event regionresized
37880 * Fires when the user resizes a region.
37881 * @param {Roo.LayoutRegion} region The resized region
37882 * @param {Number} newSize The new size (width for east/west, height for north/south)
37884 "regionresized" : true,
37886 * @event regioncollapsed
37887 * Fires when a region is collapsed.
37888 * @param {Roo.LayoutRegion} region The collapsed region
37890 "regioncollapsed" : true,
37892 * @event regionexpanded
37893 * Fires when a region is expanded.
37894 * @param {Roo.LayoutRegion} region The expanded region
37896 "regionexpanded" : true
37898 this.updating = false;
37901 this.el = Roo.get(config.el);
37907 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37912 monitorWindowResize : true,
37918 onRender : function(ct, position)
37921 this.el = Roo.get(ct);
37924 //this.fireEvent('render',this);
37928 initEvents: function()
37932 // ie scrollbar fix
37933 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37934 document.body.scroll = "no";
37935 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37936 this.el.position('relative');
37938 this.id = this.el.id;
37939 this.el.addClass("roo-layout-container");
37940 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37941 if(this.el.dom != document.body ) {
37942 this.el.on('resize', this.layout,this);
37943 this.el.on('show', this.layout,this);
37949 * Returns true if this layout is currently being updated
37950 * @return {Boolean}
37952 isUpdating : function(){
37953 return this.updating;
37957 * Suspend the LayoutManager from doing auto-layouts while
37958 * making multiple add or remove calls
37960 beginUpdate : function(){
37961 this.updating = true;
37965 * Restore auto-layouts and optionally disable the manager from performing a layout
37966 * @param {Boolean} noLayout true to disable a layout update
37968 endUpdate : function(noLayout){
37969 this.updating = false;
37975 layout: function(){
37979 onRegionResized : function(region, newSize){
37980 this.fireEvent("regionresized", region, newSize);
37984 onRegionCollapsed : function(region){
37985 this.fireEvent("regioncollapsed", region);
37988 onRegionExpanded : function(region){
37989 this.fireEvent("regionexpanded", region);
37993 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37994 * performs box-model adjustments.
37995 * @return {Object} The size as an object {width: (the width), height: (the height)}
37997 getViewSize : function()
38000 if(this.el.dom != document.body){
38001 size = this.el.getSize();
38003 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38005 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38006 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38011 * Returns the Element this layout is bound to.
38012 * @return {Roo.Element}
38014 getEl : function(){
38019 * Returns the specified region.
38020 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38021 * @return {Roo.LayoutRegion}
38023 getRegion : function(target){
38024 return this.regions[target.toLowerCase()];
38027 onWindowResize : function(){
38028 if(this.monitorWindowResize){
38035 * Ext JS Library 1.1.1
38036 * Copyright(c) 2006-2007, Ext JS, LLC.
38038 * Originally Released Under LGPL - original licence link has changed is not relivant.
38041 * <script type="text/javascript">
38044 * @class Roo.bootstrap.layout.Border
38045 * @extends Roo.bootstrap.layout.Manager
38046 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38047 * please see: examples/bootstrap/nested.html<br><br>
38049 <b>The container the layout is rendered into can be either the body element or any other element.
38050 If it is not the body element, the container needs to either be an absolute positioned element,
38051 or you will need to add "position:relative" to the css of the container. You will also need to specify
38052 the container size if it is not the body element.</b>
38055 * Create a new Border
38056 * @param {Object} config Configuration options
38058 Roo.bootstrap.layout.Border = function(config){
38059 config = config || {};
38060 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38064 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38065 if(config[region]){
38066 config[region].region = region;
38067 this.addRegion(config[region]);
38073 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38075 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38077 parent : false, // this might point to a 'nest' or a ???
38080 * Creates and adds a new region if it doesn't already exist.
38081 * @param {String} target The target region key (north, south, east, west or center).
38082 * @param {Object} config The regions config object
38083 * @return {BorderLayoutRegion} The new region
38085 addRegion : function(config)
38087 if(!this.regions[config.region]){
38088 var r = this.factory(config);
38089 this.bindRegion(r);
38091 return this.regions[config.region];
38095 bindRegion : function(r){
38096 this.regions[r.config.region] = r;
38098 r.on("visibilitychange", this.layout, this);
38099 r.on("paneladded", this.layout, this);
38100 r.on("panelremoved", this.layout, this);
38101 r.on("invalidated", this.layout, this);
38102 r.on("resized", this.onRegionResized, this);
38103 r.on("collapsed", this.onRegionCollapsed, this);
38104 r.on("expanded", this.onRegionExpanded, this);
38108 * Performs a layout update.
38110 layout : function()
38112 if(this.updating) {
38116 // render all the rebions if they have not been done alreayd?
38117 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38118 if(this.regions[region] && !this.regions[region].bodyEl){
38119 this.regions[region].onRender(this.el)
38123 var size = this.getViewSize();
38124 var w = size.width;
38125 var h = size.height;
38130 //var x = 0, y = 0;
38132 var rs = this.regions;
38133 var north = rs["north"];
38134 var south = rs["south"];
38135 var west = rs["west"];
38136 var east = rs["east"];
38137 var center = rs["center"];
38138 //if(this.hideOnLayout){ // not supported anymore
38139 //c.el.setStyle("display", "none");
38141 if(north && north.isVisible()){
38142 var b = north.getBox();
38143 var m = north.getMargins();
38144 b.width = w - (m.left+m.right);
38147 centerY = b.height + b.y + m.bottom;
38148 centerH -= centerY;
38149 north.updateBox(this.safeBox(b));
38151 if(south && south.isVisible()){
38152 var b = south.getBox();
38153 var m = south.getMargins();
38154 b.width = w - (m.left+m.right);
38156 var totalHeight = (b.height + m.top + m.bottom);
38157 b.y = h - totalHeight + m.top;
38158 centerH -= totalHeight;
38159 south.updateBox(this.safeBox(b));
38161 if(west && west.isVisible()){
38162 var b = west.getBox();
38163 var m = west.getMargins();
38164 b.height = centerH - (m.top+m.bottom);
38166 b.y = centerY + m.top;
38167 var totalWidth = (b.width + m.left + m.right);
38168 centerX += totalWidth;
38169 centerW -= totalWidth;
38170 west.updateBox(this.safeBox(b));
38172 if(east && east.isVisible()){
38173 var b = east.getBox();
38174 var m = east.getMargins();
38175 b.height = centerH - (m.top+m.bottom);
38176 var totalWidth = (b.width + m.left + m.right);
38177 b.x = w - totalWidth + m.left;
38178 b.y = centerY + m.top;
38179 centerW -= totalWidth;
38180 east.updateBox(this.safeBox(b));
38183 var m = center.getMargins();
38185 x: centerX + m.left,
38186 y: centerY + m.top,
38187 width: centerW - (m.left+m.right),
38188 height: centerH - (m.top+m.bottom)
38190 //if(this.hideOnLayout){
38191 //center.el.setStyle("display", "block");
38193 center.updateBox(this.safeBox(centerBox));
38196 this.fireEvent("layout", this);
38200 safeBox : function(box){
38201 box.width = Math.max(0, box.width);
38202 box.height = Math.max(0, box.height);
38207 * Adds a ContentPanel (or subclass) to this layout.
38208 * @param {String} target The target region key (north, south, east, west or center).
38209 * @param {Roo.ContentPanel} panel The panel to add
38210 * @return {Roo.ContentPanel} The added panel
38212 add : function(target, panel){
38214 target = target.toLowerCase();
38215 return this.regions[target].add(panel);
38219 * Remove a ContentPanel (or subclass) to this layout.
38220 * @param {String} target The target region key (north, south, east, west or center).
38221 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38222 * @return {Roo.ContentPanel} The removed panel
38224 remove : function(target, panel){
38225 target = target.toLowerCase();
38226 return this.regions[target].remove(panel);
38230 * Searches all regions for a panel with the specified id
38231 * @param {String} panelId
38232 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38234 findPanel : function(panelId){
38235 var rs = this.regions;
38236 for(var target in rs){
38237 if(typeof rs[target] != "function"){
38238 var p = rs[target].getPanel(panelId);
38248 * Searches all regions for a panel with the specified id and activates (shows) it.
38249 * @param {String/ContentPanel} panelId The panels id or the panel itself
38250 * @return {Roo.ContentPanel} The shown panel or null
38252 showPanel : function(panelId) {
38253 var rs = this.regions;
38254 for(var target in rs){
38255 var r = rs[target];
38256 if(typeof r != "function"){
38257 if(r.hasPanel(panelId)){
38258 return r.showPanel(panelId);
38266 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38267 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38270 restoreState : function(provider){
38272 provider = Roo.state.Manager;
38274 var sm = new Roo.LayoutStateManager();
38275 sm.init(this, provider);
38281 * Adds a xtype elements to the layout.
38285 xtype : 'ContentPanel',
38292 xtype : 'NestedLayoutPanel',
38298 items : [ ... list of content panels or nested layout panels.. ]
38302 * @param {Object} cfg Xtype definition of item to add.
38304 addxtype : function(cfg)
38306 // basically accepts a pannel...
38307 // can accept a layout region..!?!?
38308 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38311 // theory? children can only be panels??
38313 //if (!cfg.xtype.match(/Panel$/)) {
38318 if (typeof(cfg.region) == 'undefined') {
38319 Roo.log("Failed to add Panel, region was not set");
38323 var region = cfg.region;
38329 xitems = cfg.items;
38334 if ( region == 'center') {
38335 Roo.log("Center: " + cfg.title);
38341 case 'Content': // ContentPanel (el, cfg)
38342 case 'Scroll': // ContentPanel (el, cfg)
38344 cfg.autoCreate = cfg.autoCreate || true;
38345 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38347 // var el = this.el.createChild();
38348 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38351 this.add(region, ret);
38355 case 'TreePanel': // our new panel!
38356 cfg.el = this.el.createChild();
38357 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38358 this.add(region, ret);
38363 // create a new Layout (which is a Border Layout...
38365 var clayout = cfg.layout;
38366 clayout.el = this.el.createChild();
38367 clayout.items = clayout.items || [];
38371 // replace this exitems with the clayout ones..
38372 xitems = clayout.items;
38374 // force background off if it's in center...
38375 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38376 cfg.background = false;
38378 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38381 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38382 //console.log('adding nested layout panel ' + cfg.toSource());
38383 this.add(region, ret);
38384 nb = {}; /// find first...
38389 // needs grid and region
38391 //var el = this.getRegion(region).el.createChild();
38393 *var el = this.el.createChild();
38394 // create the grid first...
38395 cfg.grid.container = el;
38396 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38399 if (region == 'center' && this.active ) {
38400 cfg.background = false;
38403 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38405 this.add(region, ret);
38407 if (cfg.background) {
38408 // render grid on panel activation (if panel background)
38409 ret.on('activate', function(gp) {
38410 if (!gp.grid.rendered) {
38411 // gp.grid.render(el);
38415 // cfg.grid.render(el);
38421 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38422 // it was the old xcomponent building that caused this before.
38423 // espeically if border is the top element in the tree.
38433 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38435 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38436 this.add(region, ret);
38440 throw "Can not add '" + cfg.xtype + "' to Border";
38446 this.beginUpdate();
38450 Roo.each(xitems, function(i) {
38451 region = nb && i.region ? i.region : false;
38453 var add = ret.addxtype(i);
38456 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38457 if (!i.background) {
38458 abn[region] = nb[region] ;
38465 // make the last non-background panel active..
38466 //if (nb) { Roo.log(abn); }
38469 for(var r in abn) {
38470 region = this.getRegion(r);
38472 // tried using nb[r], but it does not work..
38474 region.showPanel(abn[r]);
38485 factory : function(cfg)
38488 var validRegions = Roo.bootstrap.layout.Border.regions;
38490 var target = cfg.region;
38493 var r = Roo.bootstrap.layout;
38497 return new r.North(cfg);
38499 return new r.South(cfg);
38501 return new r.East(cfg);
38503 return new r.West(cfg);
38505 return new r.Center(cfg);
38507 throw 'Layout region "'+target+'" not supported.';
38514 * Ext JS Library 1.1.1
38515 * Copyright(c) 2006-2007, Ext JS, LLC.
38517 * Originally Released Under LGPL - original licence link has changed is not relivant.
38520 * <script type="text/javascript">
38524 * @class Roo.bootstrap.layout.Basic
38525 * @extends Roo.util.Observable
38526 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38527 * and does not have a titlebar, tabs or any other features. All it does is size and position
38528 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38529 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38530 * @cfg {string} region the region that it inhabits..
38531 * @cfg {bool} skipConfig skip config?
38535 Roo.bootstrap.layout.Basic = function(config){
38537 this.mgr = config.mgr;
38539 this.position = config.region;
38541 var skipConfig = config.skipConfig;
38545 * @scope Roo.BasicLayoutRegion
38549 * @event beforeremove
38550 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38551 * @param {Roo.LayoutRegion} this
38552 * @param {Roo.ContentPanel} panel The panel
38553 * @param {Object} e The cancel event object
38555 "beforeremove" : true,
38557 * @event invalidated
38558 * Fires when the layout for this region is changed.
38559 * @param {Roo.LayoutRegion} this
38561 "invalidated" : true,
38563 * @event visibilitychange
38564 * Fires when this region is shown or hidden
38565 * @param {Roo.LayoutRegion} this
38566 * @param {Boolean} visibility true or false
38568 "visibilitychange" : true,
38570 * @event paneladded
38571 * Fires when a panel is added.
38572 * @param {Roo.LayoutRegion} this
38573 * @param {Roo.ContentPanel} panel The panel
38575 "paneladded" : true,
38577 * @event panelremoved
38578 * Fires when a panel is removed.
38579 * @param {Roo.LayoutRegion} this
38580 * @param {Roo.ContentPanel} panel The panel
38582 "panelremoved" : true,
38584 * @event beforecollapse
38585 * Fires when this region before collapse.
38586 * @param {Roo.LayoutRegion} this
38588 "beforecollapse" : true,
38591 * Fires when this region is collapsed.
38592 * @param {Roo.LayoutRegion} this
38594 "collapsed" : true,
38597 * Fires when this region is expanded.
38598 * @param {Roo.LayoutRegion} this
38603 * Fires when this region is slid into view.
38604 * @param {Roo.LayoutRegion} this
38606 "slideshow" : true,
38609 * Fires when this region slides out of view.
38610 * @param {Roo.LayoutRegion} this
38612 "slidehide" : true,
38614 * @event panelactivated
38615 * Fires when a panel is activated.
38616 * @param {Roo.LayoutRegion} this
38617 * @param {Roo.ContentPanel} panel The activated panel
38619 "panelactivated" : true,
38622 * Fires when the user resizes this region.
38623 * @param {Roo.LayoutRegion} this
38624 * @param {Number} newSize The new size (width for east/west, height for north/south)
38628 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38629 this.panels = new Roo.util.MixedCollection();
38630 this.panels.getKey = this.getPanelId.createDelegate(this);
38632 this.activePanel = null;
38633 // ensure listeners are added...
38635 if (config.listeners || config.events) {
38636 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38637 listeners : config.listeners || {},
38638 events : config.events || {}
38642 if(skipConfig !== true){
38643 this.applyConfig(config);
38647 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38649 getPanelId : function(p){
38653 applyConfig : function(config){
38654 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38655 this.config = config;
38660 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38661 * the width, for horizontal (north, south) the height.
38662 * @param {Number} newSize The new width or height
38664 resizeTo : function(newSize){
38665 var el = this.el ? this.el :
38666 (this.activePanel ? this.activePanel.getEl() : null);
38668 switch(this.position){
38671 el.setWidth(newSize);
38672 this.fireEvent("resized", this, newSize);
38676 el.setHeight(newSize);
38677 this.fireEvent("resized", this, newSize);
38683 getBox : function(){
38684 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38687 getMargins : function(){
38688 return this.margins;
38691 updateBox : function(box){
38693 var el = this.activePanel.getEl();
38694 el.dom.style.left = box.x + "px";
38695 el.dom.style.top = box.y + "px";
38696 this.activePanel.setSize(box.width, box.height);
38700 * Returns the container element for this region.
38701 * @return {Roo.Element}
38703 getEl : function(){
38704 return this.activePanel;
38708 * Returns true if this region is currently visible.
38709 * @return {Boolean}
38711 isVisible : function(){
38712 return this.activePanel ? true : false;
38715 setActivePanel : function(panel){
38716 panel = this.getPanel(panel);
38717 if(this.activePanel && this.activePanel != panel){
38718 this.activePanel.setActiveState(false);
38719 this.activePanel.getEl().setLeftTop(-10000,-10000);
38721 this.activePanel = panel;
38722 panel.setActiveState(true);
38724 panel.setSize(this.box.width, this.box.height);
38726 this.fireEvent("panelactivated", this, panel);
38727 this.fireEvent("invalidated");
38731 * Show the specified panel.
38732 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38733 * @return {Roo.ContentPanel} The shown panel or null
38735 showPanel : function(panel){
38736 panel = this.getPanel(panel);
38738 this.setActivePanel(panel);
38744 * Get the active panel for this region.
38745 * @return {Roo.ContentPanel} The active panel or null
38747 getActivePanel : function(){
38748 return this.activePanel;
38752 * Add the passed ContentPanel(s)
38753 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38754 * @return {Roo.ContentPanel} The panel added (if only one was added)
38756 add : function(panel){
38757 if(arguments.length > 1){
38758 for(var i = 0, len = arguments.length; i < len; i++) {
38759 this.add(arguments[i]);
38763 if(this.hasPanel(panel)){
38764 this.showPanel(panel);
38767 var el = panel.getEl();
38768 if(el.dom.parentNode != this.mgr.el.dom){
38769 this.mgr.el.dom.appendChild(el.dom);
38771 if(panel.setRegion){
38772 panel.setRegion(this);
38774 this.panels.add(panel);
38775 el.setStyle("position", "absolute");
38776 if(!panel.background){
38777 this.setActivePanel(panel);
38778 if(this.config.initialSize && this.panels.getCount()==1){
38779 this.resizeTo(this.config.initialSize);
38782 this.fireEvent("paneladded", this, panel);
38787 * Returns true if the panel is in this region.
38788 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38789 * @return {Boolean}
38791 hasPanel : function(panel){
38792 if(typeof panel == "object"){ // must be panel obj
38793 panel = panel.getId();
38795 return this.getPanel(panel) ? true : false;
38799 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38800 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38801 * @param {Boolean} preservePanel Overrides the config preservePanel option
38802 * @return {Roo.ContentPanel} The panel that was removed
38804 remove : function(panel, preservePanel){
38805 panel = this.getPanel(panel);
38810 this.fireEvent("beforeremove", this, panel, e);
38811 if(e.cancel === true){
38814 var panelId = panel.getId();
38815 this.panels.removeKey(panelId);
38820 * Returns the panel specified or null if it's not in this region.
38821 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38822 * @return {Roo.ContentPanel}
38824 getPanel : function(id){
38825 if(typeof id == "object"){ // must be panel obj
38828 return this.panels.get(id);
38832 * Returns this regions position (north/south/east/west/center).
38835 getPosition: function(){
38836 return this.position;
38840 * Ext JS Library 1.1.1
38841 * Copyright(c) 2006-2007, Ext JS, LLC.
38843 * Originally Released Under LGPL - original licence link has changed is not relivant.
38846 * <script type="text/javascript">
38850 * @class Roo.bootstrap.layout.Region
38851 * @extends Roo.bootstrap.layout.Basic
38852 * This class represents a region in a layout manager.
38854 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38855 * @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})
38856 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38857 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38858 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38859 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38860 * @cfg {String} title The title for the region (overrides panel titles)
38861 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38862 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38863 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38864 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38865 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38866 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38867 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38868 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38869 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38870 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38872 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38873 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38874 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38875 * @cfg {Number} width For East/West panels
38876 * @cfg {Number} height For North/South panels
38877 * @cfg {Boolean} split To show the splitter
38878 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38880 * @cfg {string} cls Extra CSS classes to add to region
38882 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38883 * @cfg {string} region the region that it inhabits..
38886 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38887 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38889 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38890 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38891 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38893 Roo.bootstrap.layout.Region = function(config)
38895 this.applyConfig(config);
38897 var mgr = config.mgr;
38898 var pos = config.region;
38899 config.skipConfig = true;
38900 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38903 this.onRender(mgr.el);
38906 this.visible = true;
38907 this.collapsed = false;
38908 this.unrendered_panels = [];
38911 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38913 position: '', // set by wrapper (eg. north/south etc..)
38914 unrendered_panels : null, // unrendered panels.
38916 tabPosition : false,
38918 mgr: false, // points to 'Border'
38921 createBody : function(){
38922 /** This region's body element
38923 * @type Roo.Element */
38924 this.bodyEl = this.el.createChild({
38926 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38930 onRender: function(ctr, pos)
38932 var dh = Roo.DomHelper;
38933 /** This region's container element
38934 * @type Roo.Element */
38935 this.el = dh.append(ctr.dom, {
38937 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38939 /** This region's title element
38940 * @type Roo.Element */
38942 this.titleEl = dh.append(this.el.dom, {
38944 unselectable: "on",
38945 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38947 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38948 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38952 this.titleEl.enableDisplayMode();
38953 /** This region's title text element
38954 * @type HTMLElement */
38955 this.titleTextEl = this.titleEl.dom.firstChild;
38956 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38958 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38959 this.closeBtn.enableDisplayMode();
38960 this.closeBtn.on("click", this.closeClicked, this);
38961 this.closeBtn.hide();
38963 this.createBody(this.config);
38964 if(this.config.hideWhenEmpty){
38966 this.on("paneladded", this.validateVisibility, this);
38967 this.on("panelremoved", this.validateVisibility, this);
38969 if(this.autoScroll){
38970 this.bodyEl.setStyle("overflow", "auto");
38972 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38974 //if(c.titlebar !== false){
38975 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38976 this.titleEl.hide();
38978 this.titleEl.show();
38979 if(this.config.title){
38980 this.titleTextEl.innerHTML = this.config.title;
38984 if(this.config.collapsed){
38985 this.collapse(true);
38987 if(this.config.hidden){
38991 if (this.unrendered_panels && this.unrendered_panels.length) {
38992 for (var i =0;i< this.unrendered_panels.length; i++) {
38993 this.add(this.unrendered_panels[i]);
38995 this.unrendered_panels = null;
39001 applyConfig : function(c)
39004 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39005 var dh = Roo.DomHelper;
39006 if(c.titlebar !== false){
39007 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39008 this.collapseBtn.on("click", this.collapse, this);
39009 this.collapseBtn.enableDisplayMode();
39011 if(c.showPin === true || this.showPin){
39012 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39013 this.stickBtn.enableDisplayMode();
39014 this.stickBtn.on("click", this.expand, this);
39015 this.stickBtn.hide();
39020 /** This region's collapsed element
39021 * @type Roo.Element */
39024 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39025 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39028 if(c.floatable !== false){
39029 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39030 this.collapsedEl.on("click", this.collapseClick, this);
39033 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39034 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39035 id: "message", unselectable: "on", style:{"float":"left"}});
39036 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39038 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39039 this.expandBtn.on("click", this.expand, this);
39043 if(this.collapseBtn){
39044 this.collapseBtn.setVisible(c.collapsible == true);
39047 this.cmargins = c.cmargins || this.cmargins ||
39048 (this.position == "west" || this.position == "east" ?
39049 {top: 0, left: 2, right:2, bottom: 0} :
39050 {top: 2, left: 0, right:0, bottom: 2});
39052 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39055 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39057 this.autoScroll = c.autoScroll || false;
39062 this.duration = c.duration || .30;
39063 this.slideDuration = c.slideDuration || .45;
39068 * Returns true if this region is currently visible.
39069 * @return {Boolean}
39071 isVisible : function(){
39072 return this.visible;
39076 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39077 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39079 //setCollapsedTitle : function(title){
39080 // title = title || " ";
39081 // if(this.collapsedTitleTextEl){
39082 // this.collapsedTitleTextEl.innerHTML = title;
39086 getBox : function(){
39088 // if(!this.collapsed){
39089 b = this.el.getBox(false, true);
39091 // b = this.collapsedEl.getBox(false, true);
39096 getMargins : function(){
39097 return this.margins;
39098 //return this.collapsed ? this.cmargins : this.margins;
39101 highlight : function(){
39102 this.el.addClass("x-layout-panel-dragover");
39105 unhighlight : function(){
39106 this.el.removeClass("x-layout-panel-dragover");
39109 updateBox : function(box)
39111 if (!this.bodyEl) {
39112 return; // not rendered yet..
39116 if(!this.collapsed){
39117 this.el.dom.style.left = box.x + "px";
39118 this.el.dom.style.top = box.y + "px";
39119 this.updateBody(box.width, box.height);
39121 this.collapsedEl.dom.style.left = box.x + "px";
39122 this.collapsedEl.dom.style.top = box.y + "px";
39123 this.collapsedEl.setSize(box.width, box.height);
39126 this.tabs.autoSizeTabs();
39130 updateBody : function(w, h)
39133 this.el.setWidth(w);
39134 w -= this.el.getBorderWidth("rl");
39135 if(this.config.adjustments){
39136 w += this.config.adjustments[0];
39139 if(h !== null && h > 0){
39140 this.el.setHeight(h);
39141 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39142 h -= this.el.getBorderWidth("tb");
39143 if(this.config.adjustments){
39144 h += this.config.adjustments[1];
39146 this.bodyEl.setHeight(h);
39148 h = this.tabs.syncHeight(h);
39151 if(this.panelSize){
39152 w = w !== null ? w : this.panelSize.width;
39153 h = h !== null ? h : this.panelSize.height;
39155 if(this.activePanel){
39156 var el = this.activePanel.getEl();
39157 w = w !== null ? w : el.getWidth();
39158 h = h !== null ? h : el.getHeight();
39159 this.panelSize = {width: w, height: h};
39160 this.activePanel.setSize(w, h);
39162 if(Roo.isIE && this.tabs){
39163 this.tabs.el.repaint();
39168 * Returns the container element for this region.
39169 * @return {Roo.Element}
39171 getEl : function(){
39176 * Hides this region.
39179 //if(!this.collapsed){
39180 this.el.dom.style.left = "-2000px";
39183 // this.collapsedEl.dom.style.left = "-2000px";
39184 // this.collapsedEl.hide();
39186 this.visible = false;
39187 this.fireEvent("visibilitychange", this, false);
39191 * Shows this region if it was previously hidden.
39194 //if(!this.collapsed){
39197 // this.collapsedEl.show();
39199 this.visible = true;
39200 this.fireEvent("visibilitychange", this, true);
39203 closeClicked : function(){
39204 if(this.activePanel){
39205 this.remove(this.activePanel);
39209 collapseClick : function(e){
39211 e.stopPropagation();
39214 e.stopPropagation();
39220 * Collapses this region.
39221 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39224 collapse : function(skipAnim, skipCheck = false){
39225 if(this.collapsed) {
39229 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39231 this.collapsed = true;
39233 this.split.el.hide();
39235 if(this.config.animate && skipAnim !== true){
39236 this.fireEvent("invalidated", this);
39237 this.animateCollapse();
39239 this.el.setLocation(-20000,-20000);
39241 this.collapsedEl.show();
39242 this.fireEvent("collapsed", this);
39243 this.fireEvent("invalidated", this);
39249 animateCollapse : function(){
39254 * Expands this region if it was previously collapsed.
39255 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39256 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39259 expand : function(e, skipAnim){
39261 e.stopPropagation();
39263 if(!this.collapsed || this.el.hasActiveFx()) {
39267 this.afterSlideIn();
39270 this.collapsed = false;
39271 if(this.config.animate && skipAnim !== true){
39272 this.animateExpand();
39276 this.split.el.show();
39278 this.collapsedEl.setLocation(-2000,-2000);
39279 this.collapsedEl.hide();
39280 this.fireEvent("invalidated", this);
39281 this.fireEvent("expanded", this);
39285 animateExpand : function(){
39289 initTabs : function()
39291 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39293 var ts = new Roo.bootstrap.panel.Tabs({
39294 el: this.bodyEl.dom,
39296 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39297 disableTooltips: this.config.disableTabTips,
39298 toolbar : this.config.toolbar
39301 if(this.config.hideTabs){
39302 ts.stripWrap.setDisplayed(false);
39305 ts.resizeTabs = this.config.resizeTabs === true;
39306 ts.minTabWidth = this.config.minTabWidth || 40;
39307 ts.maxTabWidth = this.config.maxTabWidth || 250;
39308 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39309 ts.monitorResize = false;
39310 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39311 ts.bodyEl.addClass('roo-layout-tabs-body');
39312 this.panels.each(this.initPanelAsTab, this);
39315 initPanelAsTab : function(panel){
39316 var ti = this.tabs.addTab(
39320 this.config.closeOnTab && panel.isClosable(),
39323 if(panel.tabTip !== undefined){
39324 ti.setTooltip(panel.tabTip);
39326 ti.on("activate", function(){
39327 this.setActivePanel(panel);
39330 if(this.config.closeOnTab){
39331 ti.on("beforeclose", function(t, e){
39333 this.remove(panel);
39337 panel.tabItem = ti;
39342 updatePanelTitle : function(panel, title)
39344 if(this.activePanel == panel){
39345 this.updateTitle(title);
39348 var ti = this.tabs.getTab(panel.getEl().id);
39350 if(panel.tabTip !== undefined){
39351 ti.setTooltip(panel.tabTip);
39356 updateTitle : function(title){
39357 if(this.titleTextEl && !this.config.title){
39358 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39362 setActivePanel : function(panel)
39364 panel = this.getPanel(panel);
39365 if(this.activePanel && this.activePanel != panel){
39366 if(this.activePanel.setActiveState(false) === false){
39370 this.activePanel = panel;
39371 panel.setActiveState(true);
39372 if(this.panelSize){
39373 panel.setSize(this.panelSize.width, this.panelSize.height);
39376 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39378 this.updateTitle(panel.getTitle());
39380 this.fireEvent("invalidated", this);
39382 this.fireEvent("panelactivated", this, panel);
39386 * Shows the specified panel.
39387 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39388 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39390 showPanel : function(panel)
39392 panel = this.getPanel(panel);
39395 var tab = this.tabs.getTab(panel.getEl().id);
39396 if(tab.isHidden()){
39397 this.tabs.unhideTab(tab.id);
39401 this.setActivePanel(panel);
39408 * Get the active panel for this region.
39409 * @return {Roo.ContentPanel} The active panel or null
39411 getActivePanel : function(){
39412 return this.activePanel;
39415 validateVisibility : function(){
39416 if(this.panels.getCount() < 1){
39417 this.updateTitle(" ");
39418 this.closeBtn.hide();
39421 if(!this.isVisible()){
39428 * Adds the passed ContentPanel(s) to this region.
39429 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39430 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39432 add : function(panel)
39434 if(arguments.length > 1){
39435 for(var i = 0, len = arguments.length; i < len; i++) {
39436 this.add(arguments[i]);
39441 // if we have not been rendered yet, then we can not really do much of this..
39442 if (!this.bodyEl) {
39443 this.unrendered_panels.push(panel);
39450 if(this.hasPanel(panel)){
39451 this.showPanel(panel);
39454 panel.setRegion(this);
39455 this.panels.add(panel);
39456 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39457 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39458 // and hide them... ???
39459 this.bodyEl.dom.appendChild(panel.getEl().dom);
39460 if(panel.background !== true){
39461 this.setActivePanel(panel);
39463 this.fireEvent("paneladded", this, panel);
39470 this.initPanelAsTab(panel);
39474 if(panel.background !== true){
39475 this.tabs.activate(panel.getEl().id);
39477 this.fireEvent("paneladded", this, panel);
39482 * Hides the tab for the specified panel.
39483 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39485 hidePanel : function(panel){
39486 if(this.tabs && (panel = this.getPanel(panel))){
39487 this.tabs.hideTab(panel.getEl().id);
39492 * Unhides the tab for a previously hidden panel.
39493 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39495 unhidePanel : function(panel){
39496 if(this.tabs && (panel = this.getPanel(panel))){
39497 this.tabs.unhideTab(panel.getEl().id);
39501 clearPanels : function(){
39502 while(this.panels.getCount() > 0){
39503 this.remove(this.panels.first());
39508 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39509 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39510 * @param {Boolean} preservePanel Overrides the config preservePanel option
39511 * @return {Roo.ContentPanel} The panel that was removed
39513 remove : function(panel, preservePanel)
39515 panel = this.getPanel(panel);
39520 this.fireEvent("beforeremove", this, panel, e);
39521 if(e.cancel === true){
39524 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39525 var panelId = panel.getId();
39526 this.panels.removeKey(panelId);
39528 document.body.appendChild(panel.getEl().dom);
39531 this.tabs.removeTab(panel.getEl().id);
39532 }else if (!preservePanel){
39533 this.bodyEl.dom.removeChild(panel.getEl().dom);
39535 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39536 var p = this.panels.first();
39537 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39538 tempEl.appendChild(p.getEl().dom);
39539 this.bodyEl.update("");
39540 this.bodyEl.dom.appendChild(p.getEl().dom);
39542 this.updateTitle(p.getTitle());
39544 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39545 this.setActivePanel(p);
39547 panel.setRegion(null);
39548 if(this.activePanel == panel){
39549 this.activePanel = null;
39551 if(this.config.autoDestroy !== false && preservePanel !== true){
39552 try{panel.destroy();}catch(e){}
39554 this.fireEvent("panelremoved", this, panel);
39559 * Returns the TabPanel component used by this region
39560 * @return {Roo.TabPanel}
39562 getTabs : function(){
39566 createTool : function(parentEl, className){
39567 var btn = Roo.DomHelper.append(parentEl, {
39569 cls: "x-layout-tools-button",
39572 cls: "roo-layout-tools-button-inner " + className,
39576 btn.addClassOnOver("roo-layout-tools-button-over");
39581 * Ext JS Library 1.1.1
39582 * Copyright(c) 2006-2007, Ext JS, LLC.
39584 * Originally Released Under LGPL - original licence link has changed is not relivant.
39587 * <script type="text/javascript">
39593 * @class Roo.SplitLayoutRegion
39594 * @extends Roo.LayoutRegion
39595 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39597 Roo.bootstrap.layout.Split = function(config){
39598 this.cursor = config.cursor;
39599 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39602 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39604 splitTip : "Drag to resize.",
39605 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39606 useSplitTips : false,
39608 applyConfig : function(config){
39609 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39612 onRender : function(ctr,pos) {
39614 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39615 if(!this.config.split){
39620 var splitEl = Roo.DomHelper.append(ctr.dom, {
39622 id: this.el.id + "-split",
39623 cls: "roo-layout-split roo-layout-split-"+this.position,
39626 /** The SplitBar for this region
39627 * @type Roo.SplitBar */
39628 // does not exist yet...
39629 Roo.log([this.position, this.orientation]);
39631 this.split = new Roo.bootstrap.SplitBar({
39632 dragElement : splitEl,
39633 resizingElement: this.el,
39634 orientation : this.orientation
39637 this.split.on("moved", this.onSplitMove, this);
39638 this.split.useShim = this.config.useShim === true;
39639 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39640 if(this.useSplitTips){
39641 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39643 //if(config.collapsible){
39644 // this.split.el.on("dblclick", this.collapse, this);
39647 if(typeof this.config.minSize != "undefined"){
39648 this.split.minSize = this.config.minSize;
39650 if(typeof this.config.maxSize != "undefined"){
39651 this.split.maxSize = this.config.maxSize;
39653 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39654 this.hideSplitter();
39659 getHMaxSize : function(){
39660 var cmax = this.config.maxSize || 10000;
39661 var center = this.mgr.getRegion("center");
39662 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39665 getVMaxSize : function(){
39666 var cmax = this.config.maxSize || 10000;
39667 var center = this.mgr.getRegion("center");
39668 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39671 onSplitMove : function(split, newSize){
39672 this.fireEvent("resized", this, newSize);
39676 * Returns the {@link Roo.SplitBar} for this region.
39677 * @return {Roo.SplitBar}
39679 getSplitBar : function(){
39684 this.hideSplitter();
39685 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39688 hideSplitter : function(){
39690 this.split.el.setLocation(-2000,-2000);
39691 this.split.el.hide();
39697 this.split.el.show();
39699 Roo.bootstrap.layout.Split.superclass.show.call(this);
39702 beforeSlide: function(){
39703 if(Roo.isGecko){// firefox overflow auto bug workaround
39704 this.bodyEl.clip();
39706 this.tabs.bodyEl.clip();
39708 if(this.activePanel){
39709 this.activePanel.getEl().clip();
39711 if(this.activePanel.beforeSlide){
39712 this.activePanel.beforeSlide();
39718 afterSlide : function(){
39719 if(Roo.isGecko){// firefox overflow auto bug workaround
39720 this.bodyEl.unclip();
39722 this.tabs.bodyEl.unclip();
39724 if(this.activePanel){
39725 this.activePanel.getEl().unclip();
39726 if(this.activePanel.afterSlide){
39727 this.activePanel.afterSlide();
39733 initAutoHide : function(){
39734 if(this.autoHide !== false){
39735 if(!this.autoHideHd){
39736 var st = new Roo.util.DelayedTask(this.slideIn, this);
39737 this.autoHideHd = {
39738 "mouseout": function(e){
39739 if(!e.within(this.el, true)){
39743 "mouseover" : function(e){
39749 this.el.on(this.autoHideHd);
39753 clearAutoHide : function(){
39754 if(this.autoHide !== false){
39755 this.el.un("mouseout", this.autoHideHd.mouseout);
39756 this.el.un("mouseover", this.autoHideHd.mouseover);
39760 clearMonitor : function(){
39761 Roo.get(document).un("click", this.slideInIf, this);
39764 // these names are backwards but not changed for compat
39765 slideOut : function(){
39766 if(this.isSlid || this.el.hasActiveFx()){
39769 this.isSlid = true;
39770 if(this.collapseBtn){
39771 this.collapseBtn.hide();
39773 this.closeBtnState = this.closeBtn.getStyle('display');
39774 this.closeBtn.hide();
39776 this.stickBtn.show();
39779 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39780 this.beforeSlide();
39781 this.el.setStyle("z-index", 10001);
39782 this.el.slideIn(this.getSlideAnchor(), {
39783 callback: function(){
39785 this.initAutoHide();
39786 Roo.get(document).on("click", this.slideInIf, this);
39787 this.fireEvent("slideshow", this);
39794 afterSlideIn : function(){
39795 this.clearAutoHide();
39796 this.isSlid = false;
39797 this.clearMonitor();
39798 this.el.setStyle("z-index", "");
39799 if(this.collapseBtn){
39800 this.collapseBtn.show();
39802 this.closeBtn.setStyle('display', this.closeBtnState);
39804 this.stickBtn.hide();
39806 this.fireEvent("slidehide", this);
39809 slideIn : function(cb){
39810 if(!this.isSlid || this.el.hasActiveFx()){
39814 this.isSlid = false;
39815 this.beforeSlide();
39816 this.el.slideOut(this.getSlideAnchor(), {
39817 callback: function(){
39818 this.el.setLeftTop(-10000, -10000);
39820 this.afterSlideIn();
39828 slideInIf : function(e){
39829 if(!e.within(this.el)){
39834 animateCollapse : function(){
39835 this.beforeSlide();
39836 this.el.setStyle("z-index", 20000);
39837 var anchor = this.getSlideAnchor();
39838 this.el.slideOut(anchor, {
39839 callback : function(){
39840 this.el.setStyle("z-index", "");
39841 this.collapsedEl.slideIn(anchor, {duration:.3});
39843 this.el.setLocation(-10000,-10000);
39845 this.fireEvent("collapsed", this);
39852 animateExpand : function(){
39853 this.beforeSlide();
39854 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39855 this.el.setStyle("z-index", 20000);
39856 this.collapsedEl.hide({
39859 this.el.slideIn(this.getSlideAnchor(), {
39860 callback : function(){
39861 this.el.setStyle("z-index", "");
39864 this.split.el.show();
39866 this.fireEvent("invalidated", this);
39867 this.fireEvent("expanded", this);
39895 getAnchor : function(){
39896 return this.anchors[this.position];
39899 getCollapseAnchor : function(){
39900 return this.canchors[this.position];
39903 getSlideAnchor : function(){
39904 return this.sanchors[this.position];
39907 getAlignAdj : function(){
39908 var cm = this.cmargins;
39909 switch(this.position){
39925 getExpandAdj : function(){
39926 var c = this.collapsedEl, cm = this.cmargins;
39927 switch(this.position){
39929 return [-(cm.right+c.getWidth()+cm.left), 0];
39932 return [cm.right+c.getWidth()+cm.left, 0];
39935 return [0, -(cm.top+cm.bottom+c.getHeight())];
39938 return [0, cm.top+cm.bottom+c.getHeight()];
39944 * Ext JS Library 1.1.1
39945 * Copyright(c) 2006-2007, Ext JS, LLC.
39947 * Originally Released Under LGPL - original licence link has changed is not relivant.
39950 * <script type="text/javascript">
39953 * These classes are private internal classes
39955 Roo.bootstrap.layout.Center = function(config){
39956 config.region = "center";
39957 Roo.bootstrap.layout.Region.call(this, config);
39958 this.visible = true;
39959 this.minWidth = config.minWidth || 20;
39960 this.minHeight = config.minHeight || 20;
39963 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39965 // center panel can't be hidden
39969 // center panel can't be hidden
39972 getMinWidth: function(){
39973 return this.minWidth;
39976 getMinHeight: function(){
39977 return this.minHeight;
39991 Roo.bootstrap.layout.North = function(config)
39993 config.region = 'north';
39994 config.cursor = 'n-resize';
39996 Roo.bootstrap.layout.Split.call(this, config);
40000 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40001 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40002 this.split.el.addClass("roo-layout-split-v");
40004 //var size = config.initialSize || config.height;
40005 //if(this.el && typeof size != "undefined"){
40006 // this.el.setHeight(size);
40009 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40011 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40014 onRender : function(ctr, pos)
40016 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40017 var size = this.config.initialSize || this.config.height;
40018 if(this.el && typeof size != "undefined"){
40019 this.el.setHeight(size);
40024 getBox : function(){
40025 if(this.collapsed){
40026 return this.collapsedEl.getBox();
40028 var box = this.el.getBox();
40030 box.height += this.split.el.getHeight();
40035 updateBox : function(box){
40036 if(this.split && !this.collapsed){
40037 box.height -= this.split.el.getHeight();
40038 this.split.el.setLeft(box.x);
40039 this.split.el.setTop(box.y+box.height);
40040 this.split.el.setWidth(box.width);
40042 if(this.collapsed){
40043 this.updateBody(box.width, null);
40045 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40053 Roo.bootstrap.layout.South = function(config){
40054 config.region = 'south';
40055 config.cursor = 's-resize';
40056 Roo.bootstrap.layout.Split.call(this, config);
40058 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40059 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40060 this.split.el.addClass("roo-layout-split-v");
40065 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40066 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40068 onRender : function(ctr, pos)
40070 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40071 var size = this.config.initialSize || this.config.height;
40072 if(this.el && typeof size != "undefined"){
40073 this.el.setHeight(size);
40078 getBox : function(){
40079 if(this.collapsed){
40080 return this.collapsedEl.getBox();
40082 var box = this.el.getBox();
40084 var sh = this.split.el.getHeight();
40091 updateBox : function(box){
40092 if(this.split && !this.collapsed){
40093 var sh = this.split.el.getHeight();
40096 this.split.el.setLeft(box.x);
40097 this.split.el.setTop(box.y-sh);
40098 this.split.el.setWidth(box.width);
40100 if(this.collapsed){
40101 this.updateBody(box.width, null);
40103 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40107 Roo.bootstrap.layout.East = function(config){
40108 config.region = "east";
40109 config.cursor = "e-resize";
40110 Roo.bootstrap.layout.Split.call(this, config);
40112 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40113 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40114 this.split.el.addClass("roo-layout-split-h");
40118 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40119 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40121 onRender : function(ctr, pos)
40123 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40124 var size = this.config.initialSize || this.config.width;
40125 if(this.el && typeof size != "undefined"){
40126 this.el.setWidth(size);
40131 getBox : function(){
40132 if(this.collapsed){
40133 return this.collapsedEl.getBox();
40135 var box = this.el.getBox();
40137 var sw = this.split.el.getWidth();
40144 updateBox : function(box){
40145 if(this.split && !this.collapsed){
40146 var sw = this.split.el.getWidth();
40148 this.split.el.setLeft(box.x);
40149 this.split.el.setTop(box.y);
40150 this.split.el.setHeight(box.height);
40153 if(this.collapsed){
40154 this.updateBody(null, box.height);
40156 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40160 Roo.bootstrap.layout.West = function(config){
40161 config.region = "west";
40162 config.cursor = "w-resize";
40164 Roo.bootstrap.layout.Split.call(this, config);
40166 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40167 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40168 this.split.el.addClass("roo-layout-split-h");
40172 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40173 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40175 onRender: function(ctr, pos)
40177 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40178 var size = this.config.initialSize || this.config.width;
40179 if(typeof size != "undefined"){
40180 this.el.setWidth(size);
40184 getBox : function(){
40185 if(this.collapsed){
40186 return this.collapsedEl.getBox();
40188 var box = this.el.getBox();
40189 if (box.width == 0) {
40190 box.width = this.config.width; // kludge?
40193 box.width += this.split.el.getWidth();
40198 updateBox : function(box){
40199 if(this.split && !this.collapsed){
40200 var sw = this.split.el.getWidth();
40202 this.split.el.setLeft(box.x+box.width);
40203 this.split.el.setTop(box.y);
40204 this.split.el.setHeight(box.height);
40206 if(this.collapsed){
40207 this.updateBody(null, box.height);
40209 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40211 });Roo.namespace("Roo.bootstrap.panel");/*
40213 * Ext JS Library 1.1.1
40214 * Copyright(c) 2006-2007, Ext JS, LLC.
40216 * Originally Released Under LGPL - original licence link has changed is not relivant.
40219 * <script type="text/javascript">
40222 * @class Roo.ContentPanel
40223 * @extends Roo.util.Observable
40224 * A basic ContentPanel element.
40225 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40226 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40227 * @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
40228 * @cfg {Boolean} closable True if the panel can be closed/removed
40229 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40230 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40231 * @cfg {Toolbar} toolbar A toolbar for this panel
40232 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40233 * @cfg {String} title The title for this panel
40234 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40235 * @cfg {String} url Calls {@link #setUrl} with this value
40236 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40237 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40238 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40239 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40240 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40241 * @cfg {Boolean} badges render the badges
40242 * @cfg {String} cls extra classes to use
40243 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40246 * Create a new ContentPanel.
40247 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40248 * @param {String/Object} config A string to set only the title or a config object
40249 * @param {String} content (optional) Set the HTML content for this panel
40250 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40252 Roo.bootstrap.panel.Content = function( config){
40254 this.tpl = config.tpl || false;
40256 var el = config.el;
40257 var content = config.content;
40259 if(config.autoCreate){ // xtype is available if this is called from factory
40262 this.el = Roo.get(el);
40263 if(!this.el && config && config.autoCreate){
40264 if(typeof config.autoCreate == "object"){
40265 if(!config.autoCreate.id){
40266 config.autoCreate.id = config.id||el;
40268 this.el = Roo.DomHelper.append(document.body,
40269 config.autoCreate, true);
40273 cls: (config.cls || '') +
40274 (config.background ? ' bg-' + config.background : '') +
40275 " roo-layout-inactive-content",
40278 if (config.iframe) {
40282 style : 'border: 0px',
40283 src : 'about:blank'
40289 elcfg.html = config.html;
40293 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40294 if (config.iframe) {
40295 this.iframeEl = this.el.select('iframe',true).first();
40300 this.closable = false;
40301 this.loaded = false;
40302 this.active = false;
40305 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40307 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40309 this.wrapEl = this.el; //this.el.wrap();
40311 if (config.toolbar.items) {
40312 ti = config.toolbar.items ;
40313 delete config.toolbar.items ;
40317 this.toolbar.render(this.wrapEl, 'before');
40318 for(var i =0;i < ti.length;i++) {
40319 // Roo.log(['add child', items[i]]);
40320 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40322 this.toolbar.items = nitems;
40323 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40324 delete config.toolbar;
40328 // xtype created footer. - not sure if will work as we normally have to render first..
40329 if (this.footer && !this.footer.el && this.footer.xtype) {
40330 if (!this.wrapEl) {
40331 this.wrapEl = this.el.wrap();
40334 this.footer.container = this.wrapEl.createChild();
40336 this.footer = Roo.factory(this.footer, Roo);
40341 if(typeof config == "string"){
40342 this.title = config;
40344 Roo.apply(this, config);
40348 this.resizeEl = Roo.get(this.resizeEl, true);
40350 this.resizeEl = this.el;
40352 // handle view.xtype
40360 * Fires when this panel is activated.
40361 * @param {Roo.ContentPanel} this
40365 * @event deactivate
40366 * Fires when this panel is activated.
40367 * @param {Roo.ContentPanel} this
40369 "deactivate" : true,
40373 * Fires when this panel is resized if fitToFrame is true.
40374 * @param {Roo.ContentPanel} this
40375 * @param {Number} width The width after any component adjustments
40376 * @param {Number} height The height after any component adjustments
40382 * Fires when this tab is created
40383 * @param {Roo.ContentPanel} this
40389 * Fires when this content is scrolled
40390 * @param {Roo.ContentPanel} this
40391 * @param {Event} scrollEvent
40402 if(this.autoScroll && !this.iframe){
40403 this.resizeEl.setStyle("overflow", "auto");
40404 this.resizeEl.on('scroll', this.onScroll, this);
40406 // fix randome scrolling
40407 //this.el.on('scroll', function() {
40408 // Roo.log('fix random scolling');
40409 // this.scrollTo('top',0);
40412 content = content || this.content;
40414 this.setContent(content);
40416 if(config && config.url){
40417 this.setUrl(this.url, this.params, this.loadOnce);
40422 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40424 if (this.view && typeof(this.view.xtype) != 'undefined') {
40425 this.view.el = this.el.appendChild(document.createElement("div"));
40426 this.view = Roo.factory(this.view);
40427 this.view.render && this.view.render(false, '');
40431 this.fireEvent('render', this);
40434 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40444 /* Resize Element - use this to work out scroll etc. */
40447 setRegion : function(region){
40448 this.region = region;
40449 this.setActiveClass(region && !this.background);
40453 setActiveClass: function(state)
40456 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40457 this.el.setStyle('position','relative');
40459 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40460 this.el.setStyle('position', 'absolute');
40465 * Returns the toolbar for this Panel if one was configured.
40466 * @return {Roo.Toolbar}
40468 getToolbar : function(){
40469 return this.toolbar;
40472 setActiveState : function(active)
40474 this.active = active;
40475 this.setActiveClass(active);
40477 if(this.fireEvent("deactivate", this) === false){
40482 this.fireEvent("activate", this);
40486 * Updates this panel's element (not for iframe)
40487 * @param {String} content The new content
40488 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40490 setContent : function(content, loadScripts){
40495 this.el.update(content, loadScripts);
40498 ignoreResize : function(w, h){
40499 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40502 this.lastSize = {width: w, height: h};
40507 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40508 * @return {Roo.UpdateManager} The UpdateManager
40510 getUpdateManager : function(){
40514 return this.el.getUpdateManager();
40517 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40518 * Does not work with IFRAME contents
40519 * @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:
40522 url: "your-url.php",
40523 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40524 callback: yourFunction,
40525 scope: yourObject, //(optional scope)
40528 text: "Loading...",
40534 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40535 * 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.
40536 * @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}
40537 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40538 * @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.
40539 * @return {Roo.ContentPanel} this
40547 var um = this.el.getUpdateManager();
40548 um.update.apply(um, arguments);
40554 * 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.
40555 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40556 * @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)
40557 * @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)
40558 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40560 setUrl : function(url, params, loadOnce){
40562 this.iframeEl.dom.src = url;
40566 if(this.refreshDelegate){
40567 this.removeListener("activate", this.refreshDelegate);
40569 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40570 this.on("activate", this.refreshDelegate);
40571 return this.el.getUpdateManager();
40574 _handleRefresh : function(url, params, loadOnce){
40575 if(!loadOnce || !this.loaded){
40576 var updater = this.el.getUpdateManager();
40577 updater.update(url, params, this._setLoaded.createDelegate(this));
40581 _setLoaded : function(){
40582 this.loaded = true;
40586 * Returns this panel's id
40589 getId : function(){
40594 * Returns this panel's element - used by regiosn to add.
40595 * @return {Roo.Element}
40597 getEl : function(){
40598 return this.wrapEl || this.el;
40603 adjustForComponents : function(width, height)
40605 //Roo.log('adjustForComponents ');
40606 if(this.resizeEl != this.el){
40607 width -= this.el.getFrameWidth('lr');
40608 height -= this.el.getFrameWidth('tb');
40611 var te = this.toolbar.getEl();
40612 te.setWidth(width);
40613 height -= te.getHeight();
40616 var te = this.footer.getEl();
40617 te.setWidth(width);
40618 height -= te.getHeight();
40622 if(this.adjustments){
40623 width += this.adjustments[0];
40624 height += this.adjustments[1];
40626 return {"width": width, "height": height};
40629 setSize : function(width, height){
40630 if(this.fitToFrame && !this.ignoreResize(width, height)){
40631 if(this.fitContainer && this.resizeEl != this.el){
40632 this.el.setSize(width, height);
40634 var size = this.adjustForComponents(width, height);
40636 this.iframeEl.setSize(width,height);
40639 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40640 this.fireEvent('resize', this, size.width, size.height);
40647 * Returns this panel's title
40650 getTitle : function(){
40652 if (typeof(this.title) != 'object') {
40657 for (var k in this.title) {
40658 if (!this.title.hasOwnProperty(k)) {
40662 if (k.indexOf('-') >= 0) {
40663 var s = k.split('-');
40664 for (var i = 0; i<s.length; i++) {
40665 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40668 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40675 * Set this panel's title
40676 * @param {String} title
40678 setTitle : function(title){
40679 this.title = title;
40681 this.region.updatePanelTitle(this, title);
40686 * Returns true is this panel was configured to be closable
40687 * @return {Boolean}
40689 isClosable : function(){
40690 return this.closable;
40693 beforeSlide : function(){
40695 this.resizeEl.clip();
40698 afterSlide : function(){
40700 this.resizeEl.unclip();
40704 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40705 * Will fail silently if the {@link #setUrl} method has not been called.
40706 * This does not activate the panel, just updates its content.
40708 refresh : function(){
40709 if(this.refreshDelegate){
40710 this.loaded = false;
40711 this.refreshDelegate();
40716 * Destroys this panel
40718 destroy : function(){
40719 this.el.removeAllListeners();
40720 var tempEl = document.createElement("span");
40721 tempEl.appendChild(this.el.dom);
40722 tempEl.innerHTML = "";
40728 * form - if the content panel contains a form - this is a reference to it.
40729 * @type {Roo.form.Form}
40733 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40734 * This contains a reference to it.
40740 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40750 * @param {Object} cfg Xtype definition of item to add.
40754 getChildContainer: function () {
40755 return this.getEl();
40759 onScroll : function(e)
40761 this.fireEvent('scroll', this, e);
40766 var ret = new Roo.factory(cfg);
40771 if (cfg.xtype.match(/^Form$/)) {
40774 //if (this.footer) {
40775 // el = this.footer.container.insertSibling(false, 'before');
40777 el = this.el.createChild();
40780 this.form = new Roo.form.Form(cfg);
40783 if ( this.form.allItems.length) {
40784 this.form.render(el.dom);
40788 // should only have one of theses..
40789 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40790 // views.. should not be just added - used named prop 'view''
40792 cfg.el = this.el.appendChild(document.createElement("div"));
40795 var ret = new Roo.factory(cfg);
40797 ret.render && ret.render(false, ''); // render blank..
40807 * @class Roo.bootstrap.panel.Grid
40808 * @extends Roo.bootstrap.panel.Content
40810 * Create a new GridPanel.
40811 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40812 * @param {Object} config A the config object
40818 Roo.bootstrap.panel.Grid = function(config)
40822 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40823 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40825 config.el = this.wrapper;
40826 //this.el = this.wrapper;
40828 if (config.container) {
40829 // ctor'ed from a Border/panel.grid
40832 this.wrapper.setStyle("overflow", "hidden");
40833 this.wrapper.addClass('roo-grid-container');
40838 if(config.toolbar){
40839 var tool_el = this.wrapper.createChild();
40840 this.toolbar = Roo.factory(config.toolbar);
40842 if (config.toolbar.items) {
40843 ti = config.toolbar.items ;
40844 delete config.toolbar.items ;
40848 this.toolbar.render(tool_el);
40849 for(var i =0;i < ti.length;i++) {
40850 // Roo.log(['add child', items[i]]);
40851 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40853 this.toolbar.items = nitems;
40855 delete config.toolbar;
40858 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40859 config.grid.scrollBody = true;;
40860 config.grid.monitorWindowResize = false; // turn off autosizing
40861 config.grid.autoHeight = false;
40862 config.grid.autoWidth = false;
40864 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40866 if (config.background) {
40867 // render grid on panel activation (if panel background)
40868 this.on('activate', function(gp) {
40869 if (!gp.grid.rendered) {
40870 gp.grid.render(this.wrapper);
40871 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40876 this.grid.render(this.wrapper);
40877 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40880 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40881 // ??? needed ??? config.el = this.wrapper;
40886 // xtype created footer. - not sure if will work as we normally have to render first..
40887 if (this.footer && !this.footer.el && this.footer.xtype) {
40889 var ctr = this.grid.getView().getFooterPanel(true);
40890 this.footer.dataSource = this.grid.dataSource;
40891 this.footer = Roo.factory(this.footer, Roo);
40892 this.footer.render(ctr);
40902 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40903 getId : function(){
40904 return this.grid.id;
40908 * Returns the grid for this panel
40909 * @return {Roo.bootstrap.Table}
40911 getGrid : function(){
40915 setSize : function(width, height){
40916 if(!this.ignoreResize(width, height)){
40917 var grid = this.grid;
40918 var size = this.adjustForComponents(width, height);
40919 // tfoot is not a footer?
40922 var gridel = grid.getGridEl();
40923 gridel.setSize(size.width, size.height);
40925 var tbd = grid.getGridEl().select('tbody', true).first();
40926 var thd = grid.getGridEl().select('thead',true).first();
40927 var tbf= grid.getGridEl().select('tfoot', true).first();
40930 size.height -= tbf.getHeight();
40933 size.height -= thd.getHeight();
40936 tbd.setSize(size.width, size.height );
40937 // this is for the account management tab -seems to work there.
40938 var thd = grid.getGridEl().select('thead',true).first();
40940 // tbd.setSize(size.width, size.height - thd.getHeight());
40949 beforeSlide : function(){
40950 this.grid.getView().scroller.clip();
40953 afterSlide : function(){
40954 this.grid.getView().scroller.unclip();
40957 destroy : function(){
40958 this.grid.destroy();
40960 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40965 * @class Roo.bootstrap.panel.Nest
40966 * @extends Roo.bootstrap.panel.Content
40968 * Create a new Panel, that can contain a layout.Border.
40971 * @param {Roo.BorderLayout} layout The layout for this panel
40972 * @param {String/Object} config A string to set only the title or a config object
40974 Roo.bootstrap.panel.Nest = function(config)
40976 // construct with only one argument..
40977 /* FIXME - implement nicer consturctors
40978 if (layout.layout) {
40980 layout = config.layout;
40981 delete config.layout;
40983 if (layout.xtype && !layout.getEl) {
40984 // then layout needs constructing..
40985 layout = Roo.factory(layout, Roo);
40989 config.el = config.layout.getEl();
40991 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40993 config.layout.monitorWindowResize = false; // turn off autosizing
40994 this.layout = config.layout;
40995 this.layout.getEl().addClass("roo-layout-nested-layout");
40996 this.layout.parent = this;
41003 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41005 setSize : function(width, height){
41006 if(!this.ignoreResize(width, height)){
41007 var size = this.adjustForComponents(width, height);
41008 var el = this.layout.getEl();
41009 if (size.height < 1) {
41010 el.setWidth(size.width);
41012 el.setSize(size.width, size.height);
41014 var touch = el.dom.offsetWidth;
41015 this.layout.layout();
41016 // ie requires a double layout on the first pass
41017 if(Roo.isIE && !this.initialized){
41018 this.initialized = true;
41019 this.layout.layout();
41024 // activate all subpanels if not currently active..
41026 setActiveState : function(active){
41027 this.active = active;
41028 this.setActiveClass(active);
41031 this.fireEvent("deactivate", this);
41035 this.fireEvent("activate", this);
41036 // not sure if this should happen before or after..
41037 if (!this.layout) {
41038 return; // should not happen..
41041 for (var r in this.layout.regions) {
41042 reg = this.layout.getRegion(r);
41043 if (reg.getActivePanel()) {
41044 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41045 reg.setActivePanel(reg.getActivePanel());
41048 if (!reg.panels.length) {
41051 reg.showPanel(reg.getPanel(0));
41060 * Returns the nested BorderLayout for this panel
41061 * @return {Roo.BorderLayout}
41063 getLayout : function(){
41064 return this.layout;
41068 * Adds a xtype elements to the layout of the nested panel
41072 xtype : 'ContentPanel',
41079 xtype : 'NestedLayoutPanel',
41085 items : [ ... list of content panels or nested layout panels.. ]
41089 * @param {Object} cfg Xtype definition of item to add.
41091 addxtype : function(cfg) {
41092 return this.layout.addxtype(cfg);
41097 * Ext JS Library 1.1.1
41098 * Copyright(c) 2006-2007, Ext JS, LLC.
41100 * Originally Released Under LGPL - original licence link has changed is not relivant.
41103 * <script type="text/javascript">
41106 * @class Roo.TabPanel
41107 * @extends Roo.util.Observable
41108 * A lightweight tab container.
41112 // basic tabs 1, built from existing content
41113 var tabs = new Roo.TabPanel("tabs1");
41114 tabs.addTab("script", "View Script");
41115 tabs.addTab("markup", "View Markup");
41116 tabs.activate("script");
41118 // more advanced tabs, built from javascript
41119 var jtabs = new Roo.TabPanel("jtabs");
41120 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41122 // set up the UpdateManager
41123 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41124 var updater = tab2.getUpdateManager();
41125 updater.setDefaultUrl("ajax1.htm");
41126 tab2.on('activate', updater.refresh, updater, true);
41128 // Use setUrl for Ajax loading
41129 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41130 tab3.setUrl("ajax2.htm", null, true);
41133 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41136 jtabs.activate("jtabs-1");
41139 * Create a new TabPanel.
41140 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41141 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41143 Roo.bootstrap.panel.Tabs = function(config){
41145 * The container element for this TabPanel.
41146 * @type Roo.Element
41148 this.el = Roo.get(config.el);
41151 if(typeof config == "boolean"){
41152 this.tabPosition = config ? "bottom" : "top";
41154 Roo.apply(this, config);
41158 if(this.tabPosition == "bottom"){
41159 // if tabs are at the bottom = create the body first.
41160 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41161 this.el.addClass("roo-tabs-bottom");
41163 // next create the tabs holders
41165 if (this.tabPosition == "west"){
41167 var reg = this.region; // fake it..
41169 if (!reg.mgr.parent) {
41172 reg = reg.mgr.parent.region;
41174 Roo.log("got nest?");
41176 if (reg.mgr.getRegion('west')) {
41177 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41178 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41179 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41180 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41181 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41189 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41190 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41191 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41192 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41197 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41200 // finally - if tabs are at the top, then create the body last..
41201 if(this.tabPosition != "bottom"){
41202 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41203 * @type Roo.Element
41205 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41206 this.el.addClass("roo-tabs-top");
41210 this.bodyEl.setStyle("position", "relative");
41212 this.active = null;
41213 this.activateDelegate = this.activate.createDelegate(this);
41218 * Fires when the active tab changes
41219 * @param {Roo.TabPanel} this
41220 * @param {Roo.TabPanelItem} activePanel The new active tab
41224 * @event beforetabchange
41225 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41226 * @param {Roo.TabPanel} this
41227 * @param {Object} e Set cancel to true on this object to cancel the tab change
41228 * @param {Roo.TabPanelItem} tab The tab being changed to
41230 "beforetabchange" : true
41233 Roo.EventManager.onWindowResize(this.onResize, this);
41234 this.cpad = this.el.getPadding("lr");
41235 this.hiddenCount = 0;
41238 // toolbar on the tabbar support...
41239 if (this.toolbar) {
41240 alert("no toolbar support yet");
41241 this.toolbar = false;
41243 var tcfg = this.toolbar;
41244 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41245 this.toolbar = new Roo.Toolbar(tcfg);
41246 if (Roo.isSafari) {
41247 var tbl = tcfg.container.child('table', true);
41248 tbl.setAttribute('width', '100%');
41256 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41259 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41261 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41263 tabPosition : "top",
41265 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41267 currentTabWidth : 0,
41269 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41273 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41277 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41279 preferredTabWidth : 175,
41281 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41283 resizeTabs : false,
41285 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41287 monitorResize : true,
41289 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41291 toolbar : false, // set by caller..
41293 region : false, /// set by caller
41295 disableTooltips : true, // not used yet...
41298 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41299 * @param {String} id The id of the div to use <b>or create</b>
41300 * @param {String} text The text for the tab
41301 * @param {String} content (optional) Content to put in the TabPanelItem body
41302 * @param {Boolean} closable (optional) True to create a close icon on the tab
41303 * @return {Roo.TabPanelItem} The created TabPanelItem
41305 addTab : function(id, text, content, closable, tpl)
41307 var item = new Roo.bootstrap.panel.TabItem({
41311 closable : closable,
41314 this.addTabItem(item);
41316 item.setContent(content);
41322 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41323 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41324 * @return {Roo.TabPanelItem}
41326 getTab : function(id){
41327 return this.items[id];
41331 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41332 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41334 hideTab : function(id){
41335 var t = this.items[id];
41338 this.hiddenCount++;
41339 this.autoSizeTabs();
41344 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41345 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41347 unhideTab : function(id){
41348 var t = this.items[id];
41350 t.setHidden(false);
41351 this.hiddenCount--;
41352 this.autoSizeTabs();
41357 * Adds an existing {@link Roo.TabPanelItem}.
41358 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41360 addTabItem : function(item)
41362 this.items[item.id] = item;
41363 this.items.push(item);
41364 this.autoSizeTabs();
41365 // if(this.resizeTabs){
41366 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41367 // this.autoSizeTabs();
41369 // item.autoSize();
41374 * Removes a {@link Roo.TabPanelItem}.
41375 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41377 removeTab : function(id){
41378 var items = this.items;
41379 var tab = items[id];
41380 if(!tab) { return; }
41381 var index = items.indexOf(tab);
41382 if(this.active == tab && items.length > 1){
41383 var newTab = this.getNextAvailable(index);
41388 this.stripEl.dom.removeChild(tab.pnode.dom);
41389 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41390 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41392 items.splice(index, 1);
41393 delete this.items[tab.id];
41394 tab.fireEvent("close", tab);
41395 tab.purgeListeners();
41396 this.autoSizeTabs();
41399 getNextAvailable : function(start){
41400 var items = this.items;
41402 // look for a next tab that will slide over to
41403 // replace the one being removed
41404 while(index < items.length){
41405 var item = items[++index];
41406 if(item && !item.isHidden()){
41410 // if one isn't found select the previous tab (on the left)
41413 var item = items[--index];
41414 if(item && !item.isHidden()){
41422 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41423 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41425 disableTab : function(id){
41426 var tab = this.items[id];
41427 if(tab && this.active != tab){
41433 * Enables a {@link Roo.TabPanelItem} that is disabled.
41434 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41436 enableTab : function(id){
41437 var tab = this.items[id];
41442 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41443 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41444 * @return {Roo.TabPanelItem} The TabPanelItem.
41446 activate : function(id)
41448 //Roo.log('activite:' + id);
41450 var tab = this.items[id];
41454 if(tab == this.active || tab.disabled){
41458 this.fireEvent("beforetabchange", this, e, tab);
41459 if(e.cancel !== true && !tab.disabled){
41461 this.active.hide();
41463 this.active = this.items[id];
41464 this.active.show();
41465 this.fireEvent("tabchange", this, this.active);
41471 * Gets the active {@link Roo.TabPanelItem}.
41472 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41474 getActiveTab : function(){
41475 return this.active;
41479 * Updates the tab body element to fit the height of the container element
41480 * for overflow scrolling
41481 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41483 syncHeight : function(targetHeight){
41484 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41485 var bm = this.bodyEl.getMargins();
41486 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41487 this.bodyEl.setHeight(newHeight);
41491 onResize : function(){
41492 if(this.monitorResize){
41493 this.autoSizeTabs();
41498 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41500 beginUpdate : function(){
41501 this.updating = true;
41505 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41507 endUpdate : function(){
41508 this.updating = false;
41509 this.autoSizeTabs();
41513 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41515 autoSizeTabs : function()
41517 var count = this.items.length;
41518 var vcount = count - this.hiddenCount;
41521 this.stripEl.hide();
41523 this.stripEl.show();
41526 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41531 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41532 var availWidth = Math.floor(w / vcount);
41533 var b = this.stripBody;
41534 if(b.getWidth() > w){
41535 var tabs = this.items;
41536 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41537 if(availWidth < this.minTabWidth){
41538 /*if(!this.sleft){ // incomplete scrolling code
41539 this.createScrollButtons();
41542 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41545 if(this.currentTabWidth < this.preferredTabWidth){
41546 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41552 * Returns the number of tabs in this TabPanel.
41555 getCount : function(){
41556 return this.items.length;
41560 * Resizes all the tabs to the passed width
41561 * @param {Number} The new width
41563 setTabWidth : function(width){
41564 this.currentTabWidth = width;
41565 for(var i = 0, len = this.items.length; i < len; i++) {
41566 if(!this.items[i].isHidden()) {
41567 this.items[i].setWidth(width);
41573 * Destroys this TabPanel
41574 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41576 destroy : function(removeEl){
41577 Roo.EventManager.removeResizeListener(this.onResize, this);
41578 for(var i = 0, len = this.items.length; i < len; i++){
41579 this.items[i].purgeListeners();
41581 if(removeEl === true){
41582 this.el.update("");
41587 createStrip : function(container)
41589 var strip = document.createElement("nav");
41590 strip.className = Roo.bootstrap.version == 4 ?
41591 "navbar-light bg-light" :
41592 "navbar navbar-default"; //"x-tabs-wrap";
41593 container.appendChild(strip);
41597 createStripList : function(strip)
41599 // div wrapper for retard IE
41600 // returns the "tr" element.
41601 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41602 //'<div class="x-tabs-strip-wrap">'+
41603 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41604 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41605 return strip.firstChild; //.firstChild.firstChild.firstChild;
41607 createBody : function(container)
41609 var body = document.createElement("div");
41610 Roo.id(body, "tab-body");
41611 //Roo.fly(body).addClass("x-tabs-body");
41612 Roo.fly(body).addClass("tab-content");
41613 container.appendChild(body);
41616 createItemBody :function(bodyEl, id){
41617 var body = Roo.getDom(id);
41619 body = document.createElement("div");
41622 //Roo.fly(body).addClass("x-tabs-item-body");
41623 Roo.fly(body).addClass("tab-pane");
41624 bodyEl.insertBefore(body, bodyEl.firstChild);
41628 createStripElements : function(stripEl, text, closable, tpl)
41630 var td = document.createElement("li"); // was td..
41631 td.className = 'nav-item';
41633 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41636 stripEl.appendChild(td);
41638 td.className = "x-tabs-closable";
41639 if(!this.closeTpl){
41640 this.closeTpl = new Roo.Template(
41641 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41642 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41643 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41646 var el = this.closeTpl.overwrite(td, {"text": text});
41647 var close = el.getElementsByTagName("div")[0];
41648 var inner = el.getElementsByTagName("em")[0];
41649 return {"el": el, "close": close, "inner": inner};
41652 // not sure what this is..
41653 // if(!this.tabTpl){
41654 //this.tabTpl = new Roo.Template(
41655 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41656 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41658 // this.tabTpl = new Roo.Template(
41659 // '<a href="#">' +
41660 // '<span unselectable="on"' +
41661 // (this.disableTooltips ? '' : ' title="{text}"') +
41662 // ' >{text}</span></a>'
41668 var template = tpl || this.tabTpl || false;
41671 template = new Roo.Template(
41672 Roo.bootstrap.version == 4 ?
41674 '<a class="nav-link" href="#" unselectable="on"' +
41675 (this.disableTooltips ? '' : ' title="{text}"') +
41678 '<a class="nav-link" href="#">' +
41679 '<span unselectable="on"' +
41680 (this.disableTooltips ? '' : ' title="{text}"') +
41681 ' >{text}</span></a>'
41686 switch (typeof(template)) {
41690 template = new Roo.Template(template);
41696 var el = template.overwrite(td, {"text": text});
41698 var inner = el.getElementsByTagName("span")[0];
41700 return {"el": el, "inner": inner};
41708 * @class Roo.TabPanelItem
41709 * @extends Roo.util.Observable
41710 * Represents an individual item (tab plus body) in a TabPanel.
41711 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41712 * @param {String} id The id of this TabPanelItem
41713 * @param {String} text The text for the tab of this TabPanelItem
41714 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41716 Roo.bootstrap.panel.TabItem = function(config){
41718 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41719 * @type Roo.TabPanel
41721 this.tabPanel = config.panel;
41723 * The id for this TabPanelItem
41726 this.id = config.id;
41728 this.disabled = false;
41730 this.text = config.text;
41732 this.loaded = false;
41733 this.closable = config.closable;
41736 * The body element for this TabPanelItem.
41737 * @type Roo.Element
41739 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41740 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41741 this.bodyEl.setStyle("display", "block");
41742 this.bodyEl.setStyle("zoom", "1");
41743 //this.hideAction();
41745 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41747 this.el = Roo.get(els.el);
41748 this.inner = Roo.get(els.inner, true);
41749 this.textEl = Roo.bootstrap.version == 4 ?
41750 this.el : Roo.get(this.el.dom.firstChild, true);
41752 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41753 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41756 // this.el.on("mousedown", this.onTabMouseDown, this);
41757 this.el.on("click", this.onTabClick, this);
41759 if(config.closable){
41760 var c = Roo.get(els.close, true);
41761 c.dom.title = this.closeText;
41762 c.addClassOnOver("close-over");
41763 c.on("click", this.closeClick, this);
41769 * Fires when this tab becomes the active tab.
41770 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41771 * @param {Roo.TabPanelItem} this
41775 * @event beforeclose
41776 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41777 * @param {Roo.TabPanelItem} this
41778 * @param {Object} e Set cancel to true on this object to cancel the close.
41780 "beforeclose": true,
41783 * Fires when this tab is closed.
41784 * @param {Roo.TabPanelItem} this
41788 * @event deactivate
41789 * Fires when this tab is no longer the active tab.
41790 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41791 * @param {Roo.TabPanelItem} this
41793 "deactivate" : true
41795 this.hidden = false;
41797 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41800 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41802 purgeListeners : function(){
41803 Roo.util.Observable.prototype.purgeListeners.call(this);
41804 this.el.removeAllListeners();
41807 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41810 this.status_node.addClass("active");
41813 this.tabPanel.stripWrap.repaint();
41815 this.fireEvent("activate", this.tabPanel, this);
41819 * Returns true if this tab is the active tab.
41820 * @return {Boolean}
41822 isActive : function(){
41823 return this.tabPanel.getActiveTab() == this;
41827 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41830 this.status_node.removeClass("active");
41832 this.fireEvent("deactivate", this.tabPanel, this);
41835 hideAction : function(){
41836 this.bodyEl.hide();
41837 this.bodyEl.setStyle("position", "absolute");
41838 this.bodyEl.setLeft("-20000px");
41839 this.bodyEl.setTop("-20000px");
41842 showAction : function(){
41843 this.bodyEl.setStyle("position", "relative");
41844 this.bodyEl.setTop("");
41845 this.bodyEl.setLeft("");
41846 this.bodyEl.show();
41850 * Set the tooltip for the tab.
41851 * @param {String} tooltip The tab's tooltip
41853 setTooltip : function(text){
41854 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41855 this.textEl.dom.qtip = text;
41856 this.textEl.dom.removeAttribute('title');
41858 this.textEl.dom.title = text;
41862 onTabClick : function(e){
41863 e.preventDefault();
41864 this.tabPanel.activate(this.id);
41867 onTabMouseDown : function(e){
41868 e.preventDefault();
41869 this.tabPanel.activate(this.id);
41872 getWidth : function(){
41873 return this.inner.getWidth();
41876 setWidth : function(width){
41877 var iwidth = width - this.linode.getPadding("lr");
41878 this.inner.setWidth(iwidth);
41879 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41880 this.linode.setWidth(width);
41884 * Show or hide the tab
41885 * @param {Boolean} hidden True to hide or false to show.
41887 setHidden : function(hidden){
41888 this.hidden = hidden;
41889 this.linode.setStyle("display", hidden ? "none" : "");
41893 * Returns true if this tab is "hidden"
41894 * @return {Boolean}
41896 isHidden : function(){
41897 return this.hidden;
41901 * Returns the text for this tab
41904 getText : function(){
41908 autoSize : function(){
41909 //this.el.beginMeasure();
41910 this.textEl.setWidth(1);
41912 * #2804 [new] Tabs in Roojs
41913 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41915 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41916 //this.el.endMeasure();
41920 * Sets the text for the tab (Note: this also sets the tooltip text)
41921 * @param {String} text The tab's text and tooltip
41923 setText : function(text){
41925 this.textEl.update(text);
41926 this.setTooltip(text);
41927 //if(!this.tabPanel.resizeTabs){
41928 // this.autoSize();
41932 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41934 activate : function(){
41935 this.tabPanel.activate(this.id);
41939 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41941 disable : function(){
41942 if(this.tabPanel.active != this){
41943 this.disabled = true;
41944 this.status_node.addClass("disabled");
41949 * Enables this TabPanelItem if it was previously disabled.
41951 enable : function(){
41952 this.disabled = false;
41953 this.status_node.removeClass("disabled");
41957 * Sets the content for this TabPanelItem.
41958 * @param {String} content The content
41959 * @param {Boolean} loadScripts true to look for and load scripts
41961 setContent : function(content, loadScripts){
41962 this.bodyEl.update(content, loadScripts);
41966 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41967 * @return {Roo.UpdateManager} The UpdateManager
41969 getUpdateManager : function(){
41970 return this.bodyEl.getUpdateManager();
41974 * Set a URL to be used to load the content for this TabPanelItem.
41975 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41976 * @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)
41977 * @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)
41978 * @return {Roo.UpdateManager} The UpdateManager
41980 setUrl : function(url, params, loadOnce){
41981 if(this.refreshDelegate){
41982 this.un('activate', this.refreshDelegate);
41984 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41985 this.on("activate", this.refreshDelegate);
41986 return this.bodyEl.getUpdateManager();
41990 _handleRefresh : function(url, params, loadOnce){
41991 if(!loadOnce || !this.loaded){
41992 var updater = this.bodyEl.getUpdateManager();
41993 updater.update(url, params, this._setLoaded.createDelegate(this));
41998 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41999 * Will fail silently if the setUrl method has not been called.
42000 * This does not activate the panel, just updates its content.
42002 refresh : function(){
42003 if(this.refreshDelegate){
42004 this.loaded = false;
42005 this.refreshDelegate();
42010 _setLoaded : function(){
42011 this.loaded = true;
42015 closeClick : function(e){
42018 this.fireEvent("beforeclose", this, o);
42019 if(o.cancel !== true){
42020 this.tabPanel.removeTab(this.id);
42024 * The text displayed in the tooltip for the close icon.
42027 closeText : "Close this tab"
42030 * This script refer to:
42031 * Title: International Telephone Input
42032 * Author: Jack O'Connor
42033 * Code version: v12.1.12
42034 * Availability: https://github.com/jackocnr/intl-tel-input.git
42037 Roo.bootstrap.PhoneInputData = function() {
42040 "Afghanistan (افغانستان)",
42045 "Albania (Shqipëri)",
42050 "Algeria (الجزائر)",
42075 "Antigua and Barbuda",
42085 "Armenia (Հայաստան)",
42101 "Austria (Österreich)",
42106 "Azerbaijan (Azərbaycan)",
42116 "Bahrain (البحرين)",
42121 "Bangladesh (বাংলাদেশ)",
42131 "Belarus (Беларусь)",
42136 "Belgium (België)",
42166 "Bosnia and Herzegovina (Босна и Херцеговина)",
42181 "British Indian Ocean Territory",
42186 "British Virgin Islands",
42196 "Bulgaria (България)",
42206 "Burundi (Uburundi)",
42211 "Cambodia (កម្ពុជា)",
42216 "Cameroon (Cameroun)",
42225 ["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"]
42228 "Cape Verde (Kabu Verdi)",
42233 "Caribbean Netherlands",
42244 "Central African Republic (République centrafricaine)",
42264 "Christmas Island",
42270 "Cocos (Keeling) Islands",
42281 "Comoros (جزر القمر)",
42286 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42291 "Congo (Republic) (Congo-Brazzaville)",
42311 "Croatia (Hrvatska)",
42332 "Czech Republic (Česká republika)",
42337 "Denmark (Danmark)",
42352 "Dominican Republic (República Dominicana)",
42356 ["809", "829", "849"]
42374 "Equatorial Guinea (Guinea Ecuatorial)",
42394 "Falkland Islands (Islas Malvinas)",
42399 "Faroe Islands (Føroyar)",
42420 "French Guiana (Guyane française)",
42425 "French Polynesia (Polynésie française)",
42440 "Georgia (საქართველო)",
42445 "Germany (Deutschland)",
42465 "Greenland (Kalaallit Nunaat)",
42502 "Guinea-Bissau (Guiné Bissau)",
42527 "Hungary (Magyarország)",
42532 "Iceland (Ísland)",
42552 "Iraq (العراق)",
42568 "Israel (ישראל)",
42595 "Jordan (الأردن)",
42600 "Kazakhstan (Казахстан)",
42621 "Kuwait (الكويت)",
42626 "Kyrgyzstan (Кыргызстан)",
42636 "Latvia (Latvija)",
42641 "Lebanon (لبنان)",
42656 "Libya (ليبيا)",
42666 "Lithuania (Lietuva)",
42681 "Macedonia (FYROM) (Македонија)",
42686 "Madagascar (Madagasikara)",
42716 "Marshall Islands",
42726 "Mauritania (موريتانيا)",
42731 "Mauritius (Moris)",
42752 "Moldova (Republica Moldova)",
42762 "Mongolia (Монгол)",
42767 "Montenegro (Crna Gora)",
42777 "Morocco (المغرب)",
42783 "Mozambique (Moçambique)",
42788 "Myanmar (Burma) (မြန်မာ)",
42793 "Namibia (Namibië)",
42808 "Netherlands (Nederland)",
42813 "New Caledonia (Nouvelle-Calédonie)",
42848 "North Korea (조선 민주주의 인민 공화국)",
42853 "Northern Mariana Islands",
42869 "Pakistan (پاکستان)",
42879 "Palestine (فلسطين)",
42889 "Papua New Guinea",
42931 "Réunion (La Réunion)",
42937 "Romania (România)",
42953 "Saint Barthélemy",
42964 "Saint Kitts and Nevis",
42974 "Saint Martin (Saint-Martin (partie française))",
42980 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42985 "Saint Vincent and the Grenadines",
43000 "São Tomé and Príncipe (São Tomé e Príncipe)",
43005 "Saudi Arabia (المملكة العربية السعودية)",
43010 "Senegal (Sénégal)",
43040 "Slovakia (Slovensko)",
43045 "Slovenia (Slovenija)",
43055 "Somalia (Soomaaliya)",
43065 "South Korea (대한민국)",
43070 "South Sudan (جنوب السودان)",
43080 "Sri Lanka (ශ්රී ලංකාව)",
43085 "Sudan (السودان)",
43095 "Svalbard and Jan Mayen",
43106 "Sweden (Sverige)",
43111 "Switzerland (Schweiz)",
43116 "Syria (سوريا)",
43161 "Trinidad and Tobago",
43166 "Tunisia (تونس)",
43171 "Turkey (Türkiye)",
43181 "Turks and Caicos Islands",
43191 "U.S. Virgin Islands",
43201 "Ukraine (Україна)",
43206 "United Arab Emirates (الإمارات العربية المتحدة)",
43228 "Uzbekistan (Oʻzbekiston)",
43238 "Vatican City (Città del Vaticano)",
43249 "Vietnam (Việt Nam)",
43254 "Wallis and Futuna (Wallis-et-Futuna)",
43259 "Western Sahara (الصحراء الغربية)",
43265 "Yemen (اليمن)",
43289 * This script refer to:
43290 * Title: International Telephone Input
43291 * Author: Jack O'Connor
43292 * Code version: v12.1.12
43293 * Availability: https://github.com/jackocnr/intl-tel-input.git
43297 * @class Roo.bootstrap.PhoneInput
43298 * @extends Roo.bootstrap.TriggerField
43299 * An input with International dial-code selection
43301 * @cfg {String} defaultDialCode default '+852'
43302 * @cfg {Array} preferedCountries default []
43305 * Create a new PhoneInput.
43306 * @param {Object} config Configuration options
43309 Roo.bootstrap.PhoneInput = function(config) {
43310 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43313 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43315 listWidth: undefined,
43317 selectedClass: 'active',
43319 invalidClass : "has-warning",
43321 validClass: 'has-success',
43323 allowed: '0123456789',
43328 * @cfg {String} defaultDialCode The default dial code when initializing the input
43330 defaultDialCode: '+852',
43333 * @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
43335 preferedCountries: false,
43337 getAutoCreate : function()
43339 var data = Roo.bootstrap.PhoneInputData();
43340 var align = this.labelAlign || this.parentLabelAlign();
43343 this.allCountries = [];
43344 this.dialCodeMapping = [];
43346 for (var i = 0; i < data.length; i++) {
43348 this.allCountries[i] = {
43352 priority: c[3] || 0,
43353 areaCodes: c[4] || null
43355 this.dialCodeMapping[c[2]] = {
43358 priority: c[3] || 0,
43359 areaCodes: c[4] || null
43371 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43372 maxlength: this.max_length,
43373 cls : 'form-control tel-input',
43374 autocomplete: 'new-password'
43377 var hiddenInput = {
43380 cls: 'hidden-tel-input'
43384 hiddenInput.name = this.name;
43387 if (this.disabled) {
43388 input.disabled = true;
43391 var flag_container = {
43408 cls: this.hasFeedback ? 'has-feedback' : '',
43414 cls: 'dial-code-holder',
43421 cls: 'roo-select2-container input-group',
43428 if (this.fieldLabel.length) {
43431 tooltip: 'This field is required'
43437 cls: 'control-label',
43443 html: this.fieldLabel
43446 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43452 if(this.indicatorpos == 'right') {
43453 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43460 if(align == 'left') {
43468 if(this.labelWidth > 12){
43469 label.style = "width: " + this.labelWidth + 'px';
43471 if(this.labelWidth < 13 && this.labelmd == 0){
43472 this.labelmd = this.labelWidth;
43474 if(this.labellg > 0){
43475 label.cls += ' col-lg-' + this.labellg;
43476 input.cls += ' col-lg-' + (12 - this.labellg);
43478 if(this.labelmd > 0){
43479 label.cls += ' col-md-' + this.labelmd;
43480 container.cls += ' col-md-' + (12 - this.labelmd);
43482 if(this.labelsm > 0){
43483 label.cls += ' col-sm-' + this.labelsm;
43484 container.cls += ' col-sm-' + (12 - this.labelsm);
43486 if(this.labelxs > 0){
43487 label.cls += ' col-xs-' + this.labelxs;
43488 container.cls += ' col-xs-' + (12 - this.labelxs);
43498 var settings = this;
43500 ['xs','sm','md','lg'].map(function(size){
43501 if (settings[size]) {
43502 cfg.cls += ' col-' + size + '-' + settings[size];
43506 this.store = new Roo.data.Store({
43507 proxy : new Roo.data.MemoryProxy({}),
43508 reader : new Roo.data.JsonReader({
43519 'name' : 'dialCode',
43523 'name' : 'priority',
43527 'name' : 'areaCodes',
43534 if(!this.preferedCountries) {
43535 this.preferedCountries = [
43542 var p = this.preferedCountries.reverse();
43545 for (var i = 0; i < p.length; i++) {
43546 for (var j = 0; j < this.allCountries.length; j++) {
43547 if(this.allCountries[j].iso2 == p[i]) {
43548 var t = this.allCountries[j];
43549 this.allCountries.splice(j,1);
43550 this.allCountries.unshift(t);
43556 this.store.proxy.data = {
43558 data: this.allCountries
43564 initEvents : function()
43567 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43569 this.indicator = this.indicatorEl();
43570 this.flag = this.flagEl();
43571 this.dialCodeHolder = this.dialCodeHolderEl();
43573 this.trigger = this.el.select('div.flag-box',true).first();
43574 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43579 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43580 _this.list.setWidth(lw);
43583 this.list.on('mouseover', this.onViewOver, this);
43584 this.list.on('mousemove', this.onViewMove, this);
43585 this.inputEl().on("keyup", this.onKeyUp, this);
43586 this.inputEl().on("keypress", this.onKeyPress, this);
43588 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43590 this.view = new Roo.View(this.list, this.tpl, {
43591 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43594 this.view.on('click', this.onViewClick, this);
43595 this.setValue(this.defaultDialCode);
43598 onTriggerClick : function(e)
43600 Roo.log('trigger click');
43605 if(this.isExpanded()){
43607 this.hasFocus = false;
43609 this.store.load({});
43610 this.hasFocus = true;
43615 isExpanded : function()
43617 return this.list.isVisible();
43620 collapse : function()
43622 if(!this.isExpanded()){
43626 Roo.get(document).un('mousedown', this.collapseIf, this);
43627 Roo.get(document).un('mousewheel', this.collapseIf, this);
43628 this.fireEvent('collapse', this);
43632 expand : function()
43636 if(this.isExpanded() || !this.hasFocus){
43640 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43641 this.list.setWidth(lw);
43644 this.restrictHeight();
43646 Roo.get(document).on('mousedown', this.collapseIf, this);
43647 Roo.get(document).on('mousewheel', this.collapseIf, this);
43649 this.fireEvent('expand', this);
43652 restrictHeight : function()
43654 this.list.alignTo(this.inputEl(), this.listAlign);
43655 this.list.alignTo(this.inputEl(), this.listAlign);
43658 onViewOver : function(e, t)
43660 if(this.inKeyMode){
43663 var item = this.view.findItemFromChild(t);
43666 var index = this.view.indexOf(item);
43667 this.select(index, false);
43672 onViewClick : function(view, doFocus, el, e)
43674 var index = this.view.getSelectedIndexes()[0];
43676 var r = this.store.getAt(index);
43679 this.onSelect(r, index);
43681 if(doFocus !== false && !this.blockFocus){
43682 this.inputEl().focus();
43686 onViewMove : function(e, t)
43688 this.inKeyMode = false;
43691 select : function(index, scrollIntoView)
43693 this.selectedIndex = index;
43694 this.view.select(index);
43695 if(scrollIntoView !== false){
43696 var el = this.view.getNode(index);
43698 this.list.scrollChildIntoView(el, false);
43703 createList : function()
43705 this.list = Roo.get(document.body).createChild({
43707 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43708 style: 'display:none'
43711 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43714 collapseIf : function(e)
43716 var in_combo = e.within(this.el);
43717 var in_list = e.within(this.list);
43718 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43720 if (in_combo || in_list || is_list) {
43726 onSelect : function(record, index)
43728 if(this.fireEvent('beforeselect', this, record, index) !== false){
43730 this.setFlagClass(record.data.iso2);
43731 this.setDialCode(record.data.dialCode);
43732 this.hasFocus = false;
43734 this.fireEvent('select', this, record, index);
43738 flagEl : function()
43740 var flag = this.el.select('div.flag',true).first();
43747 dialCodeHolderEl : function()
43749 var d = this.el.select('input.dial-code-holder',true).first();
43756 setDialCode : function(v)
43758 this.dialCodeHolder.dom.value = '+'+v;
43761 setFlagClass : function(n)
43763 this.flag.dom.className = 'flag '+n;
43766 getValue : function()
43768 var v = this.inputEl().getValue();
43769 if(this.dialCodeHolder) {
43770 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43775 setValue : function(v)
43777 var d = this.getDialCode(v);
43779 //invalid dial code
43780 if(v.length == 0 || !d || d.length == 0) {
43782 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43783 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43789 this.setFlagClass(this.dialCodeMapping[d].iso2);
43790 this.setDialCode(d);
43791 this.inputEl().dom.value = v.replace('+'+d,'');
43792 this.hiddenEl().dom.value = this.getValue();
43797 getDialCode : function(v)
43801 if (v.length == 0) {
43802 return this.dialCodeHolder.dom.value;
43806 if (v.charAt(0) != "+") {
43809 var numericChars = "";
43810 for (var i = 1; i < v.length; i++) {
43811 var c = v.charAt(i);
43814 if (this.dialCodeMapping[numericChars]) {
43815 dialCode = v.substr(1, i);
43817 if (numericChars.length == 4) {
43827 this.setValue(this.defaultDialCode);
43831 hiddenEl : function()
43833 return this.el.select('input.hidden-tel-input',true).first();
43836 // after setting val
43837 onKeyUp : function(e){
43838 this.setValue(this.getValue());
43841 onKeyPress : function(e){
43842 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43849 * @class Roo.bootstrap.MoneyField
43850 * @extends Roo.bootstrap.ComboBox
43851 * Bootstrap MoneyField class
43854 * Create a new MoneyField.
43855 * @param {Object} config Configuration options
43858 Roo.bootstrap.MoneyField = function(config) {
43860 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43864 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43867 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43869 allowDecimals : true,
43871 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43873 decimalSeparator : ".",
43875 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43877 decimalPrecision : 0,
43879 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43881 allowNegative : true,
43883 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43887 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43889 minValue : Number.NEGATIVE_INFINITY,
43891 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43893 maxValue : Number.MAX_VALUE,
43895 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43897 minText : "The minimum value for this field is {0}",
43899 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43901 maxText : "The maximum value for this field is {0}",
43903 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43904 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43906 nanText : "{0} is not a valid number",
43908 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43912 * @cfg {String} defaults currency of the MoneyField
43913 * value should be in lkey
43915 defaultCurrency : false,
43917 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43919 thousandsDelimiter : false,
43921 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43932 getAutoCreate : function()
43934 var align = this.labelAlign || this.parentLabelAlign();
43946 cls : 'form-control roo-money-amount-input',
43947 autocomplete: 'new-password'
43950 var hiddenInput = {
43954 cls: 'hidden-number-input'
43957 if(this.max_length) {
43958 input.maxlength = this.max_length;
43962 hiddenInput.name = this.name;
43965 if (this.disabled) {
43966 input.disabled = true;
43969 var clg = 12 - this.inputlg;
43970 var cmd = 12 - this.inputmd;
43971 var csm = 12 - this.inputsm;
43972 var cxs = 12 - this.inputxs;
43976 cls : 'row roo-money-field',
43980 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43984 cls: 'roo-select2-container input-group',
43988 cls : 'form-control roo-money-currency-input',
43989 autocomplete: 'new-password',
43991 name : this.currencyName
43995 cls : 'input-group-addon',
44009 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44013 cls: this.hasFeedback ? 'has-feedback' : '',
44024 if (this.fieldLabel.length) {
44027 tooltip: 'This field is required'
44033 cls: 'control-label',
44039 html: this.fieldLabel
44042 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44048 if(this.indicatorpos == 'right') {
44049 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44056 if(align == 'left') {
44064 if(this.labelWidth > 12){
44065 label.style = "width: " + this.labelWidth + 'px';
44067 if(this.labelWidth < 13 && this.labelmd == 0){
44068 this.labelmd = this.labelWidth;
44070 if(this.labellg > 0){
44071 label.cls += ' col-lg-' + this.labellg;
44072 input.cls += ' col-lg-' + (12 - this.labellg);
44074 if(this.labelmd > 0){
44075 label.cls += ' col-md-' + this.labelmd;
44076 container.cls += ' col-md-' + (12 - this.labelmd);
44078 if(this.labelsm > 0){
44079 label.cls += ' col-sm-' + this.labelsm;
44080 container.cls += ' col-sm-' + (12 - this.labelsm);
44082 if(this.labelxs > 0){
44083 label.cls += ' col-xs-' + this.labelxs;
44084 container.cls += ' col-xs-' + (12 - this.labelxs);
44095 var settings = this;
44097 ['xs','sm','md','lg'].map(function(size){
44098 if (settings[size]) {
44099 cfg.cls += ' col-' + size + '-' + settings[size];
44106 initEvents : function()
44108 this.indicator = this.indicatorEl();
44110 this.initCurrencyEvent();
44112 this.initNumberEvent();
44115 initCurrencyEvent : function()
44118 throw "can not find store for combo";
44121 this.store = Roo.factory(this.store, Roo.data);
44122 this.store.parent = this;
44126 this.triggerEl = this.el.select('.input-group-addon', true).first();
44128 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44133 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44134 _this.list.setWidth(lw);
44137 this.list.on('mouseover', this.onViewOver, this);
44138 this.list.on('mousemove', this.onViewMove, this);
44139 this.list.on('scroll', this.onViewScroll, this);
44142 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44145 this.view = new Roo.View(this.list, this.tpl, {
44146 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44149 this.view.on('click', this.onViewClick, this);
44151 this.store.on('beforeload', this.onBeforeLoad, this);
44152 this.store.on('load', this.onLoad, this);
44153 this.store.on('loadexception', this.onLoadException, this);
44155 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44156 "up" : function(e){
44157 this.inKeyMode = true;
44161 "down" : function(e){
44162 if(!this.isExpanded()){
44163 this.onTriggerClick();
44165 this.inKeyMode = true;
44170 "enter" : function(e){
44173 if(this.fireEvent("specialkey", this, e)){
44174 this.onViewClick(false);
44180 "esc" : function(e){
44184 "tab" : function(e){
44187 if(this.fireEvent("specialkey", this, e)){
44188 this.onViewClick(false);
44196 doRelay : function(foo, bar, hname){
44197 if(hname == 'down' || this.scope.isExpanded()){
44198 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44206 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44210 initNumberEvent : function(e)
44212 this.inputEl().on("keydown" , this.fireKey, this);
44213 this.inputEl().on("focus", this.onFocus, this);
44214 this.inputEl().on("blur", this.onBlur, this);
44216 this.inputEl().relayEvent('keyup', this);
44218 if(this.indicator){
44219 this.indicator.addClass('invisible');
44222 this.originalValue = this.getValue();
44224 if(this.validationEvent == 'keyup'){
44225 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44226 this.inputEl().on('keyup', this.filterValidation, this);
44228 else if(this.validationEvent !== false){
44229 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44232 if(this.selectOnFocus){
44233 this.on("focus", this.preFocus, this);
44236 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44237 this.inputEl().on("keypress", this.filterKeys, this);
44239 this.inputEl().relayEvent('keypress', this);
44242 var allowed = "0123456789";
44244 if(this.allowDecimals){
44245 allowed += this.decimalSeparator;
44248 if(this.allowNegative){
44252 if(this.thousandsDelimiter) {
44256 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44258 var keyPress = function(e){
44260 var k = e.getKey();
44262 var c = e.getCharCode();
44265 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44266 allowed.indexOf(String.fromCharCode(c)) === -1
44272 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44276 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44281 this.inputEl().on("keypress", keyPress, this);
44285 onTriggerClick : function(e)
44292 this.loadNext = false;
44294 if(this.isExpanded()){
44299 this.hasFocus = true;
44301 if(this.triggerAction == 'all') {
44302 this.doQuery(this.allQuery, true);
44306 this.doQuery(this.getRawValue());
44309 getCurrency : function()
44311 var v = this.currencyEl().getValue();
44316 restrictHeight : function()
44318 this.list.alignTo(this.currencyEl(), this.listAlign);
44319 this.list.alignTo(this.currencyEl(), this.listAlign);
44322 onViewClick : function(view, doFocus, el, e)
44324 var index = this.view.getSelectedIndexes()[0];
44326 var r = this.store.getAt(index);
44329 this.onSelect(r, index);
44333 onSelect : function(record, index){
44335 if(this.fireEvent('beforeselect', this, record, index) !== false){
44337 this.setFromCurrencyData(index > -1 ? record.data : false);
44341 this.fireEvent('select', this, record, index);
44345 setFromCurrencyData : function(o)
44349 this.lastCurrency = o;
44351 if (this.currencyField) {
44352 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44354 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44357 this.lastSelectionText = currency;
44359 //setting default currency
44360 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44361 this.setCurrency(this.defaultCurrency);
44365 this.setCurrency(currency);
44368 setFromData : function(o)
44372 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44374 this.setFromCurrencyData(c);
44379 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44381 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44384 this.setValue(value);
44388 setCurrency : function(v)
44390 this.currencyValue = v;
44393 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44398 setValue : function(v)
44400 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44406 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44408 this.inputEl().dom.value = (v == '') ? '' :
44409 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44411 if(!this.allowZero && v === '0') {
44412 this.hiddenEl().dom.value = '';
44413 this.inputEl().dom.value = '';
44420 getRawValue : function()
44422 var v = this.inputEl().getValue();
44427 getValue : function()
44429 return this.fixPrecision(this.parseValue(this.getRawValue()));
44432 parseValue : function(value)
44434 if(this.thousandsDelimiter) {
44436 r = new RegExp(",", "g");
44437 value = value.replace(r, "");
44440 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44441 return isNaN(value) ? '' : value;
44445 fixPrecision : function(value)
44447 if(this.thousandsDelimiter) {
44449 r = new RegExp(",", "g");
44450 value = value.replace(r, "");
44453 var nan = isNaN(value);
44455 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44456 return nan ? '' : value;
44458 return parseFloat(value).toFixed(this.decimalPrecision);
44461 decimalPrecisionFcn : function(v)
44463 return Math.floor(v);
44466 validateValue : function(value)
44468 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44472 var num = this.parseValue(value);
44475 this.markInvalid(String.format(this.nanText, value));
44479 if(num < this.minValue){
44480 this.markInvalid(String.format(this.minText, this.minValue));
44484 if(num > this.maxValue){
44485 this.markInvalid(String.format(this.maxText, this.maxValue));
44492 validate : function()
44494 if(this.disabled || this.allowBlank){
44499 var currency = this.getCurrency();
44501 if(this.validateValue(this.getRawValue()) && currency.length){
44506 this.markInvalid();
44510 getName: function()
44515 beforeBlur : function()
44521 var v = this.parseValue(this.getRawValue());
44528 onBlur : function()
44532 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44533 //this.el.removeClass(this.focusClass);
44536 this.hasFocus = false;
44538 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44542 var v = this.getValue();
44544 if(String(v) !== String(this.startValue)){
44545 this.fireEvent('change', this, v, this.startValue);
44548 this.fireEvent("blur", this);
44551 inputEl : function()
44553 return this.el.select('.roo-money-amount-input', true).first();
44556 currencyEl : function()
44558 return this.el.select('.roo-money-currency-input', true).first();
44561 hiddenEl : function()
44563 return this.el.select('input.hidden-number-input',true).first();
44567 * @class Roo.bootstrap.BezierSignature
44568 * @extends Roo.bootstrap.Component
44569 * Bootstrap BezierSignature class
44570 * This script refer to:
44571 * Title: Signature Pad
44573 * Availability: https://github.com/szimek/signature_pad
44576 * Create a new BezierSignature
44577 * @param {Object} config The config object
44580 Roo.bootstrap.BezierSignature = function(config){
44581 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44587 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44594 mouse_btn_down: true,
44597 * @cfg {int} canvas height
44599 canvas_height: '200px',
44602 * @cfg {float|function} Radius of a single dot.
44607 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44612 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44617 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44622 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44627 * @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.
44629 bg_color: 'rgba(0, 0, 0, 0)',
44632 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44634 dot_color: 'black',
44637 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44639 velocity_filter_weight: 0.7,
44642 * @cfg {function} Callback when stroke begin.
44647 * @cfg {function} Callback when stroke end.
44651 getAutoCreate : function()
44653 var cls = 'roo-signature column';
44656 cls += ' ' + this.cls;
44666 for(var i = 0; i < col_sizes.length; i++) {
44667 if(this[col_sizes[i]]) {
44668 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44678 cls: 'roo-signature-body',
44682 cls: 'roo-signature-body-canvas',
44683 height: this.canvas_height,
44684 width: this.canvas_width
44691 style: 'display: none'
44699 initEvents: function()
44701 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44703 var canvas = this.canvasEl();
44705 // mouse && touch event swapping...
44706 canvas.dom.style.touchAction = 'none';
44707 canvas.dom.style.msTouchAction = 'none';
44709 this.mouse_btn_down = false;
44710 canvas.on('mousedown', this._handleMouseDown, this);
44711 canvas.on('mousemove', this._handleMouseMove, this);
44712 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44714 if (window.PointerEvent) {
44715 canvas.on('pointerdown', this._handleMouseDown, this);
44716 canvas.on('pointermove', this._handleMouseMove, this);
44717 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44720 if ('ontouchstart' in window) {
44721 canvas.on('touchstart', this._handleTouchStart, this);
44722 canvas.on('touchmove', this._handleTouchMove, this);
44723 canvas.on('touchend', this._handleTouchEnd, this);
44726 Roo.EventManager.onWindowResize(this.resize, this, true);
44728 // file input event
44729 this.fileEl().on('change', this.uploadImage, this);
44736 resize: function(){
44738 var canvas = this.canvasEl().dom;
44739 var ctx = this.canvasElCtx();
44740 var img_data = false;
44742 if(canvas.width > 0) {
44743 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44745 // setting canvas width will clean img data
44748 var style = window.getComputedStyle ?
44749 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44751 var padding_left = parseInt(style.paddingLeft) || 0;
44752 var padding_right = parseInt(style.paddingRight) || 0;
44754 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44757 ctx.putImageData(img_data, 0, 0);
44761 _handleMouseDown: function(e)
44763 if (e.browserEvent.which === 1) {
44764 this.mouse_btn_down = true;
44765 this.strokeBegin(e);
44769 _handleMouseMove: function (e)
44771 if (this.mouse_btn_down) {
44772 this.strokeMoveUpdate(e);
44776 _handleMouseUp: function (e)
44778 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44779 this.mouse_btn_down = false;
44784 _handleTouchStart: function (e) {
44786 e.preventDefault();
44787 if (e.browserEvent.targetTouches.length === 1) {
44788 // var touch = e.browserEvent.changedTouches[0];
44789 // this.strokeBegin(touch);
44791 this.strokeBegin(e); // assume e catching the correct xy...
44795 _handleTouchMove: function (e) {
44796 e.preventDefault();
44797 // var touch = event.targetTouches[0];
44798 // _this._strokeMoveUpdate(touch);
44799 this.strokeMoveUpdate(e);
44802 _handleTouchEnd: function (e) {
44803 var wasCanvasTouched = e.target === this.canvasEl().dom;
44804 if (wasCanvasTouched) {
44805 e.preventDefault();
44806 // var touch = event.changedTouches[0];
44807 // _this._strokeEnd(touch);
44812 reset: function () {
44813 this._lastPoints = [];
44814 this._lastVelocity = 0;
44815 this._lastWidth = (this.min_width + this.max_width) / 2;
44816 this.canvasElCtx().fillStyle = this.dot_color;
44819 strokeMoveUpdate: function(e)
44821 this.strokeUpdate(e);
44823 if (this.throttle) {
44824 this.throttleStroke(this.strokeUpdate, this.throttle);
44827 this.strokeUpdate(e);
44831 strokeBegin: function(e)
44833 var newPointGroup = {
44834 color: this.dot_color,
44838 if (typeof this.onBegin === 'function') {
44842 this.curve_data.push(newPointGroup);
44844 this.strokeUpdate(e);
44847 strokeUpdate: function(e)
44849 var rect = this.canvasEl().dom.getBoundingClientRect();
44850 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44851 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44852 var lastPoints = lastPointGroup.points;
44853 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44854 var isLastPointTooClose = lastPoint
44855 ? point.distanceTo(lastPoint) <= this.min_distance
44857 var color = lastPointGroup.color;
44858 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44859 var curve = this.addPoint(point);
44861 this.drawDot({color: color, point: point});
44864 this.drawCurve({color: color, curve: curve});
44874 strokeEnd: function(e)
44876 this.strokeUpdate(e);
44877 if (typeof this.onEnd === 'function') {
44882 addPoint: function (point) {
44883 var _lastPoints = this._lastPoints;
44884 _lastPoints.push(point);
44885 if (_lastPoints.length > 2) {
44886 if (_lastPoints.length === 3) {
44887 _lastPoints.unshift(_lastPoints[0]);
44889 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44890 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44891 _lastPoints.shift();
44897 calculateCurveWidths: function (startPoint, endPoint) {
44898 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44899 (1 - this.velocity_filter_weight) * this._lastVelocity;
44901 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44904 start: this._lastWidth
44907 this._lastVelocity = velocity;
44908 this._lastWidth = newWidth;
44912 drawDot: function (_a) {
44913 var color = _a.color, point = _a.point;
44914 var ctx = this.canvasElCtx();
44915 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44917 this.drawCurveSegment(point.x, point.y, width);
44919 ctx.fillStyle = color;
44923 drawCurve: function (_a) {
44924 var color = _a.color, curve = _a.curve;
44925 var ctx = this.canvasElCtx();
44926 var widthDelta = curve.endWidth - curve.startWidth;
44927 var drawSteps = Math.floor(curve.length()) * 2;
44929 ctx.fillStyle = color;
44930 for (var i = 0; i < drawSteps; i += 1) {
44931 var t = i / drawSteps;
44937 var x = uuu * curve.startPoint.x;
44938 x += 3 * uu * t * curve.control1.x;
44939 x += 3 * u * tt * curve.control2.x;
44940 x += ttt * curve.endPoint.x;
44941 var y = uuu * curve.startPoint.y;
44942 y += 3 * uu * t * curve.control1.y;
44943 y += 3 * u * tt * curve.control2.y;
44944 y += ttt * curve.endPoint.y;
44945 var width = curve.startWidth + ttt * widthDelta;
44946 this.drawCurveSegment(x, y, width);
44952 drawCurveSegment: function (x, y, width) {
44953 var ctx = this.canvasElCtx();
44955 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44956 this.is_empty = false;
44961 var ctx = this.canvasElCtx();
44962 var canvas = this.canvasEl().dom;
44963 ctx.fillStyle = this.bg_color;
44964 ctx.clearRect(0, 0, canvas.width, canvas.height);
44965 ctx.fillRect(0, 0, canvas.width, canvas.height);
44966 this.curve_data = [];
44968 this.is_empty = true;
44973 return this.el.select('input',true).first();
44976 canvasEl: function()
44978 return this.el.select('canvas',true).first();
44981 canvasElCtx: function()
44983 return this.el.select('canvas',true).first().dom.getContext('2d');
44986 getImage: function(type)
44988 if(this.is_empty) {
44993 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44996 drawFromImage: function(img_src)
44998 var img = new Image();
45000 img.onload = function(){
45001 this.canvasElCtx().drawImage(img, 0, 0);
45006 this.is_empty = false;
45009 selectImage: function()
45011 this.fileEl().dom.click();
45014 uploadImage: function(e)
45016 var reader = new FileReader();
45018 reader.onload = function(e){
45019 var img = new Image();
45020 img.onload = function(){
45022 this.canvasElCtx().drawImage(img, 0, 0);
45024 img.src = e.target.result;
45027 reader.readAsDataURL(e.target.files[0]);
45030 // Bezier Point Constructor
45031 Point: (function () {
45032 function Point(x, y, time) {
45035 this.time = time || Date.now();
45037 Point.prototype.distanceTo = function (start) {
45038 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45040 Point.prototype.equals = function (other) {
45041 return this.x === other.x && this.y === other.y && this.time === other.time;
45043 Point.prototype.velocityFrom = function (start) {
45044 return this.time !== start.time
45045 ? this.distanceTo(start) / (this.time - start.time)
45052 // Bezier Constructor
45053 Bezier: (function () {
45054 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45055 this.startPoint = startPoint;
45056 this.control2 = control2;
45057 this.control1 = control1;
45058 this.endPoint = endPoint;
45059 this.startWidth = startWidth;
45060 this.endWidth = endWidth;
45062 Bezier.fromPoints = function (points, widths, scope) {
45063 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45064 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45065 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45067 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45068 var dx1 = s1.x - s2.x;
45069 var dy1 = s1.y - s2.y;
45070 var dx2 = s2.x - s3.x;
45071 var dy2 = s2.y - s3.y;
45072 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45073 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45074 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45075 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45076 var dxm = m1.x - m2.x;
45077 var dym = m1.y - m2.y;
45078 var k = l2 / (l1 + l2);
45079 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45080 var tx = s2.x - cm.x;
45081 var ty = s2.y - cm.y;
45083 c1: new scope.Point(m1.x + tx, m1.y + ty),
45084 c2: new scope.Point(m2.x + tx, m2.y + ty)
45087 Bezier.prototype.length = function () {
45092 for (var i = 0; i <= steps; i += 1) {
45094 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45095 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45097 var xdiff = cx - px;
45098 var ydiff = cy - py;
45099 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45106 Bezier.prototype.point = function (t, start, c1, c2, end) {
45107 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45108 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45109 + (3.0 * c2 * (1.0 - t) * t * t)
45110 + (end * t * t * t);
45115 throttleStroke: function(fn, wait) {
45116 if (wait === void 0) { wait = 250; }
45118 var timeout = null;
45122 var later = function () {
45123 previous = Date.now();
45125 result = fn.apply(storedContext, storedArgs);
45127 storedContext = null;
45131 return function wrapper() {
45133 for (var _i = 0; _i < arguments.length; _i++) {
45134 args[_i] = arguments[_i];
45136 var now = Date.now();
45137 var remaining = wait - (now - previous);
45138 storedContext = this;
45140 if (remaining <= 0 || remaining > wait) {
45142 clearTimeout(timeout);
45146 result = fn.apply(storedContext, storedArgs);
45148 storedContext = null;
45152 else if (!timeout) {
45153 timeout = window.setTimeout(later, remaining);