2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7311 * @extends Roo.dd.DDProxy
7312 * @class Roo.grid.SplitDragZone
7313 * Support for Column Header resizing
7315 * @param {Object} config
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321 this.view = grid.getView();
7322 this.proxy = this.view.resizeProxy;
7323 Roo.grid.SplitDragZone.superclass.constructor.call(
7326 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328 dragElId : Roo.id(this.proxy.dom),
7333 this.setHandleElId(Roo.id(hd));
7334 if (hd2 !== false) {
7335 this.setOuterHandleElId(Roo.id(hd2));
7338 this.scroll = false;
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341 fly: Roo.Element.fly,
7343 b4StartDrag : function(x, y){
7344 this.view.headersDisabled = true;
7345 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348 this.proxy.setHeight(h);
7350 // for old system colWidth really stored the actual width?
7351 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352 // which in reality did not work.. - it worked only for fixed sizes
7353 // for resizable we need to use actual sizes.
7354 var w = this.cm.getColumnWidth(this.cellIndex);
7355 if (!this.view.mainWrap) {
7357 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362 // this was w-this.grid.minColumnWidth;
7363 // doesnt really make sense? - w = thie curren width or the rendered one?
7364 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365 this.resetConstraints();
7366 this.setXConstraint(minw, 1000);
7367 this.setYConstraint(0, 0);
7368 this.minX = x - minw;
7369 this.maxX = x + 1000;
7371 if (!this.view.mainWrap) { // this is Bootstrap code..
7372 this.getDragEl().style.display='block';
7375 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379 handleMouseDown : function(e){
7380 ev = Roo.EventObject.setEvent(e);
7381 var t = this.fly(ev.getTarget());
7382 if(t.hasClass("x-grid-split")){
7383 this.cellIndex = this.view.getCellIndex(t.dom);
7385 this.cm = this.grid.colModel;
7386 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392 endDrag : function(e){
7393 this.view.headersDisabled = false;
7394 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395 var diff = endX - this.startPos;
7396 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
7399 autoOffset : function(){
7404 * Ext JS Library 1.1.1
7405 * Copyright(c) 2006-2007, Ext JS, LLC.
7407 * Originally Released Under LGPL - original licence link has changed is not relivant.
7410 * <script type="text/javascript">
7414 * @class Roo.grid.AbstractSelectionModel
7415 * @extends Roo.util.Observable
7416 * Abstract base class for grid SelectionModels. It provides the interface that should be
7417 * implemented by descendant classes. This class should not be directly instantiated.
7420 Roo.grid.AbstractSelectionModel = function(){
7421 this.locked = false;
7422 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7425 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7426 /** @ignore Called by the grid automatically. Do not call directly. */
7427 init : function(grid){
7433 * Locks the selections.
7440 * Unlocks the selections.
7442 unlock : function(){
7443 this.locked = false;
7447 * Returns true if the selections are locked.
7450 isLocked : function(){
7455 * Ext JS Library 1.1.1
7456 * Copyright(c) 2006-2007, Ext JS, LLC.
7458 * Originally Released Under LGPL - original licence link has changed is not relivant.
7461 * <script type="text/javascript">
7464 * @extends Roo.grid.AbstractSelectionModel
7465 * @class Roo.grid.RowSelectionModel
7466 * The default SelectionModel used by {@link Roo.grid.Grid}.
7467 * It supports multiple selections and keyboard selection/navigation.
7469 * @param {Object} config
7471 Roo.grid.RowSelectionModel = function(config){
7472 Roo.apply(this, config);
7473 this.selections = new Roo.util.MixedCollection(false, function(o){
7478 this.lastActive = false;
7482 * @event selectionchange
7483 * Fires when the selection changes
7484 * @param {SelectionModel} this
7486 "selectionchange" : true,
7488 * @event afterselectionchange
7489 * Fires after the selection changes (eg. by key press or clicking)
7490 * @param {SelectionModel} this
7492 "afterselectionchange" : true,
7494 * @event beforerowselect
7495 * Fires when a row is selected being selected, return false to cancel.
7496 * @param {SelectionModel} this
7497 * @param {Number} rowIndex The selected index
7498 * @param {Boolean} keepExisting False if other selections will be cleared
7500 "beforerowselect" : true,
7503 * Fires when a row is selected.
7504 * @param {SelectionModel} this
7505 * @param {Number} rowIndex The selected index
7506 * @param {Roo.data.Record} r The record
7510 * @event rowdeselect
7511 * Fires when a row is deselected.
7512 * @param {SelectionModel} this
7513 * @param {Number} rowIndex The selected index
7515 "rowdeselect" : true
7517 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7518 this.locked = false;
7521 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7523 * @cfg {Boolean} singleSelect
7524 * True to allow selection of only one row at a time (defaults to false)
7526 singleSelect : false,
7529 initEvents : function(){
7531 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7532 this.grid.on("mousedown", this.handleMouseDown, this);
7533 }else{ // allow click to work like normal
7534 this.grid.on("rowclick", this.handleDragableRowClick, this);
7536 // bootstrap does not have a view..
7537 var view = this.grid.view ? this.grid.view : this.grid;
7538 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7541 this.selectPrevious(e.shiftKey);
7542 }else if(this.last !== false && this.lastActive !== false){
7543 var last = this.last;
7544 this.selectRange(this.last, this.lastActive-1);
7545 view.focusRow(this.lastActive);
7550 this.selectFirstRow();
7552 this.fireEvent("afterselectionchange", this);
7554 "down" : function(e){
7556 this.selectNext(e.shiftKey);
7557 }else if(this.last !== false && this.lastActive !== false){
7558 var last = this.last;
7559 this.selectRange(this.last, this.lastActive+1);
7560 view.focusRow(this.lastActive);
7565 this.selectFirstRow();
7567 this.fireEvent("afterselectionchange", this);
7573 view.on("refresh", this.onRefresh, this);
7574 view.on("rowupdated", this.onRowUpdated, this);
7575 view.on("rowremoved", this.onRemove, this);
7579 onRefresh : function(){
7580 var ds = this.grid.ds, i, v = this.grid.view;
7581 var s = this.selections;
7583 if((i = ds.indexOfId(r.id)) != -1){
7585 s.add(ds.getAt(i)); // updating the selection relate data
7593 onRemove : function(v, index, r){
7594 this.selections.remove(r);
7598 onRowUpdated : function(v, index, r){
7599 if(this.isSelected(r)){
7600 v.onRowSelect(index);
7606 * @param {Array} records The records to select
7607 * @param {Boolean} keepExisting (optional) True to keep existing selections
7609 selectRecords : function(records, keepExisting){
7611 this.clearSelections();
7613 var ds = this.grid.ds;
7614 for(var i = 0, len = records.length; i < len; i++){
7615 this.selectRow(ds.indexOf(records[i]), true);
7620 * Gets the number of selected rows.
7623 getCount : function(){
7624 return this.selections.length;
7628 * Selects the first row in the grid.
7630 selectFirstRow : function(){
7635 * Select the last row.
7636 * @param {Boolean} keepExisting (optional) True to keep existing selections
7638 selectLastRow : function(keepExisting){
7639 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7643 * Selects the row immediately following the last selected row.
7644 * @param {Boolean} keepExisting (optional) True to keep existing selections
7646 selectNext : function(keepExisting){
7647 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7648 this.selectRow(this.last+1, keepExisting);
7649 var view = this.grid.view ? this.grid.view : this.grid;
7650 view.focusRow(this.last);
7655 * Selects the row that precedes the last selected row.
7656 * @param {Boolean} keepExisting (optional) True to keep existing selections
7658 selectPrevious : function(keepExisting){
7660 this.selectRow(this.last-1, keepExisting);
7661 var view = this.grid.view ? this.grid.view : this.grid;
7662 view.focusRow(this.last);
7667 * Returns the selected records
7668 * @return {Array} Array of selected records
7670 getSelections : function(){
7671 return [].concat(this.selections.items);
7675 * Returns the first selected record.
7678 getSelected : function(){
7679 return this.selections.itemAt(0);
7684 * Clears all selections.
7686 clearSelections : function(fast){
7691 var ds = this.grid.ds;
7692 var s = this.selections;
7694 this.deselectRow(ds.indexOfId(r.id));
7698 this.selections.clear();
7707 selectAll : function(){
7711 this.selections.clear();
7712 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7713 this.selectRow(i, true);
7718 * Returns True if there is a selection.
7721 hasSelection : function(){
7722 return this.selections.length > 0;
7726 * Returns True if the specified row is selected.
7727 * @param {Number/Record} record The record or index of the record to check
7730 isSelected : function(index){
7731 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7732 return (r && this.selections.key(r.id) ? true : false);
7736 * Returns True if the specified record id is selected.
7737 * @param {String} id The id of record to check
7740 isIdSelected : function(id){
7741 return (this.selections.key(id) ? true : false);
7745 handleMouseDown : function(e, t)
7747 var view = this.grid.view ? this.grid.view : this.grid;
7749 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7752 if(e.shiftKey && this.last !== false){
7753 var last = this.last;
7754 this.selectRange(last, rowIndex, e.ctrlKey);
7755 this.last = last; // reset the last
7756 view.focusRow(rowIndex);
7758 var isSelected = this.isSelected(rowIndex);
7759 if(e.button !== 0 && isSelected){
7760 view.focusRow(rowIndex);
7761 }else if(e.ctrlKey && isSelected){
7762 this.deselectRow(rowIndex);
7763 }else if(!isSelected){
7764 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7765 view.focusRow(rowIndex);
7768 this.fireEvent("afterselectionchange", this);
7771 handleDragableRowClick : function(grid, rowIndex, e)
7773 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7774 this.selectRow(rowIndex, false);
7775 var view = this.grid.view ? this.grid.view : this.grid;
7776 view.focusRow(rowIndex);
7777 this.fireEvent("afterselectionchange", this);
7782 * Selects multiple rows.
7783 * @param {Array} rows Array of the indexes of the row to select
7784 * @param {Boolean} keepExisting (optional) True to keep existing selections
7786 selectRows : function(rows, keepExisting){
7788 this.clearSelections();
7790 for(var i = 0, len = rows.length; i < len; i++){
7791 this.selectRow(rows[i], true);
7796 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7797 * @param {Number} startRow The index of the first row in the range
7798 * @param {Number} endRow The index of the last row in the range
7799 * @param {Boolean} keepExisting (optional) True to retain existing selections
7801 selectRange : function(startRow, endRow, keepExisting){
7806 this.clearSelections();
7808 if(startRow <= endRow){
7809 for(var i = startRow; i <= endRow; i++){
7810 this.selectRow(i, true);
7813 for(var i = startRow; i >= endRow; i--){
7814 this.selectRow(i, true);
7820 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7821 * @param {Number} startRow The index of the first row in the range
7822 * @param {Number} endRow The index of the last row in the range
7824 deselectRange : function(startRow, endRow, preventViewNotify){
7828 for(var i = startRow; i <= endRow; i++){
7829 this.deselectRow(i, preventViewNotify);
7835 * @param {Number} row The index of the row to select
7836 * @param {Boolean} keepExisting (optional) True to keep existing selections
7838 selectRow : function(index, keepExisting, preventViewNotify){
7839 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7842 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7843 if(!keepExisting || this.singleSelect){
7844 this.clearSelections();
7846 var r = this.grid.ds.getAt(index);
7847 this.selections.add(r);
7848 this.last = this.lastActive = index;
7849 if(!preventViewNotify){
7850 var view = this.grid.view ? this.grid.view : this.grid;
7851 view.onRowSelect(index);
7853 this.fireEvent("rowselect", this, index, r);
7854 this.fireEvent("selectionchange", this);
7860 * @param {Number} row The index of the row to deselect
7862 deselectRow : function(index, preventViewNotify){
7866 if(this.last == index){
7869 if(this.lastActive == index){
7870 this.lastActive = false;
7872 var r = this.grid.ds.getAt(index);
7873 this.selections.remove(r);
7874 if(!preventViewNotify){
7875 var view = this.grid.view ? this.grid.view : this.grid;
7876 view.onRowDeselect(index);
7878 this.fireEvent("rowdeselect", this, index);
7879 this.fireEvent("selectionchange", this);
7883 restoreLast : function(){
7885 this.last = this._last;
7890 acceptsNav : function(row, col, cm){
7891 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7895 onEditorKey : function(field, e){
7896 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7901 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7903 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7905 }else if(k == e.ENTER && !e.ctrlKey){
7909 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7911 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7913 }else if(k == e.ESC){
7917 g.startEditing(newCell[0], newCell[1]);
7922 * Ext JS Library 1.1.1
7923 * Copyright(c) 2006-2007, Ext JS, LLC.
7925 * Originally Released Under LGPL - original licence link has changed is not relivant.
7928 * <script type="text/javascript">
7933 * @class Roo.grid.ColumnModel
7934 * @extends Roo.util.Observable
7935 * This is the default implementation of a ColumnModel used by the Grid. It defines
7936 * the columns in the grid.
7939 var colModel = new Roo.grid.ColumnModel([
7940 {header: "Ticker", width: 60, sortable: true, locked: true},
7941 {header: "Company Name", width: 150, sortable: true},
7942 {header: "Market Cap.", width: 100, sortable: true},
7943 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7944 {header: "Employees", width: 100, sortable: true, resizable: false}
7949 * The config options listed for this class are options which may appear in each
7950 * individual column definition.
7951 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7953 * @param {Object} config An Array of column config objects. See this class's
7954 * config objects for details.
7956 Roo.grid.ColumnModel = function(config){
7958 * The config passed into the constructor
7960 this.config = []; //config;
7963 // if no id, create one
7964 // if the column does not have a dataIndex mapping,
7965 // map it to the order it is in the config
7966 for(var i = 0, len = config.length; i < len; i++){
7967 this.addColumn(config[i]);
7972 * The width of columns which have no width specified (defaults to 100)
7975 this.defaultWidth = 100;
7978 * Default sortable of columns which have no sortable specified (defaults to false)
7981 this.defaultSortable = false;
7985 * @event widthchange
7986 * Fires when the width of a column changes.
7987 * @param {ColumnModel} this
7988 * @param {Number} columnIndex The column index
7989 * @param {Number} newWidth The new width
7991 "widthchange": true,
7993 * @event headerchange
7994 * Fires when the text of a header changes.
7995 * @param {ColumnModel} this
7996 * @param {Number} columnIndex The column index
7997 * @param {Number} newText The new header text
7999 "headerchange": true,
8001 * @event hiddenchange
8002 * Fires when a column is hidden or "unhidden".
8003 * @param {ColumnModel} this
8004 * @param {Number} columnIndex The column index
8005 * @param {Boolean} hidden true if hidden, false otherwise
8007 "hiddenchange": true,
8009 * @event columnmoved
8010 * Fires when a column is moved.
8011 * @param {ColumnModel} this
8012 * @param {Number} oldIndex
8013 * @param {Number} newIndex
8015 "columnmoved" : true,
8017 * @event columlockchange
8018 * Fires when a column's locked state is changed
8019 * @param {ColumnModel} this
8020 * @param {Number} colIndex
8021 * @param {Boolean} locked true if locked
8023 "columnlockchange" : true
8025 Roo.grid.ColumnModel.superclass.constructor.call(this);
8027 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8029 * @cfg {String} header The header text to display in the Grid view.
8032 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8033 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8034 * specified, the column's index is used as an index into the Record's data Array.
8037 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8038 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8041 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8042 * Defaults to the value of the {@link #defaultSortable} property.
8043 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8046 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8049 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8052 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8055 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8058 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8059 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8060 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8061 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8064 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8067 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8070 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8073 * @cfg {String} cursor (Optional)
8076 * @cfg {String} tooltip (Optional)
8079 * @cfg {Number} xs (Optional)
8082 * @cfg {Number} sm (Optional)
8085 * @cfg {Number} md (Optional)
8088 * @cfg {Number} lg (Optional)
8091 * Returns the id of the column at the specified index.
8092 * @param {Number} index The column index
8093 * @return {String} the id
8095 getColumnId : function(index){
8096 return this.config[index].id;
8100 * Returns the column for a specified id.
8101 * @param {String} id The column id
8102 * @return {Object} the column
8104 getColumnById : function(id){
8105 return this.lookup[id];
8110 * Returns the column Object for a specified dataIndex.
8111 * @param {String} dataIndex The column dataIndex
8112 * @return {Object|Boolean} the column or false if not found
8114 getColumnByDataIndex: function(dataIndex){
8115 var index = this.findColumnIndex(dataIndex);
8116 return index > -1 ? this.config[index] : false;
8120 * Returns the index for a specified column id.
8121 * @param {String} id The column id
8122 * @return {Number} the index, or -1 if not found
8124 getIndexById : function(id){
8125 for(var i = 0, len = this.config.length; i < len; i++){
8126 if(this.config[i].id == id){
8134 * Returns the index for a specified column dataIndex.
8135 * @param {String} dataIndex The column dataIndex
8136 * @return {Number} the index, or -1 if not found
8139 findColumnIndex : function(dataIndex){
8140 for(var i = 0, len = this.config.length; i < len; i++){
8141 if(this.config[i].dataIndex == dataIndex){
8149 moveColumn : function(oldIndex, newIndex){
8150 var c = this.config[oldIndex];
8151 this.config.splice(oldIndex, 1);
8152 this.config.splice(newIndex, 0, c);
8153 this.dataMap = null;
8154 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8157 isLocked : function(colIndex){
8158 return this.config[colIndex].locked === true;
8161 setLocked : function(colIndex, value, suppressEvent){
8162 if(this.isLocked(colIndex) == value){
8165 this.config[colIndex].locked = value;
8167 this.fireEvent("columnlockchange", this, colIndex, value);
8171 getTotalLockedWidth : function(){
8173 for(var i = 0; i < this.config.length; i++){
8174 if(this.isLocked(i) && !this.isHidden(i)){
8175 this.totalWidth += this.getColumnWidth(i);
8181 getLockedCount : function(){
8182 for(var i = 0, len = this.config.length; i < len; i++){
8183 if(!this.isLocked(i)){
8188 return this.config.length;
8192 * Returns the number of columns.
8195 getColumnCount : function(visibleOnly){
8196 if(visibleOnly === true){
8198 for(var i = 0, len = this.config.length; i < len; i++){
8199 if(!this.isHidden(i)){
8205 return this.config.length;
8209 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8210 * @param {Function} fn
8211 * @param {Object} scope (optional)
8212 * @return {Array} result
8214 getColumnsBy : function(fn, scope){
8216 for(var i = 0, len = this.config.length; i < len; i++){
8217 var c = this.config[i];
8218 if(fn.call(scope||this, c, i) === true){
8226 * Returns true if the specified column is sortable.
8227 * @param {Number} col The column index
8230 isSortable : function(col){
8231 if(typeof this.config[col].sortable == "undefined"){
8232 return this.defaultSortable;
8234 return this.config[col].sortable;
8238 * Returns the rendering (formatting) function defined for the column.
8239 * @param {Number} col The column index.
8240 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8242 getRenderer : function(col){
8243 if(!this.config[col].renderer){
8244 return Roo.grid.ColumnModel.defaultRenderer;
8246 return this.config[col].renderer;
8250 * Sets the rendering (formatting) function for a column.
8251 * @param {Number} col The column index
8252 * @param {Function} fn The function to use to process the cell's raw data
8253 * to return HTML markup for the grid view. The render function is called with
8254 * the following parameters:<ul>
8255 * <li>Data value.</li>
8256 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8257 * <li>css A CSS style string to apply to the table cell.</li>
8258 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8259 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8260 * <li>Row index</li>
8261 * <li>Column index</li>
8262 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8264 setRenderer : function(col, fn){
8265 this.config[col].renderer = fn;
8269 * Returns the width for the specified column.
8270 * @param {Number} col The column index
8273 getColumnWidth : function(col){
8274 return this.config[col].width * 1 || this.defaultWidth;
8278 * Sets the width for a column.
8279 * @param {Number} col The column index
8280 * @param {Number} width The new width
8282 setColumnWidth : function(col, width, suppressEvent){
8283 this.config[col].width = width;
8284 this.totalWidth = null;
8286 this.fireEvent("widthchange", this, col, width);
8291 * Returns the total width of all columns.
8292 * @param {Boolean} includeHidden True to include hidden column widths
8295 getTotalWidth : function(includeHidden){
8296 if(!this.totalWidth){
8297 this.totalWidth = 0;
8298 for(var i = 0, len = this.config.length; i < len; i++){
8299 if(includeHidden || !this.isHidden(i)){
8300 this.totalWidth += this.getColumnWidth(i);
8304 return this.totalWidth;
8308 * Returns the header for the specified column.
8309 * @param {Number} col The column index
8312 getColumnHeader : function(col){
8313 return this.config[col].header;
8317 * Sets the header for a column.
8318 * @param {Number} col The column index
8319 * @param {String} header The new header
8321 setColumnHeader : function(col, header){
8322 this.config[col].header = header;
8323 this.fireEvent("headerchange", this, col, header);
8327 * Returns the tooltip for the specified column.
8328 * @param {Number} col The column index
8331 getColumnTooltip : function(col){
8332 return this.config[col].tooltip;
8335 * Sets the tooltip for a column.
8336 * @param {Number} col The column index
8337 * @param {String} tooltip The new tooltip
8339 setColumnTooltip : function(col, tooltip){
8340 this.config[col].tooltip = tooltip;
8344 * Returns the dataIndex for the specified column.
8345 * @param {Number} col The column index
8348 getDataIndex : function(col){
8349 return this.config[col].dataIndex;
8353 * Sets the dataIndex for a column.
8354 * @param {Number} col The column index
8355 * @param {Number} dataIndex The new dataIndex
8357 setDataIndex : function(col, dataIndex){
8358 this.config[col].dataIndex = dataIndex;
8364 * Returns true if the cell is editable.
8365 * @param {Number} colIndex The column index
8366 * @param {Number} rowIndex The row index - this is nto actually used..?
8369 isCellEditable : function(colIndex, rowIndex){
8370 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8374 * Returns the editor defined for the cell/column.
8375 * return false or null to disable editing.
8376 * @param {Number} colIndex The column index
8377 * @param {Number} rowIndex The row index
8380 getCellEditor : function(colIndex, rowIndex){
8381 return this.config[colIndex].editor;
8385 * Sets if a column is editable.
8386 * @param {Number} col The column index
8387 * @param {Boolean} editable True if the column is editable
8389 setEditable : function(col, editable){
8390 this.config[col].editable = editable;
8395 * Returns true if the column is hidden.
8396 * @param {Number} colIndex The column index
8399 isHidden : function(colIndex){
8400 return this.config[colIndex].hidden;
8405 * Returns true if the column width cannot be changed
8407 isFixed : function(colIndex){
8408 return this.config[colIndex].fixed;
8412 * Returns true if the column can be resized
8415 isResizable : function(colIndex){
8416 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8419 * Sets if a column is hidden.
8420 * @param {Number} colIndex The column index
8421 * @param {Boolean} hidden True if the column is hidden
8423 setHidden : function(colIndex, hidden){
8424 this.config[colIndex].hidden = hidden;
8425 this.totalWidth = null;
8426 this.fireEvent("hiddenchange", this, colIndex, hidden);
8430 * Sets the editor for a column.
8431 * @param {Number} col The column index
8432 * @param {Object} editor The editor object
8434 setEditor : function(col, editor){
8435 this.config[col].editor = editor;
8438 * Add a column (experimental...) - defaults to adding to the end..
8439 * @param {Object} config
8441 addColumn : function(c)
8444 var i = this.config.length;
8447 if(typeof c.dataIndex == "undefined"){
8450 if(typeof c.renderer == "string"){
8451 c.renderer = Roo.util.Format[c.renderer];
8453 if(typeof c.id == "undefined"){
8456 if(c.editor && c.editor.xtype){
8457 c.editor = Roo.factory(c.editor, Roo.grid);
8459 if(c.editor && c.editor.isFormField){
8460 c.editor = new Roo.grid.GridEditor(c.editor);
8462 this.lookup[c.id] = c;
8467 Roo.grid.ColumnModel.defaultRenderer = function(value)
8469 if(typeof value == "object") {
8472 if(typeof value == "string" && value.length < 1){
8476 return String.format("{0}", value);
8479 // Alias for backwards compatibility
8480 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8483 * Ext JS Library 1.1.1
8484 * Copyright(c) 2006-2007, Ext JS, LLC.
8486 * Originally Released Under LGPL - original licence link has changed is not relivant.
8489 * <script type="text/javascript">
8493 * @class Roo.LoadMask
8494 * A simple utility class for generically masking elements while loading data. If the element being masked has
8495 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8496 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8497 * element's UpdateManager load indicator and will be destroyed after the initial load.
8499 * Create a new LoadMask
8500 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8501 * @param {Object} config The config object
8503 Roo.LoadMask = function(el, config){
8504 this.el = Roo.get(el);
8505 Roo.apply(this, config);
8507 this.store.on('beforeload', this.onBeforeLoad, this);
8508 this.store.on('load', this.onLoad, this);
8509 this.store.on('loadexception', this.onLoadException, this);
8510 this.removeMask = false;
8512 var um = this.el.getUpdateManager();
8513 um.showLoadIndicator = false; // disable the default indicator
8514 um.on('beforeupdate', this.onBeforeLoad, this);
8515 um.on('update', this.onLoad, this);
8516 um.on('failure', this.onLoad, this);
8517 this.removeMask = true;
8521 Roo.LoadMask.prototype = {
8523 * @cfg {Boolean} removeMask
8524 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8525 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8529 * The text to display in a centered loading message box (defaults to 'Loading...')
8533 * @cfg {String} msgCls
8534 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8536 msgCls : 'x-mask-loading',
8539 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8545 * Disables the mask to prevent it from being displayed
8547 disable : function(){
8548 this.disabled = true;
8552 * Enables the mask so that it can be displayed
8554 enable : function(){
8555 this.disabled = false;
8558 onLoadException : function()
8562 if (typeof(arguments[3]) != 'undefined') {
8563 Roo.MessageBox.alert("Error loading",arguments[3]);
8567 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8568 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8575 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8580 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8584 onBeforeLoad : function(){
8586 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8591 destroy : function(){
8593 this.store.un('beforeload', this.onBeforeLoad, this);
8594 this.store.un('load', this.onLoad, this);
8595 this.store.un('loadexception', this.onLoadException, this);
8597 var um = this.el.getUpdateManager();
8598 um.un('beforeupdate', this.onBeforeLoad, this);
8599 um.un('update', this.onLoad, this);
8600 um.un('failure', this.onLoad, this);
8604 * @class Roo.bootstrap.Table
8606 * @extends Roo.bootstrap.Component
8607 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8608 * Similar to Roo.grid.Grid
8610 var table = Roo.factory({
8612 xns : Roo.bootstrap,
8613 autoSizeColumns: true,
8620 sortInfo : { direction : 'ASC', field: 'name' },
8622 xtype : 'HttpProxy',
8625 url : 'https://example.com/some.data.url.json'
8628 xtype : 'JsonReader',
8630 fields : [ 'id', 'name', whatever' ],
8637 xtype : 'ColumnModel',
8641 dataIndex : 'is_in_group',
8644 renderer : function(v, x , r) {
8646 return String.format("{0}", v)
8652 xtype : 'RowSelectionModel',
8653 xns : Roo.bootstrap.Table
8654 // you can add listeners to catch selection change here....
8660 grid.render(Roo.get("some-div"));
8663 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8668 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8669 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8670 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8672 * @cfg {String} cls table class
8675 * @cfg {boolean} striped Should the rows be alternative striped
8676 * @cfg {boolean} bordered Add borders to the table
8677 * @cfg {boolean} hover Add hover highlighting
8678 * @cfg {boolean} condensed Format condensed
8679 * @cfg {boolean} responsive Format condensed
8680 * @cfg {Boolean} loadMask (true|false) default false
8681 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8682 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8683 * @cfg {Boolean} rowSelection (true|false) default false
8684 * @cfg {Boolean} cellSelection (true|false) default false
8685 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8686 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8687 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8688 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8689 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8690 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8693 * Create a new Table
8694 * @param {Object} config The config object
8697 Roo.bootstrap.Table = function(config)
8699 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8702 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8703 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8704 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8705 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8707 this.view = this; // compat with grid.
8709 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8711 this.sm.grid = this;
8712 this.selModel = Roo.factory(this.sm, Roo.grid);
8713 this.sm = this.selModel;
8714 this.sm.xmodule = this.xmodule || false;
8717 if (this.cm && typeof(this.cm.config) == 'undefined') {
8718 this.colModel = new Roo.grid.ColumnModel(this.cm);
8719 this.cm = this.colModel;
8720 this.cm.xmodule = this.xmodule || false;
8723 this.store= Roo.factory(this.store, Roo.data);
8724 this.ds = this.store;
8725 this.ds.xmodule = this.xmodule || false;
8728 if (this.footer && this.store) {
8729 this.footer.dataSource = this.ds;
8730 this.footer = Roo.factory(this.footer);
8737 * Fires when a cell is clicked
8738 * @param {Roo.bootstrap.Table} this
8739 * @param {Roo.Element} el
8740 * @param {Number} rowIndex
8741 * @param {Number} columnIndex
8742 * @param {Roo.EventObject} e
8746 * @event celldblclick
8747 * Fires when a cell is double clicked
8748 * @param {Roo.bootstrap.Table} this
8749 * @param {Roo.Element} el
8750 * @param {Number} rowIndex
8751 * @param {Number} columnIndex
8752 * @param {Roo.EventObject} e
8754 "celldblclick" : true,
8757 * Fires when a row is clicked
8758 * @param {Roo.bootstrap.Table} this
8759 * @param {Roo.Element} el
8760 * @param {Number} rowIndex
8761 * @param {Roo.EventObject} e
8765 * @event rowdblclick
8766 * Fires when a row is double clicked
8767 * @param {Roo.bootstrap.Table} this
8768 * @param {Roo.Element} el
8769 * @param {Number} rowIndex
8770 * @param {Roo.EventObject} e
8772 "rowdblclick" : true,
8775 * Fires when a mouseover occur
8776 * @param {Roo.bootstrap.Table} this
8777 * @param {Roo.Element} el
8778 * @param {Number} rowIndex
8779 * @param {Number} columnIndex
8780 * @param {Roo.EventObject} e
8785 * Fires when a mouseout occur
8786 * @param {Roo.bootstrap.Table} this
8787 * @param {Roo.Element} el
8788 * @param {Number} rowIndex
8789 * @param {Number} columnIndex
8790 * @param {Roo.EventObject} e
8795 * Fires when a row is rendered, so you can change add a style to it.
8796 * @param {Roo.bootstrap.Table} this
8797 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8801 * @event rowsrendered
8802 * Fires when all the rows have been rendered
8803 * @param {Roo.bootstrap.Table} this
8805 'rowsrendered' : true,
8807 * @event contextmenu
8808 * The raw contextmenu event for the entire grid.
8809 * @param {Roo.EventObject} e
8811 "contextmenu" : true,
8813 * @event rowcontextmenu
8814 * Fires when a row is right clicked
8815 * @param {Roo.bootstrap.Table} this
8816 * @param {Number} rowIndex
8817 * @param {Roo.EventObject} e
8819 "rowcontextmenu" : true,
8821 * @event cellcontextmenu
8822 * Fires when a cell is right clicked
8823 * @param {Roo.bootstrap.Table} this
8824 * @param {Number} rowIndex
8825 * @param {Number} cellIndex
8826 * @param {Roo.EventObject} e
8828 "cellcontextmenu" : true,
8830 * @event headercontextmenu
8831 * Fires when a header is right clicked
8832 * @param {Roo.bootstrap.Table} this
8833 * @param {Number} columnIndex
8834 * @param {Roo.EventObject} e
8836 "headercontextmenu" : true,
8839 * The raw mousedown event for the entire grid.
8840 * @param {Roo.EventObject} e
8847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8863 enableColumnResize: true,
8865 rowSelection : false,
8866 cellSelection : false,
8869 minColumnWidth : 50,
8871 // Roo.Element - the tbody
8872 bodyEl: false, // <tbody> Roo.Element - thead element
8873 headEl: false, // <thead> Roo.Element - thead element
8874 resizeProxy : false, // proxy element for dragging?
8878 container: false, // used by gridpanel...
8884 auto_hide_footer : false,
8886 view: false, // actually points to this..
8888 getAutoCreate : function()
8890 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8897 // this get's auto added by panel.Grid
8898 if (this.scrollBody) {
8899 cfg.cls += ' table-body-fixed';
8902 cfg.cls += ' table-striped';
8906 cfg.cls += ' table-hover';
8908 if (this.bordered) {
8909 cfg.cls += ' table-bordered';
8911 if (this.condensed) {
8912 cfg.cls += ' table-condensed';
8915 if (this.responsive) {
8916 cfg.cls += ' table-responsive';
8920 cfg.cls+= ' ' +this.cls;
8926 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8929 if(this.store || this.cm){
8930 if(this.headerShow){
8931 cfg.cn.push(this.renderHeader());
8934 cfg.cn.push(this.renderBody());
8936 if(this.footerShow){
8937 cfg.cn.push(this.renderFooter());
8939 // where does this come from?
8940 //cfg.cls+= ' TableGrid';
8943 return { cn : [ cfg ] };
8946 initEvents : function()
8948 if(!this.store || !this.cm){
8951 if (this.selModel) {
8952 this.selModel.initEvents();
8956 //Roo.log('initEvents with ds!!!!');
8958 this.bodyEl = this.el.select('tbody', true).first();
8959 this.headEl = this.el.select('thead', true).first();
8960 this.mainFoot = this.el.select('tfoot', true).first();
8965 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8966 e.on('click', this.sort, this);
8970 // why is this done????? = it breaks dialogs??
8971 //this.parent().el.setStyle('position', 'relative');
8975 this.footer.parentId = this.id;
8976 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8979 this.el.select('tfoot tr td').first().addClass('hide');
8984 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8987 this.store.on('load', this.onLoad, this);
8988 this.store.on('beforeload', this.onBeforeLoad, this);
8989 this.store.on('update', this.onUpdate, this);
8990 this.store.on('add', this.onAdd, this);
8991 this.store.on("clear", this.clear, this);
8993 this.el.on("contextmenu", this.onContextMenu, this);
8996 this.cm.on("headerchange", this.onHeaderChange, this);
8997 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8999 //?? does bodyEl get replaced on render?
9000 this.bodyEl.on("click", this.onClick, this);
9001 this.bodyEl.on("dblclick", this.onDblClick, this);
9002 this.bodyEl.on('scroll', this.onBodyScroll, this);
9004 // guessing mainbody will work - this relays usually caught by selmodel at present.
9005 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9008 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9011 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9012 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9017 // Compatibility with grid - we implement all the view features at present.
9018 getView : function()
9023 onContextMenu : function(e, t)
9025 this.processEvent("contextmenu", e);
9028 processEvent : function(name, e)
9030 if (name != 'touchstart' ) {
9031 this.fireEvent(name, e);
9034 var t = e.getTarget();
9036 var cell = Roo.get(t);
9042 if(cell.findParent('tfoot', false, true)){
9046 if(cell.findParent('thead', false, true)){
9048 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9049 cell = Roo.get(t).findParent('th', false, true);
9051 Roo.log("failed to find th in thead?");
9052 Roo.log(e.getTarget());
9057 var cellIndex = cell.dom.cellIndex;
9059 var ename = name == 'touchstart' ? 'click' : name;
9060 this.fireEvent("header" + ename, this, cellIndex, e);
9065 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9066 cell = Roo.get(t).findParent('td', false, true);
9068 Roo.log("failed to find th in tbody?");
9069 Roo.log(e.getTarget());
9074 var row = cell.findParent('tr', false, true);
9075 var cellIndex = cell.dom.cellIndex;
9076 var rowIndex = row.dom.rowIndex - 1;
9080 this.fireEvent("row" + name, this, rowIndex, e);
9084 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9090 onMouseover : function(e, el)
9092 var cell = Roo.get(el);
9098 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9099 cell = cell.findParent('td', false, true);
9102 var row = cell.findParent('tr', false, true);
9103 var cellIndex = cell.dom.cellIndex;
9104 var rowIndex = row.dom.rowIndex - 1; // start from 0
9106 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9110 onMouseout : function(e, el)
9112 var cell = Roo.get(el);
9118 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9119 cell = cell.findParent('td', false, true);
9122 var row = cell.findParent('tr', false, true);
9123 var cellIndex = cell.dom.cellIndex;
9124 var rowIndex = row.dom.rowIndex - 1; // start from 0
9126 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9130 onClick : function(e, el)
9132 var cell = Roo.get(el);
9134 if(!cell || (!this.cellSelection && !this.rowSelection)){
9138 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9139 cell = cell.findParent('td', false, true);
9142 if(!cell || typeof(cell) == 'undefined'){
9146 var row = cell.findParent('tr', false, true);
9148 if(!row || typeof(row) == 'undefined'){
9152 var cellIndex = cell.dom.cellIndex;
9153 var rowIndex = this.getRowIndex(row);
9155 // why??? - should these not be based on SelectionModel?
9156 //if(this.cellSelection){
9157 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9160 //if(this.rowSelection){
9161 this.fireEvent('rowclick', this, row, rowIndex, e);
9166 onDblClick : function(e,el)
9168 var cell = Roo.get(el);
9170 if(!cell || (!this.cellSelection && !this.rowSelection)){
9174 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9175 cell = cell.findParent('td', false, true);
9178 if(!cell || typeof(cell) == 'undefined'){
9182 var row = cell.findParent('tr', false, true);
9184 if(!row || typeof(row) == 'undefined'){
9188 var cellIndex = cell.dom.cellIndex;
9189 var rowIndex = this.getRowIndex(row);
9191 if(this.cellSelection){
9192 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9195 if(this.rowSelection){
9196 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9199 findRowIndex : function(el)
9201 var cell = Roo.get(el);
9205 var row = cell.findParent('tr', false, true);
9207 if(!row || typeof(row) == 'undefined'){
9210 return this.getRowIndex(row);
9212 sort : function(e,el)
9214 var col = Roo.get(el);
9216 if(!col.hasClass('sortable')){
9220 var sort = col.attr('sort');
9223 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9227 this.store.sortInfo = {field : sort, direction : dir};
9230 Roo.log("calling footer first");
9231 this.footer.onClick('first');
9234 this.store.load({ params : { start : 0 } });
9238 renderHeader : function()
9246 this.totalWidth = 0;
9248 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9250 var config = cm.config[i];
9254 cls : 'x-hcol-' + i,
9257 html: cm.getColumnHeader(i)
9260 var tooltip = cm.getColumnTooltip(i);
9262 c.tooltip = tooltip;
9268 if(typeof(config.sortable) != 'undefined' && config.sortable){
9269 c.cls += ' sortable';
9270 c.html = '<i class="fa"></i>' + c.html;
9273 // could use BS4 hidden-..-down
9275 if(typeof(config.lgHeader) != 'undefined'){
9276 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9279 if(typeof(config.mdHeader) != 'undefined'){
9280 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9283 if(typeof(config.smHeader) != 'undefined'){
9284 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9287 if(typeof(config.xsHeader) != 'undefined'){
9288 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9295 if(typeof(config.tooltip) != 'undefined'){
9296 c.tooltip = config.tooltip;
9299 if(typeof(config.colspan) != 'undefined'){
9300 c.colspan = config.colspan;
9303 if(typeof(config.hidden) != 'undefined' && config.hidden){
9306 c.cls += ' d-block';
9309 if(typeof(config.dataIndex) != 'undefined'){
9310 c.sort = config.dataIndex;
9315 if(typeof(config.align) != 'undefined' && config.align.length){
9316 c.style += ' text-align:' + config.align + ';';
9319 if(typeof(config.width) != 'undefined'){
9320 c.style += ' width:' + config.width + 'px;';
9321 this.totalWidth += config.width;
9323 this.totalWidth += 100; // assume minimum of 100 per column?
9326 if(typeof(config.cls) != 'undefined'){
9327 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9329 // this is the bit that doesnt reall work at all...
9333 ['xs','sm','md','lg'].map(function(size){
9335 if(typeof(config[size]) == 'undefined'){
9339 if (!config[size]) { // 0 = hidden
9340 // BS 4 '0' is treated as hide that column and below.
9341 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9345 c.cls += ' col-' + size + '-' + config[size] + (
9346 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9353 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9364 renderBody : function()
9374 colspan : this.cm.getColumnCount()
9384 renderFooter : function()
9394 colspan : this.cm.getColumnCount()
9408 // Roo.log('ds onload');
9413 var ds = this.store;
9415 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9416 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9417 if (_this.store.sortInfo) {
9419 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9420 e.select('i', true).addClass(['fa-arrow-up']);
9423 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9424 e.select('i', true).addClass(['fa-arrow-down']);
9429 var tbody = this.bodyEl;
9431 if(ds.getCount() > 0){
9432 ds.data.each(function(d,rowIndex){
9433 var row = this.renderRow(cm, ds, rowIndex);
9435 tbody.createChild(row);
9439 if(row.cellObjects.length){
9440 Roo.each(row.cellObjects, function(r){
9441 _this.renderCellObject(r);
9448 var tfoot = this.el.select('tfoot', true).first();
9450 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9452 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9454 var total = this.ds.getTotalCount();
9456 if(this.footer.pageSize < total){
9457 this.mainFoot.show();
9461 Roo.each(this.el.select('tbody td', true).elements, function(e){
9462 e.on('mouseover', _this.onMouseover, _this);
9465 Roo.each(this.el.select('tbody td', true).elements, function(e){
9466 e.on('mouseout', _this.onMouseout, _this);
9468 this.fireEvent('rowsrendered', this);
9474 onUpdate : function(ds,record)
9476 this.refreshRow(record);
9480 onRemove : function(ds, record, index, isUpdate){
9481 if(isUpdate !== true){
9482 this.fireEvent("beforerowremoved", this, index, record);
9484 var bt = this.bodyEl.dom;
9486 var rows = this.el.select('tbody > tr', true).elements;
9488 if(typeof(rows[index]) != 'undefined'){
9489 bt.removeChild(rows[index].dom);
9492 // if(bt.rows[index]){
9493 // bt.removeChild(bt.rows[index]);
9496 if(isUpdate !== true){
9497 //this.stripeRows(index);
9498 //this.syncRowHeights(index, index);
9500 this.fireEvent("rowremoved", this, index, record);
9504 onAdd : function(ds, records, rowIndex)
9506 //Roo.log('on Add called');
9507 // - note this does not handle multiple adding very well..
9508 var bt = this.bodyEl.dom;
9509 for (var i =0 ; i < records.length;i++) {
9510 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9511 //Roo.log(records[i]);
9512 //Roo.log(this.store.getAt(rowIndex+i));
9513 this.insertRow(this.store, rowIndex + i, false);
9520 refreshRow : function(record){
9521 var ds = this.store, index;
9522 if(typeof record == 'number'){
9524 record = ds.getAt(index);
9526 index = ds.indexOf(record);
9528 return; // should not happen - but seems to
9531 this.insertRow(ds, index, true);
9533 this.onRemove(ds, record, index+1, true);
9535 //this.syncRowHeights(index, index);
9537 this.fireEvent("rowupdated", this, index, record);
9540 onRowSelect : function(rowIndex){
9541 var row = this.getRowDom(rowIndex);
9542 row.addClass(['bg-info','info']);
9545 onRowDeselect : function(rowIndex){
9546 var row = this.getRowDom(rowIndex);
9547 row.removeClass(['bg-info','info']);
9550 * Focuses the specified row.
9551 * @param {Number} row The row index
9553 focusRow : function(row)
9555 //Roo.log('GridView.focusRow');
9556 var x = this.bodyEl.dom.scrollLeft;
9557 this.focusCell(row, 0, false);
9558 this.bodyEl.dom.scrollLeft = x;
9562 * Focuses the specified cell.
9563 * @param {Number} row The row index
9564 * @param {Number} col The column index
9565 * @param {Boolean} hscroll false to disable horizontal scrolling
9567 focusCell : function(row, col, hscroll)
9569 //Roo.log('GridView.focusCell');
9570 var el = this.ensureVisible(row, col, hscroll);
9571 // not sure what focusEL achives = it's a <a> pos relative
9572 //this.focusEl.alignTo(el, "tl-tl");
9574 // this.focusEl.focus();
9576 // this.focusEl.focus.defer(1, this.focusEl);
9581 * Scrolls the specified cell into view
9582 * @param {Number} row The row index
9583 * @param {Number} col The column index
9584 * @param {Boolean} hscroll false to disable horizontal scrolling
9586 ensureVisible : function(row, col, hscroll)
9588 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9589 //return null; //disable for testing.
9590 if(typeof row != "number"){
9593 if(row < 0 && row >= this.ds.getCount()){
9596 col = (col !== undefined ? col : 0);
9598 while(cm.isHidden(col)){
9602 var el = this.getCellDom(row, col);
9606 var c = this.bodyEl.dom;
9608 var ctop = parseInt(el.offsetTop, 10);
9609 var cleft = parseInt(el.offsetLeft, 10);
9610 var cbot = ctop + el.offsetHeight;
9611 var cright = cleft + el.offsetWidth;
9613 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9614 var ch = 0; //?? header is not withing the area?
9615 var stop = parseInt(c.scrollTop, 10);
9616 var sleft = parseInt(c.scrollLeft, 10);
9617 var sbot = stop + ch;
9618 var sright = sleft + c.clientWidth;
9620 Roo.log('GridView.ensureVisible:' +
9622 ' c.clientHeight:' + c.clientHeight +
9623 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9632 //Roo.log("set scrolltop to ctop DISABLE?");
9633 }else if(cbot > sbot){
9634 //Roo.log("set scrolltop to cbot-ch");
9635 c.scrollTop = cbot-ch;
9638 if(hscroll !== false){
9640 c.scrollLeft = cleft;
9641 }else if(cright > sright){
9642 c.scrollLeft = cright-c.clientWidth;
9650 insertRow : function(dm, rowIndex, isUpdate){
9653 this.fireEvent("beforerowsinserted", this, rowIndex);
9655 //var s = this.getScrollState();
9656 var row = this.renderRow(this.cm, this.store, rowIndex);
9657 // insert before rowIndex..
9658 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9662 if(row.cellObjects.length){
9663 Roo.each(row.cellObjects, function(r){
9664 _this.renderCellObject(r);
9669 this.fireEvent("rowsinserted", this, rowIndex);
9670 //this.syncRowHeights(firstRow, lastRow);
9671 //this.stripeRows(firstRow);
9678 getRowDom : function(rowIndex)
9680 var rows = this.el.select('tbody > tr', true).elements;
9682 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9685 getCellDom : function(rowIndex, colIndex)
9687 var row = this.getRowDom(rowIndex);
9688 if (row === false) {
9691 var cols = row.select('td', true).elements;
9692 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9696 // returns the object tree for a tr..
9699 renderRow : function(cm, ds, rowIndex)
9701 var d = ds.getAt(rowIndex);
9705 cls : 'x-row-' + rowIndex,
9709 var cellObjects = [];
9711 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9712 var config = cm.config[i];
9714 var renderer = cm.getRenderer(i);
9718 if(typeof(renderer) !== 'undefined'){
9719 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9721 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9722 // and are rendered into the cells after the row is rendered - using the id for the element.
9724 if(typeof(value) === 'object'){
9734 rowIndex : rowIndex,
9739 this.fireEvent('rowclass', this, rowcfg);
9743 // this might end up displaying HTML?
9744 // this is too messy... - better to only do it on columsn you know are going to be too long
9745 //tooltip : (typeof(value) === 'object') ? '' : value,
9746 cls : rowcfg.rowClass + ' x-col-' + i,
9748 html: (typeof(value) === 'object') ? '' : value
9755 if(typeof(config.colspan) != 'undefined'){
9756 td.colspan = config.colspan;
9759 if(typeof(config.hidden) != 'undefined' && config.hidden){
9760 td.cls += ' d-none';
9762 td.cls += ' d-block';
9765 if(typeof(config.align) != 'undefined' && config.align.length){
9766 td.style += ' text-align:' + config.align + ';';
9768 if(typeof(config.valign) != 'undefined' && config.valign.length){
9769 td.style += ' vertical-align:' + config.valign + ';';
9772 if(typeof(config.width) != 'undefined'){
9773 td.style += ' width:' + config.width + 'px;';
9776 if(typeof(config.cursor) != 'undefined'){
9777 td.style += ' cursor:' + config.cursor + ';';
9780 if(typeof(config.cls) != 'undefined'){
9781 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9784 ['xs','sm','md','lg'].map(function(size){
9786 if(typeof(config[size]) == 'undefined'){
9792 if (!config[size]) { // 0 = hidden
9793 // BS 4 '0' is treated as hide that column and below.
9794 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9798 td.cls += ' col-' + size + '-' + config[size] + (
9799 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9809 row.cellObjects = cellObjects;
9817 onBeforeLoad : function()
9826 this.el.select('tbody', true).first().dom.innerHTML = '';
9829 * Show or hide a row.
9830 * @param {Number} rowIndex to show or hide
9831 * @param {Boolean} state hide
9833 setRowVisibility : function(rowIndex, state)
9835 var bt = this.bodyEl.dom;
9837 var rows = this.el.select('tbody > tr', true).elements;
9839 if(typeof(rows[rowIndex]) == 'undefined'){
9842 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9847 getSelectionModel : function(){
9849 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9851 return this.selModel;
9854 * Render the Roo.bootstrap object from renderder
9856 renderCellObject : function(r)
9860 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9862 var t = r.cfg.render(r.container);
9865 Roo.each(r.cfg.cn, function(c){
9867 container: t.getChildContainer(),
9870 _this.renderCellObject(child);
9875 * get the Row Index from a dom element.
9876 * @param {Roo.Element} row The row to look for
9877 * @returns {Number} the row
9879 getRowIndex : function(row)
9883 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9894 * get the header TH element for columnIndex
9895 * @param {Number} columnIndex
9896 * @returns {Roo.Element}
9898 getHeaderIndex: function(colIndex)
9900 var cols = this.headEl.select('th', true).elements;
9901 return cols[colIndex];
9904 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
9905 * @param {domElement} cell to look for
9906 * @returns {Number} the column
9908 getCellIndex : function(cell)
9910 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
9912 return parseInt(id[1], 10);
9917 * Returns the grid's underlying element = used by panel.Grid
9918 * @return {Element} The element
9920 getGridEl : function(){
9924 * Forces a resize - used by panel.Grid
9925 * @return {Element} The element
9927 autoSize : function()
9929 //var ctr = Roo.get(this.container.dom.parentElement);
9930 var ctr = Roo.get(this.el.dom);
9932 var thd = this.getGridEl().select('thead',true).first();
9933 var tbd = this.getGridEl().select('tbody', true).first();
9934 var tfd = this.getGridEl().select('tfoot', true).first();
9936 var cw = ctr.getWidth();
9937 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9941 tbd.setWidth(ctr.getWidth());
9942 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9943 // this needs fixing for various usage - currently only hydra job advers I think..
9945 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9947 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9950 cw = Math.max(cw, this.totalWidth);
9951 this.getGridEl().select('tbody tr',true).setWidth(cw);
9953 // resize 'expandable coloumn?
9955 return; // we doe not have a view in this design..
9958 onBodyScroll: function()
9960 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9962 this.headEl.setStyle({
9963 'position' : 'relative',
9964 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
9970 var scrollHeight = this.bodyEl.dom.scrollHeight;
9972 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
9974 var height = this.bodyEl.getHeight();
9976 if(scrollHeight - height == scrollTop) {
9978 var total = this.ds.getTotalCount();
9980 if(this.footer.cursor + this.footer.pageSize < total){
9982 this.footer.ds.load({
9984 start : this.footer.cursor + this.footer.pageSize,
9985 limit : this.footer.pageSize
9994 onColumnSplitterMoved : function()
10000 onHeaderChange : function()
10002 var header = this.renderHeader();
10003 var table = this.el.select('table', true).first();
10005 this.headEl.remove();
10006 this.headEl = table.createChild(header, this.bodyEl, false);
10008 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10009 e.on('click', this.sort, this);
10012 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10013 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10018 onHiddenChange : function(colModel, colIndex, hidden)
10020 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10021 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10023 //this.CSS.updateRule(thSelector, "display", "");
10024 var cols = this.headEl.select('th', true).elements;
10025 if (typeof(cols[colIndex]) != 'undefined') {
10026 cols[colIndex].removeClass(['d-none', 'd-block']);
10027 cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
10029 this.CSS.updateRule(tdSelector, "display", "");
10032 // this.CSS.updateRule(thSelector, "display", "none");
10033 this.CSS.updateRule(tdSelector, "display", "none");
10036 this.onHeaderChange();
10040 setColumnWidth: function(col_index, width)
10042 // width = "md-2 xs-2..."
10043 if(!this.colModel.config[col_index]) {
10047 var w = width.split(" ");
10049 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10051 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10054 for(var j = 0; j < w.length; j++) {
10060 var size_cls = w[j].split("-");
10062 if(!Number.isInteger(size_cls[1] * 1)) {
10066 if(!this.colModel.config[col_index][size_cls[0]]) {
10070 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10074 h_row[0].classList.replace(
10075 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10076 "col-"+size_cls[0]+"-"+size_cls[1]
10079 for(var i = 0; i < rows.length; i++) {
10081 var size_cls = w[j].split("-");
10083 if(!Number.isInteger(size_cls[1] * 1)) {
10087 if(!this.colModel.config[col_index][size_cls[0]]) {
10091 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10095 rows[i].classList.replace(
10096 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10097 "col-"+size_cls[0]+"-"+size_cls[1]
10101 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10106 // currently only used to find the split on drag..
10107 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10112 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10113 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
10121 * @class Roo.bootstrap.TableCell
10122 * @extends Roo.bootstrap.Component
10123 * Bootstrap TableCell class
10124 * @cfg {String} html cell contain text
10125 * @cfg {String} cls cell class
10126 * @cfg {String} tag cell tag (td|th) default td
10127 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10128 * @cfg {String} align Aligns the content in a cell
10129 * @cfg {String} axis Categorizes cells
10130 * @cfg {String} bgcolor Specifies the background color of a cell
10131 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10132 * @cfg {Number} colspan Specifies the number of columns a cell should span
10133 * @cfg {String} headers Specifies one or more header cells a cell is related to
10134 * @cfg {Number} height Sets the height of a cell
10135 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10136 * @cfg {Number} rowspan Sets the number of rows a cell should span
10137 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10138 * @cfg {String} valign Vertical aligns the content in a cell
10139 * @cfg {Number} width Specifies the width of a cell
10142 * Create a new TableCell
10143 * @param {Object} config The config object
10146 Roo.bootstrap.TableCell = function(config){
10147 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10150 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10170 getAutoCreate : function(){
10171 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10178 cfg.tag = this.tag;
10191 cfg.align=this.align
10196 if (this.bgcolor) {
10197 cfg.bgcolor=this.bgcolor
10199 if (this.charoff) {
10200 cfg.charoff=this.charoff
10202 if (this.colspan) {
10203 cfg.colspan=this.colspan
10205 if (this.headers) {
10206 cfg.headers=this.headers
10209 cfg.height=this.height
10212 cfg.nowrap=this.nowrap
10214 if (this.rowspan) {
10215 cfg.rowspan=this.rowspan
10218 cfg.scope=this.scope
10221 cfg.valign=this.valign
10224 cfg.width=this.width
10243 * @class Roo.bootstrap.TableRow
10244 * @extends Roo.bootstrap.Component
10245 * Bootstrap TableRow class
10246 * @cfg {String} cls row class
10247 * @cfg {String} align Aligns the content in a table row
10248 * @cfg {String} bgcolor Specifies a background color for a table row
10249 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10250 * @cfg {String} valign Vertical aligns the content in a table row
10253 * Create a new TableRow
10254 * @param {Object} config The config object
10257 Roo.bootstrap.TableRow = function(config){
10258 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10261 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10269 getAutoCreate : function(){
10270 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10277 cfg.cls = this.cls;
10280 cfg.align = this.align;
10283 cfg.bgcolor = this.bgcolor;
10286 cfg.charoff = this.charoff;
10289 cfg.valign = this.valign;
10307 * @class Roo.bootstrap.TableBody
10308 * @extends Roo.bootstrap.Component
10309 * Bootstrap TableBody class
10310 * @cfg {String} cls element class
10311 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10312 * @cfg {String} align Aligns the content inside the element
10313 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10314 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10317 * Create a new TableBody
10318 * @param {Object} config The config object
10321 Roo.bootstrap.TableBody = function(config){
10322 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10325 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10333 getAutoCreate : function(){
10334 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10344 cfg.tag = this.tag;
10348 cfg.align = this.align;
10351 cfg.charoff = this.charoff;
10354 cfg.valign = this.valign;
10361 // initEvents : function()
10364 // if(!this.store){
10368 // this.store = Roo.factory(this.store, Roo.data);
10369 // this.store.on('load', this.onLoad, this);
10371 // this.store.load();
10375 // onLoad: function ()
10377 // this.fireEvent('load', this);
10387 * Ext JS Library 1.1.1
10388 * Copyright(c) 2006-2007, Ext JS, LLC.
10390 * Originally Released Under LGPL - original licence link has changed is not relivant.
10393 * <script type="text/javascript">
10396 // as we use this in bootstrap.
10397 Roo.namespace('Roo.form');
10399 * @class Roo.form.Action
10400 * Internal Class used to handle form actions
10402 * @param {Roo.form.BasicForm} el The form element or its id
10403 * @param {Object} config Configuration options
10408 // define the action interface
10409 Roo.form.Action = function(form, options){
10411 this.options = options || {};
10414 * Client Validation Failed
10417 Roo.form.Action.CLIENT_INVALID = 'client';
10419 * Server Validation Failed
10422 Roo.form.Action.SERVER_INVALID = 'server';
10424 * Connect to Server Failed
10427 Roo.form.Action.CONNECT_FAILURE = 'connect';
10429 * Reading Data from Server Failed
10432 Roo.form.Action.LOAD_FAILURE = 'load';
10434 Roo.form.Action.prototype = {
10436 failureType : undefined,
10437 response : undefined,
10438 result : undefined,
10440 // interface method
10441 run : function(options){
10445 // interface method
10446 success : function(response){
10450 // interface method
10451 handleResponse : function(response){
10455 // default connection failure
10456 failure : function(response){
10458 this.response = response;
10459 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10460 this.form.afterAction(this, false);
10463 processResponse : function(response){
10464 this.response = response;
10465 if(!response.responseText){
10468 this.result = this.handleResponse(response);
10469 return this.result;
10472 // utility functions used internally
10473 getUrl : function(appendParams){
10474 var url = this.options.url || this.form.url || this.form.el.dom.action;
10476 var p = this.getParams();
10478 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10484 getMethod : function(){
10485 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10488 getParams : function(){
10489 var bp = this.form.baseParams;
10490 var p = this.options.params;
10492 if(typeof p == "object"){
10493 p = Roo.urlEncode(Roo.applyIf(p, bp));
10494 }else if(typeof p == 'string' && bp){
10495 p += '&' + Roo.urlEncode(bp);
10498 p = Roo.urlEncode(bp);
10503 createCallback : function(){
10505 success: this.success,
10506 failure: this.failure,
10508 timeout: (this.form.timeout*1000),
10509 upload: this.form.fileUpload ? this.success : undefined
10514 Roo.form.Action.Submit = function(form, options){
10515 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10518 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10521 haveProgress : false,
10522 uploadComplete : false,
10524 // uploadProgress indicator.
10525 uploadProgress : function()
10527 if (!this.form.progressUrl) {
10531 if (!this.haveProgress) {
10532 Roo.MessageBox.progress("Uploading", "Uploading");
10534 if (this.uploadComplete) {
10535 Roo.MessageBox.hide();
10539 this.haveProgress = true;
10541 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10543 var c = new Roo.data.Connection();
10545 url : this.form.progressUrl,
10550 success : function(req){
10551 //console.log(data);
10555 rdata = Roo.decode(req.responseText)
10557 Roo.log("Invalid data from server..");
10561 if (!rdata || !rdata.success) {
10563 Roo.MessageBox.alert(Roo.encode(rdata));
10566 var data = rdata.data;
10568 if (this.uploadComplete) {
10569 Roo.MessageBox.hide();
10574 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10575 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10578 this.uploadProgress.defer(2000,this);
10581 failure: function(data) {
10582 Roo.log('progress url failed ');
10593 // run get Values on the form, so it syncs any secondary forms.
10594 this.form.getValues();
10596 var o = this.options;
10597 var method = this.getMethod();
10598 var isPost = method == 'POST';
10599 if(o.clientValidation === false || this.form.isValid()){
10601 if (this.form.progressUrl) {
10602 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10603 (new Date() * 1) + '' + Math.random());
10608 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10609 form:this.form.el.dom,
10610 url:this.getUrl(!isPost),
10612 params:isPost ? this.getParams() : null,
10613 isUpload: this.form.fileUpload,
10614 formData : this.form.formData
10617 this.uploadProgress();
10619 }else if (o.clientValidation !== false){ // client validation failed
10620 this.failureType = Roo.form.Action.CLIENT_INVALID;
10621 this.form.afterAction(this, false);
10625 success : function(response)
10627 this.uploadComplete= true;
10628 if (this.haveProgress) {
10629 Roo.MessageBox.hide();
10633 var result = this.processResponse(response);
10634 if(result === true || result.success){
10635 this.form.afterAction(this, true);
10639 this.form.markInvalid(result.errors);
10640 this.failureType = Roo.form.Action.SERVER_INVALID;
10642 this.form.afterAction(this, false);
10644 failure : function(response)
10646 this.uploadComplete= true;
10647 if (this.haveProgress) {
10648 Roo.MessageBox.hide();
10651 this.response = response;
10652 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10653 this.form.afterAction(this, false);
10656 handleResponse : function(response){
10657 if(this.form.errorReader){
10658 var rs = this.form.errorReader.read(response);
10661 for(var i = 0, len = rs.records.length; i < len; i++) {
10662 var r = rs.records[i];
10663 errors[i] = r.data;
10666 if(errors.length < 1){
10670 success : rs.success,
10676 ret = Roo.decode(response.responseText);
10680 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10690 Roo.form.Action.Load = function(form, options){
10691 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10692 this.reader = this.form.reader;
10695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10700 Roo.Ajax.request(Roo.apply(
10701 this.createCallback(), {
10702 method:this.getMethod(),
10703 url:this.getUrl(false),
10704 params:this.getParams()
10708 success : function(response){
10710 var result = this.processResponse(response);
10711 if(result === true || !result.success || !result.data){
10712 this.failureType = Roo.form.Action.LOAD_FAILURE;
10713 this.form.afterAction(this, false);
10716 this.form.clearInvalid();
10717 this.form.setValues(result.data);
10718 this.form.afterAction(this, true);
10721 handleResponse : function(response){
10722 if(this.form.reader){
10723 var rs = this.form.reader.read(response);
10724 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10726 success : rs.success,
10730 return Roo.decode(response.responseText);
10734 Roo.form.Action.ACTION_TYPES = {
10735 'load' : Roo.form.Action.Load,
10736 'submit' : Roo.form.Action.Submit
10745 * @class Roo.bootstrap.Form
10746 * @extends Roo.bootstrap.Component
10747 * Bootstrap Form class
10748 * @cfg {String} method GET | POST (default POST)
10749 * @cfg {String} labelAlign top | left (default top)
10750 * @cfg {String} align left | right - for navbars
10751 * @cfg {Boolean} loadMask load mask when submit (default true)
10755 * Create a new Form
10756 * @param {Object} config The config object
10760 Roo.bootstrap.Form = function(config){
10762 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10764 Roo.bootstrap.Form.popover.apply();
10768 * @event clientvalidation
10769 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10770 * @param {Form} this
10771 * @param {Boolean} valid true if the form has passed client-side validation
10773 clientvalidation: true,
10775 * @event beforeaction
10776 * Fires before any action is performed. Return false to cancel the action.
10777 * @param {Form} this
10778 * @param {Action} action The action to be performed
10780 beforeaction: true,
10782 * @event actionfailed
10783 * Fires when an action fails.
10784 * @param {Form} this
10785 * @param {Action} action The action that failed
10787 actionfailed : true,
10789 * @event actioncomplete
10790 * Fires when an action is completed.
10791 * @param {Form} this
10792 * @param {Action} action The action that completed
10794 actioncomplete : true
10798 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10801 * @cfg {String} method
10802 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10806 * @cfg {String} url
10807 * The URL to use for form actions if one isn't supplied in the action options.
10810 * @cfg {Boolean} fileUpload
10811 * Set to true if this form is a file upload.
10815 * @cfg {Object} baseParams
10816 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10820 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10824 * @cfg {Sting} align (left|right) for navbar forms
10829 activeAction : null,
10832 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10833 * element by passing it or its id or mask the form itself by passing in true.
10836 waitMsgTarget : false,
10841 * @cfg {Boolean} errorMask (true|false) default false
10846 * @cfg {Number} maskOffset Default 100
10851 * @cfg {Boolean} maskBody
10855 getAutoCreate : function(){
10859 method : this.method || 'POST',
10860 id : this.id || Roo.id(),
10863 if (this.parent().xtype.match(/^Nav/)) {
10864 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10868 if (this.labelAlign == 'left' ) {
10869 cfg.cls += ' form-horizontal';
10875 initEvents : function()
10877 this.el.on('submit', this.onSubmit, this);
10878 // this was added as random key presses on the form where triggering form submit.
10879 this.el.on('keypress', function(e) {
10880 if (e.getCharCode() != 13) {
10883 // we might need to allow it for textareas.. and some other items.
10884 // check e.getTarget().
10886 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10890 Roo.log("keypress blocked");
10892 e.preventDefault();
10898 onSubmit : function(e){
10903 * Returns true if client-side validation on the form is successful.
10906 isValid : function(){
10907 var items = this.getItems();
10909 var target = false;
10911 items.each(function(f){
10917 Roo.log('invalid field: ' + f.name);
10921 if(!target && f.el.isVisible(true)){
10927 if(this.errorMask && !valid){
10928 Roo.bootstrap.Form.popover.mask(this, target);
10935 * Returns true if any fields in this form have changed since their original load.
10938 isDirty : function(){
10940 var items = this.getItems();
10941 items.each(function(f){
10951 * Performs a predefined action (submit or load) or custom actions you define on this form.
10952 * @param {String} actionName The name of the action type
10953 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10954 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10955 * accept other config options):
10957 Property Type Description
10958 ---------------- --------------- ----------------------------------------------------------------------------------
10959 url String The url for the action (defaults to the form's url)
10960 method String The form method to use (defaults to the form's method, or POST if not defined)
10961 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10962 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10963 validate the form on the client (defaults to false)
10965 * @return {BasicForm} this
10967 doAction : function(action, options){
10968 if(typeof action == 'string'){
10969 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10971 if(this.fireEvent('beforeaction', this, action) !== false){
10972 this.beforeAction(action);
10973 action.run.defer(100, action);
10979 beforeAction : function(action){
10980 var o = action.options;
10985 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10987 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10990 // not really supported yet.. ??
10992 //if(this.waitMsgTarget === true){
10993 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10994 //}else if(this.waitMsgTarget){
10995 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10996 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10998 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11004 afterAction : function(action, success){
11005 this.activeAction = null;
11006 var o = action.options;
11011 Roo.get(document.body).unmask();
11017 //if(this.waitMsgTarget === true){
11018 // this.el.unmask();
11019 //}else if(this.waitMsgTarget){
11020 // this.waitMsgTarget.unmask();
11022 // Roo.MessageBox.updateProgress(1);
11023 // Roo.MessageBox.hide();
11030 Roo.callback(o.success, o.scope, [this, action]);
11031 this.fireEvent('actioncomplete', this, action);
11035 // failure condition..
11036 // we have a scenario where updates need confirming.
11037 // eg. if a locking scenario exists..
11038 // we look for { errors : { needs_confirm : true }} in the response.
11040 (typeof(action.result) != 'undefined') &&
11041 (typeof(action.result.errors) != 'undefined') &&
11042 (typeof(action.result.errors.needs_confirm) != 'undefined')
11045 Roo.log("not supported yet");
11048 Roo.MessageBox.confirm(
11049 "Change requires confirmation",
11050 action.result.errorMsg,
11055 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11065 Roo.callback(o.failure, o.scope, [this, action]);
11066 // show an error message if no failed handler is set..
11067 if (!this.hasListener('actionfailed')) {
11068 Roo.log("need to add dialog support");
11070 Roo.MessageBox.alert("Error",
11071 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11072 action.result.errorMsg :
11073 "Saving Failed, please check your entries or try again"
11078 this.fireEvent('actionfailed', this, action);
11083 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11084 * @param {String} id The value to search for
11087 findField : function(id){
11088 var items = this.getItems();
11089 var field = items.get(id);
11091 items.each(function(f){
11092 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11099 return field || null;
11102 * Mark fields in this form invalid in bulk.
11103 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11104 * @return {BasicForm} this
11106 markInvalid : function(errors){
11107 if(errors instanceof Array){
11108 for(var i = 0, len = errors.length; i < len; i++){
11109 var fieldError = errors[i];
11110 var f = this.findField(fieldError.id);
11112 f.markInvalid(fieldError.msg);
11118 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11119 field.markInvalid(errors[id]);
11123 //Roo.each(this.childForms || [], function (f) {
11124 // f.markInvalid(errors);
11131 * Set values for fields in this form in bulk.
11132 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11133 * @return {BasicForm} this
11135 setValues : function(values){
11136 if(values instanceof Array){ // array of objects
11137 for(var i = 0, len = values.length; i < len; i++){
11139 var f = this.findField(v.id);
11141 f.setValue(v.value);
11142 if(this.trackResetOnLoad){
11143 f.originalValue = f.getValue();
11147 }else{ // object hash
11150 if(typeof values[id] != 'function' && (field = this.findField(id))){
11152 if (field.setFromData &&
11153 field.valueField &&
11154 field.displayField &&
11155 // combos' with local stores can
11156 // be queried via setValue()
11157 // to set their value..
11158 (field.store && !field.store.isLocal)
11162 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11163 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11164 field.setFromData(sd);
11166 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11168 field.setFromData(values);
11171 field.setValue(values[id]);
11175 if(this.trackResetOnLoad){
11176 field.originalValue = field.getValue();
11182 //Roo.each(this.childForms || [], function (f) {
11183 // f.setValues(values);
11190 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11191 * they are returned as an array.
11192 * @param {Boolean} asString
11195 getValues : function(asString){
11196 //if (this.childForms) {
11197 // copy values from the child forms
11198 // Roo.each(this.childForms, function (f) {
11199 // this.setValues(f.getValues());
11205 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11206 if(asString === true){
11209 return Roo.urlDecode(fs);
11213 * Returns the fields in this form as an object with key/value pairs.
11214 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11217 getFieldValues : function(with_hidden)
11219 var items = this.getItems();
11221 items.each(function(f){
11223 if (!f.getName()) {
11227 var v = f.getValue();
11229 if (f.inputType =='radio') {
11230 if (typeof(ret[f.getName()]) == 'undefined') {
11231 ret[f.getName()] = ''; // empty..
11234 if (!f.el.dom.checked) {
11238 v = f.el.dom.value;
11242 if(f.xtype == 'MoneyField'){
11243 ret[f.currencyName] = f.getCurrency();
11246 // not sure if this supported any more..
11247 if ((typeof(v) == 'object') && f.getRawValue) {
11248 v = f.getRawValue() ; // dates..
11250 // combo boxes where name != hiddenName...
11251 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11252 ret[f.name] = f.getRawValue();
11254 ret[f.getName()] = v;
11261 * Clears all invalid messages in this form.
11262 * @return {BasicForm} this
11264 clearInvalid : function(){
11265 var items = this.getItems();
11267 items.each(function(f){
11275 * Resets this form.
11276 * @return {BasicForm} this
11278 reset : function(){
11279 var items = this.getItems();
11280 items.each(function(f){
11284 Roo.each(this.childForms || [], function (f) {
11292 getItems : function()
11294 var r=new Roo.util.MixedCollection(false, function(o){
11295 return o.id || (o.id = Roo.id());
11297 var iter = function(el) {
11304 Roo.each(el.items,function(e) {
11313 hideFields : function(items)
11315 Roo.each(items, function(i){
11317 var f = this.findField(i);
11328 showFields : function(items)
11330 Roo.each(items, function(i){
11332 var f = this.findField(i);
11345 Roo.apply(Roo.bootstrap.Form, {
11361 intervalID : false,
11367 if(this.isApplied){
11372 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11373 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11374 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11375 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11378 this.maskEl.top.enableDisplayMode("block");
11379 this.maskEl.left.enableDisplayMode("block");
11380 this.maskEl.bottom.enableDisplayMode("block");
11381 this.maskEl.right.enableDisplayMode("block");
11383 this.toolTip = new Roo.bootstrap.Tooltip({
11384 cls : 'roo-form-error-popover',
11386 'left' : ['r-l', [-2,0], 'right'],
11387 'right' : ['l-r', [2,0], 'left'],
11388 'bottom' : ['tl-bl', [0,2], 'top'],
11389 'top' : [ 'bl-tl', [0,-2], 'bottom']
11393 this.toolTip.render(Roo.get(document.body));
11395 this.toolTip.el.enableDisplayMode("block");
11397 Roo.get(document.body).on('click', function(){
11401 Roo.get(document.body).on('touchstart', function(){
11405 this.isApplied = true
11408 mask : function(form, target)
11412 this.target = target;
11414 if(!this.form.errorMask || !target.el){
11418 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11420 Roo.log(scrollable);
11422 var ot = this.target.el.calcOffsetsTo(scrollable);
11424 var scrollTo = ot[1] - this.form.maskOffset;
11426 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11428 scrollable.scrollTo('top', scrollTo);
11430 var box = this.target.el.getBox();
11432 var zIndex = Roo.bootstrap.Modal.zIndex++;
11435 this.maskEl.top.setStyle('position', 'absolute');
11436 this.maskEl.top.setStyle('z-index', zIndex);
11437 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11438 this.maskEl.top.setLeft(0);
11439 this.maskEl.top.setTop(0);
11440 this.maskEl.top.show();
11442 this.maskEl.left.setStyle('position', 'absolute');
11443 this.maskEl.left.setStyle('z-index', zIndex);
11444 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11445 this.maskEl.left.setLeft(0);
11446 this.maskEl.left.setTop(box.y - this.padding);
11447 this.maskEl.left.show();
11449 this.maskEl.bottom.setStyle('position', 'absolute');
11450 this.maskEl.bottom.setStyle('z-index', zIndex);
11451 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11452 this.maskEl.bottom.setLeft(0);
11453 this.maskEl.bottom.setTop(box.bottom + this.padding);
11454 this.maskEl.bottom.show();
11456 this.maskEl.right.setStyle('position', 'absolute');
11457 this.maskEl.right.setStyle('z-index', zIndex);
11458 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11459 this.maskEl.right.setLeft(box.right + this.padding);
11460 this.maskEl.right.setTop(box.y - this.padding);
11461 this.maskEl.right.show();
11463 this.toolTip.bindEl = this.target.el;
11465 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11467 var tip = this.target.blankText;
11469 if(this.target.getValue() !== '' ) {
11471 if (this.target.invalidText.length) {
11472 tip = this.target.invalidText;
11473 } else if (this.target.regexText.length){
11474 tip = this.target.regexText;
11478 this.toolTip.show(tip);
11480 this.intervalID = window.setInterval(function() {
11481 Roo.bootstrap.Form.popover.unmask();
11484 window.onwheel = function(){ return false;};
11486 (function(){ this.isMasked = true; }).defer(500, this);
11490 unmask : function()
11492 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11496 this.maskEl.top.setStyle('position', 'absolute');
11497 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11498 this.maskEl.top.hide();
11500 this.maskEl.left.setStyle('position', 'absolute');
11501 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11502 this.maskEl.left.hide();
11504 this.maskEl.bottom.setStyle('position', 'absolute');
11505 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11506 this.maskEl.bottom.hide();
11508 this.maskEl.right.setStyle('position', 'absolute');
11509 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11510 this.maskEl.right.hide();
11512 this.toolTip.hide();
11514 this.toolTip.el.hide();
11516 window.onwheel = function(){ return true;};
11518 if(this.intervalID){
11519 window.clearInterval(this.intervalID);
11520 this.intervalID = false;
11523 this.isMasked = false;
11533 * Ext JS Library 1.1.1
11534 * Copyright(c) 2006-2007, Ext JS, LLC.
11536 * Originally Released Under LGPL - original licence link has changed is not relivant.
11539 * <script type="text/javascript">
11542 * @class Roo.form.VTypes
11543 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11546 Roo.form.VTypes = function(){
11547 // closure these in so they are only created once.
11548 var alpha = /^[a-zA-Z_]+$/;
11549 var alphanum = /^[a-zA-Z0-9_]+$/;
11550 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11551 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11553 // All these messages and functions are configurable
11556 * The function used to validate email addresses
11557 * @param {String} value The email address
11559 'email' : function(v){
11560 return email.test(v);
11563 * The error text to display when the email validation function returns false
11566 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11568 * The keystroke filter mask to be applied on email input
11571 'emailMask' : /[a-z0-9_\.\-@]/i,
11574 * The function used to validate URLs
11575 * @param {String} value The URL
11577 'url' : function(v){
11578 return url.test(v);
11581 * The error text to display when the url validation function returns false
11584 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11587 * The function used to validate alpha values
11588 * @param {String} value The value
11590 'alpha' : function(v){
11591 return alpha.test(v);
11594 * The error text to display when the alpha validation function returns false
11597 'alphaText' : 'This field should only contain letters and _',
11599 * The keystroke filter mask to be applied on alpha input
11602 'alphaMask' : /[a-z_]/i,
11605 * The function used to validate alphanumeric values
11606 * @param {String} value The value
11608 'alphanum' : function(v){
11609 return alphanum.test(v);
11612 * The error text to display when the alphanumeric validation function returns false
11615 'alphanumText' : 'This field should only contain letters, numbers and _',
11617 * The keystroke filter mask to be applied on alphanumeric input
11620 'alphanumMask' : /[a-z0-9_]/i
11630 * @class Roo.bootstrap.Input
11631 * @extends Roo.bootstrap.Component
11632 * Bootstrap Input class
11633 * @cfg {Boolean} disabled is it disabled
11634 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11635 * @cfg {String} name name of the input
11636 * @cfg {string} fieldLabel - the label associated
11637 * @cfg {string} placeholder - placeholder to put in text.
11638 * @cfg {string} before - input group add on before
11639 * @cfg {string} after - input group add on after
11640 * @cfg {string} size - (lg|sm) or leave empty..
11641 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11642 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11643 * @cfg {Number} md colspan out of 12 for computer-sized screens
11644 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11645 * @cfg {string} value default value of the input
11646 * @cfg {Number} labelWidth set the width of label
11647 * @cfg {Number} labellg set the width of label (1-12)
11648 * @cfg {Number} labelmd set the width of label (1-12)
11649 * @cfg {Number} labelsm set the width of label (1-12)
11650 * @cfg {Number} labelxs set the width of label (1-12)
11651 * @cfg {String} labelAlign (top|left)
11652 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11653 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11654 * @cfg {String} indicatorpos (left|right) default left
11655 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11656 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11657 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11659 * @cfg {String} align (left|center|right) Default left
11660 * @cfg {Boolean} forceFeedback (true|false) Default false
11663 * Create a new Input
11664 * @param {Object} config The config object
11667 Roo.bootstrap.Input = function(config){
11669 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11674 * Fires when this field receives input focus.
11675 * @param {Roo.form.Field} this
11680 * Fires when this field loses input focus.
11681 * @param {Roo.form.Field} this
11685 * @event specialkey
11686 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11687 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11688 * @param {Roo.form.Field} this
11689 * @param {Roo.EventObject} e The event object
11694 * Fires just before the field blurs if the field value has changed.
11695 * @param {Roo.form.Field} this
11696 * @param {Mixed} newValue The new value
11697 * @param {Mixed} oldValue The original value
11702 * Fires after the field has been marked as invalid.
11703 * @param {Roo.form.Field} this
11704 * @param {String} msg The validation message
11709 * Fires after the field has been validated with no errors.
11710 * @param {Roo.form.Field} this
11715 * Fires after the key up
11716 * @param {Roo.form.Field} this
11717 * @param {Roo.EventObject} e The event Object
11722 * Fires after the user pastes into input
11723 * @param {Roo.form.Field} this
11724 * @param {Roo.EventObject} e The event Object
11730 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11732 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11733 automatic validation (defaults to "keyup").
11735 validationEvent : "keyup",
11737 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11739 validateOnBlur : true,
11741 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11743 validationDelay : 250,
11745 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11747 focusClass : "x-form-focus", // not needed???
11751 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11753 invalidClass : "has-warning",
11756 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11758 validClass : "has-success",
11761 * @cfg {Boolean} hasFeedback (true|false) default true
11763 hasFeedback : true,
11766 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11768 invalidFeedbackClass : "glyphicon-warning-sign",
11771 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11773 validFeedbackClass : "glyphicon-ok",
11776 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11778 selectOnFocus : false,
11781 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11785 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11790 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11792 disableKeyFilter : false,
11795 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11799 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11803 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11805 blankText : "Please complete this mandatory field",
11808 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11812 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11814 maxLength : Number.MAX_VALUE,
11816 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11818 minLengthText : "The minimum length for this field is {0}",
11820 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11822 maxLengthText : "The maximum length for this field is {0}",
11826 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11827 * If available, this function will be called only after the basic validators all return true, and will be passed the
11828 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11832 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11833 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11834 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11838 * @cfg {String} regexText -- Depricated - use Invalid Text
11843 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11849 autocomplete: false,
11853 inputType : 'text',
11856 placeholder: false,
11861 preventMark: false,
11862 isFormField : true,
11865 labelAlign : false,
11868 formatedValue : false,
11869 forceFeedback : false,
11871 indicatorpos : 'left',
11881 parentLabelAlign : function()
11884 while (parent.parent()) {
11885 parent = parent.parent();
11886 if (typeof(parent.labelAlign) !='undefined') {
11887 return parent.labelAlign;
11894 getAutoCreate : function()
11896 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11902 if(this.inputType != 'hidden'){
11903 cfg.cls = 'form-group' //input-group
11909 type : this.inputType,
11910 value : this.value,
11911 cls : 'form-control',
11912 placeholder : this.placeholder || '',
11913 autocomplete : this.autocomplete || 'new-password'
11915 if (this.inputType == 'file') {
11916 input.style = 'overflow:hidden'; // why not in CSS?
11919 if(this.capture.length){
11920 input.capture = this.capture;
11923 if(this.accept.length){
11924 input.accept = this.accept + "/*";
11928 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11931 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11932 input.maxLength = this.maxLength;
11935 if (this.disabled) {
11936 input.disabled=true;
11939 if (this.readOnly) {
11940 input.readonly=true;
11944 input.name = this.name;
11948 input.cls += ' input-' + this.size;
11952 ['xs','sm','md','lg'].map(function(size){
11953 if (settings[size]) {
11954 cfg.cls += ' col-' + size + '-' + settings[size];
11958 var inputblock = input;
11962 cls: 'glyphicon form-control-feedback'
11965 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11968 cls : 'has-feedback',
11976 if (this.before || this.after) {
11979 cls : 'input-group',
11983 if (this.before && typeof(this.before) == 'string') {
11985 inputblock.cn.push({
11987 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11991 if (this.before && typeof(this.before) == 'object') {
11992 this.before = Roo.factory(this.before);
11994 inputblock.cn.push({
11996 cls : 'roo-input-before input-group-prepend input-group-' +
11997 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12001 inputblock.cn.push(input);
12003 if (this.after && typeof(this.after) == 'string') {
12004 inputblock.cn.push({
12006 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12010 if (this.after && typeof(this.after) == 'object') {
12011 this.after = Roo.factory(this.after);
12013 inputblock.cn.push({
12015 cls : 'roo-input-after input-group-append input-group-' +
12016 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12020 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12021 inputblock.cls += ' has-feedback';
12022 inputblock.cn.push(feedback);
12027 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12028 tooltip : 'This field is required'
12030 if (this.allowBlank ) {
12031 indicator.style = this.allowBlank ? ' display:none' : '';
12033 if (align ==='left' && this.fieldLabel.length) {
12035 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12042 cls : 'control-label col-form-label',
12043 html : this.fieldLabel
12054 var labelCfg = cfg.cn[1];
12055 var contentCfg = cfg.cn[2];
12057 if(this.indicatorpos == 'right'){
12062 cls : 'control-label col-form-label',
12066 html : this.fieldLabel
12080 labelCfg = cfg.cn[0];
12081 contentCfg = cfg.cn[1];
12085 if(this.labelWidth > 12){
12086 labelCfg.style = "width: " + this.labelWidth + 'px';
12089 if(this.labelWidth < 13 && this.labelmd == 0){
12090 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12093 if(this.labellg > 0){
12094 labelCfg.cls += ' col-lg-' + this.labellg;
12095 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12098 if(this.labelmd > 0){
12099 labelCfg.cls += ' col-md-' + this.labelmd;
12100 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12103 if(this.labelsm > 0){
12104 labelCfg.cls += ' col-sm-' + this.labelsm;
12105 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12108 if(this.labelxs > 0){
12109 labelCfg.cls += ' col-xs-' + this.labelxs;
12110 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12114 } else if ( this.fieldLabel.length) {
12121 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12122 tooltip : 'This field is required',
12123 style : this.allowBlank ? ' display:none' : ''
12127 //cls : 'input-group-addon',
12128 html : this.fieldLabel
12136 if(this.indicatorpos == 'right'){
12141 //cls : 'input-group-addon',
12142 html : this.fieldLabel
12147 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12148 tooltip : 'This field is required',
12149 style : this.allowBlank ? ' display:none' : ''
12169 if (this.parentType === 'Navbar' && this.parent().bar) {
12170 cfg.cls += ' navbar-form';
12173 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12174 // on BS4 we do this only if not form
12175 cfg.cls += ' navbar-form';
12183 * return the real input element.
12185 inputEl: function ()
12187 return this.el.select('input.form-control',true).first();
12190 tooltipEl : function()
12192 return this.inputEl();
12195 indicatorEl : function()
12197 if (Roo.bootstrap.version == 4) {
12198 return false; // not enabled in v4 yet.
12201 var indicator = this.el.select('i.roo-required-indicator',true).first();
12211 setDisabled : function(v)
12213 var i = this.inputEl().dom;
12215 i.removeAttribute('disabled');
12219 i.setAttribute('disabled','true');
12221 initEvents : function()
12224 this.inputEl().on("keydown" , this.fireKey, this);
12225 this.inputEl().on("focus", this.onFocus, this);
12226 this.inputEl().on("blur", this.onBlur, this);
12228 this.inputEl().relayEvent('keyup', this);
12229 this.inputEl().relayEvent('paste', this);
12231 this.indicator = this.indicatorEl();
12233 if(this.indicator){
12234 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12237 // reference to original value for reset
12238 this.originalValue = this.getValue();
12239 //Roo.form.TextField.superclass.initEvents.call(this);
12240 if(this.validationEvent == 'keyup'){
12241 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12242 this.inputEl().on('keyup', this.filterValidation, this);
12244 else if(this.validationEvent !== false){
12245 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12248 if(this.selectOnFocus){
12249 this.on("focus", this.preFocus, this);
12252 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12253 this.inputEl().on("keypress", this.filterKeys, this);
12255 this.inputEl().relayEvent('keypress', this);
12258 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12259 this.el.on("click", this.autoSize, this);
12262 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12263 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12266 if (typeof(this.before) == 'object') {
12267 this.before.render(this.el.select('.roo-input-before',true).first());
12269 if (typeof(this.after) == 'object') {
12270 this.after.render(this.el.select('.roo-input-after',true).first());
12273 this.inputEl().on('change', this.onChange, this);
12276 filterValidation : function(e){
12277 if(!e.isNavKeyPress()){
12278 this.validationTask.delay(this.validationDelay);
12282 * Validates the field value
12283 * @return {Boolean} True if the value is valid, else false
12285 validate : function(){
12286 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12287 if(this.disabled || this.validateValue(this.getRawValue())){
12292 this.markInvalid();
12298 * Validates a value according to the field's validation rules and marks the field as invalid
12299 * if the validation fails
12300 * @param {Mixed} value The value to validate
12301 * @return {Boolean} True if the value is valid, else false
12303 validateValue : function(value)
12305 if(this.getVisibilityEl().hasClass('hidden')){
12309 if(value.length < 1) { // if it's blank
12310 if(this.allowBlank){
12316 if(value.length < this.minLength){
12319 if(value.length > this.maxLength){
12323 var vt = Roo.form.VTypes;
12324 if(!vt[this.vtype](value, this)){
12328 if(typeof this.validator == "function"){
12329 var msg = this.validator(value);
12333 if (typeof(msg) == 'string') {
12334 this.invalidText = msg;
12338 if(this.regex && !this.regex.test(value)){
12346 fireKey : function(e){
12347 //Roo.log('field ' + e.getKey());
12348 if(e.isNavKeyPress()){
12349 this.fireEvent("specialkey", this, e);
12352 focus : function (selectText){
12354 this.inputEl().focus();
12355 if(selectText === true){
12356 this.inputEl().dom.select();
12362 onFocus : function(){
12363 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12364 // this.el.addClass(this.focusClass);
12366 if(!this.hasFocus){
12367 this.hasFocus = true;
12368 this.startValue = this.getValue();
12369 this.fireEvent("focus", this);
12373 beforeBlur : Roo.emptyFn,
12377 onBlur : function(){
12379 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12380 //this.el.removeClass(this.focusClass);
12382 this.hasFocus = false;
12383 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12386 var v = this.getValue();
12387 if(String(v) !== String(this.startValue)){
12388 this.fireEvent('change', this, v, this.startValue);
12390 this.fireEvent("blur", this);
12393 onChange : function(e)
12395 var v = this.getValue();
12396 if(String(v) !== String(this.startValue)){
12397 this.fireEvent('change', this, v, this.startValue);
12403 * Resets the current field value to the originally loaded value and clears any validation messages
12405 reset : function(){
12406 this.setValue(this.originalValue);
12410 * Returns the name of the field
12411 * @return {Mixed} name The name field
12413 getName: function(){
12417 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12418 * @return {Mixed} value The field value
12420 getValue : function(){
12422 var v = this.inputEl().getValue();
12427 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12428 * @return {Mixed} value The field value
12430 getRawValue : function(){
12431 var v = this.inputEl().getValue();
12437 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12438 * @param {Mixed} value The value to set
12440 setRawValue : function(v){
12441 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12444 selectText : function(start, end){
12445 var v = this.getRawValue();
12447 start = start === undefined ? 0 : start;
12448 end = end === undefined ? v.length : end;
12449 var d = this.inputEl().dom;
12450 if(d.setSelectionRange){
12451 d.setSelectionRange(start, end);
12452 }else if(d.createTextRange){
12453 var range = d.createTextRange();
12454 range.moveStart("character", start);
12455 range.moveEnd("character", v.length-end);
12462 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12463 * @param {Mixed} value The value to set
12465 setValue : function(v){
12468 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12474 processValue : function(value){
12475 if(this.stripCharsRe){
12476 var newValue = value.replace(this.stripCharsRe, '');
12477 if(newValue !== value){
12478 this.setRawValue(newValue);
12485 preFocus : function(){
12487 if(this.selectOnFocus){
12488 this.inputEl().dom.select();
12491 filterKeys : function(e){
12492 var k = e.getKey();
12493 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12496 var c = e.getCharCode(), cc = String.fromCharCode(c);
12497 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12500 if(!this.maskRe.test(cc)){
12505 * Clear any invalid styles/messages for this field
12507 clearInvalid : function(){
12509 if(!this.el || this.preventMark){ // not rendered
12514 this.el.removeClass([this.invalidClass, 'is-invalid']);
12516 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12518 var feedback = this.el.select('.form-control-feedback', true).first();
12521 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12526 if(this.indicator){
12527 this.indicator.removeClass('visible');
12528 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12531 this.fireEvent('valid', this);
12535 * Mark this field as valid
12537 markValid : function()
12539 if(!this.el || this.preventMark){ // not rendered...
12543 this.el.removeClass([this.invalidClass, this.validClass]);
12544 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12546 var feedback = this.el.select('.form-control-feedback', true).first();
12549 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12552 if(this.indicator){
12553 this.indicator.removeClass('visible');
12554 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12562 if(this.allowBlank && !this.getRawValue().length){
12565 if (Roo.bootstrap.version == 3) {
12566 this.el.addClass(this.validClass);
12568 this.inputEl().addClass('is-valid');
12571 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12573 var feedback = this.el.select('.form-control-feedback', true).first();
12576 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12577 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12582 this.fireEvent('valid', this);
12586 * Mark this field as invalid
12587 * @param {String} msg The validation message
12589 markInvalid : function(msg)
12591 if(!this.el || this.preventMark){ // not rendered
12595 this.el.removeClass([this.invalidClass, this.validClass]);
12596 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12598 var feedback = this.el.select('.form-control-feedback', true).first();
12601 this.el.select('.form-control-feedback', true).first().removeClass(
12602 [this.invalidFeedbackClass, this.validFeedbackClass]);
12609 if(this.allowBlank && !this.getRawValue().length){
12613 if(this.indicator){
12614 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12615 this.indicator.addClass('visible');
12617 if (Roo.bootstrap.version == 3) {
12618 this.el.addClass(this.invalidClass);
12620 this.inputEl().addClass('is-invalid');
12625 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12627 var feedback = this.el.select('.form-control-feedback', true).first();
12630 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12632 if(this.getValue().length || this.forceFeedback){
12633 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12640 this.fireEvent('invalid', this, msg);
12643 SafariOnKeyDown : function(event)
12645 // this is a workaround for a password hang bug on chrome/ webkit.
12646 if (this.inputEl().dom.type != 'password') {
12650 var isSelectAll = false;
12652 if(this.inputEl().dom.selectionEnd > 0){
12653 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12655 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12656 event.preventDefault();
12661 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12663 event.preventDefault();
12664 // this is very hacky as keydown always get's upper case.
12666 var cc = String.fromCharCode(event.getCharCode());
12667 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12671 adjustWidth : function(tag, w){
12672 tag = tag.toLowerCase();
12673 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12674 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12675 if(tag == 'input'){
12678 if(tag == 'textarea'){
12681 }else if(Roo.isOpera){
12682 if(tag == 'input'){
12685 if(tag == 'textarea'){
12693 setFieldLabel : function(v)
12695 if(!this.rendered){
12699 if(this.indicatorEl()){
12700 var ar = this.el.select('label > span',true);
12702 if (ar.elements.length) {
12703 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12704 this.fieldLabel = v;
12708 var br = this.el.select('label',true);
12710 if(br.elements.length) {
12711 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12712 this.fieldLabel = v;
12716 Roo.log('Cannot Found any of label > span || label in input');
12720 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12721 this.fieldLabel = v;
12736 * @class Roo.bootstrap.TextArea
12737 * @extends Roo.bootstrap.Input
12738 * Bootstrap TextArea class
12739 * @cfg {Number} cols Specifies the visible width of a text area
12740 * @cfg {Number} rows Specifies the visible number of lines in a text area
12741 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12742 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12743 * @cfg {string} html text
12746 * Create a new TextArea
12747 * @param {Object} config The config object
12750 Roo.bootstrap.TextArea = function(config){
12751 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12755 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12765 getAutoCreate : function(){
12767 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12773 if(this.inputType != 'hidden'){
12774 cfg.cls = 'form-group' //input-group
12782 value : this.value || '',
12783 html: this.html || '',
12784 cls : 'form-control',
12785 placeholder : this.placeholder || ''
12789 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12790 input.maxLength = this.maxLength;
12794 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12798 input.cols = this.cols;
12801 if (this.readOnly) {
12802 input.readonly = true;
12806 input.name = this.name;
12810 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12814 ['xs','sm','md','lg'].map(function(size){
12815 if (settings[size]) {
12816 cfg.cls += ' col-' + size + '-' + settings[size];
12820 var inputblock = input;
12822 if(this.hasFeedback && !this.allowBlank){
12826 cls: 'glyphicon form-control-feedback'
12830 cls : 'has-feedback',
12839 if (this.before || this.after) {
12842 cls : 'input-group',
12846 inputblock.cn.push({
12848 cls : 'input-group-addon',
12853 inputblock.cn.push(input);
12855 if(this.hasFeedback && !this.allowBlank){
12856 inputblock.cls += ' has-feedback';
12857 inputblock.cn.push(feedback);
12861 inputblock.cn.push({
12863 cls : 'input-group-addon',
12870 if (align ==='left' && this.fieldLabel.length) {
12875 cls : 'control-label',
12876 html : this.fieldLabel
12887 if(this.labelWidth > 12){
12888 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12891 if(this.labelWidth < 13 && this.labelmd == 0){
12892 this.labelmd = this.labelWidth;
12895 if(this.labellg > 0){
12896 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12897 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12900 if(this.labelmd > 0){
12901 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12902 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12905 if(this.labelsm > 0){
12906 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12907 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12910 if(this.labelxs > 0){
12911 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12912 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12915 } else if ( this.fieldLabel.length) {
12920 //cls : 'input-group-addon',
12921 html : this.fieldLabel
12939 if (this.disabled) {
12940 input.disabled=true;
12947 * return the real textarea element.
12949 inputEl: function ()
12951 return this.el.select('textarea.form-control',true).first();
12955 * Clear any invalid styles/messages for this field
12957 clearInvalid : function()
12960 if(!this.el || this.preventMark){ // not rendered
12964 var label = this.el.select('label', true).first();
12965 var icon = this.el.select('i.fa-star', true).first();
12970 this.el.removeClass( this.validClass);
12971 this.inputEl().removeClass('is-invalid');
12973 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12975 var feedback = this.el.select('.form-control-feedback', true).first();
12978 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12983 this.fireEvent('valid', this);
12987 * Mark this field as valid
12989 markValid : function()
12991 if(!this.el || this.preventMark){ // not rendered
12995 this.el.removeClass([this.invalidClass, this.validClass]);
12996 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12998 var feedback = this.el.select('.form-control-feedback', true).first();
13001 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13004 if(this.disabled || this.allowBlank){
13008 var label = this.el.select('label', true).first();
13009 var icon = this.el.select('i.fa-star', true).first();
13014 if (Roo.bootstrap.version == 3) {
13015 this.el.addClass(this.validClass);
13017 this.inputEl().addClass('is-valid');
13021 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13023 var feedback = this.el.select('.form-control-feedback', true).first();
13026 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13027 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13032 this.fireEvent('valid', this);
13036 * Mark this field as invalid
13037 * @param {String} msg The validation message
13039 markInvalid : function(msg)
13041 if(!this.el || this.preventMark){ // not rendered
13045 this.el.removeClass([this.invalidClass, this.validClass]);
13046 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13048 var feedback = this.el.select('.form-control-feedback', true).first();
13051 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13054 if(this.disabled || this.allowBlank){
13058 var label = this.el.select('label', true).first();
13059 var icon = this.el.select('i.fa-star', true).first();
13061 if(!this.getValue().length && label && !icon){
13062 this.el.createChild({
13064 cls : 'text-danger fa fa-lg fa-star',
13065 tooltip : 'This field is required',
13066 style : 'margin-right:5px;'
13070 if (Roo.bootstrap.version == 3) {
13071 this.el.addClass(this.invalidClass);
13073 this.inputEl().addClass('is-invalid');
13076 // fixme ... this may be depricated need to test..
13077 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13079 var feedback = this.el.select('.form-control-feedback', true).first();
13082 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13084 if(this.getValue().length || this.forceFeedback){
13085 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13092 this.fireEvent('invalid', this, msg);
13100 * trigger field - base class for combo..
13105 * @class Roo.bootstrap.TriggerField
13106 * @extends Roo.bootstrap.Input
13107 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13108 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13109 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13110 * for which you can provide a custom implementation. For example:
13112 var trigger = new Roo.bootstrap.TriggerField();
13113 trigger.onTriggerClick = myTriggerFn;
13114 trigger.applyTo('my-field');
13117 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13118 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13119 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13120 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13121 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13124 * Create a new TriggerField.
13125 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13126 * to the base TextField)
13128 Roo.bootstrap.TriggerField = function(config){
13129 this.mimicing = false;
13130 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13133 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13135 * @cfg {String} triggerClass A CSS class to apply to the trigger
13138 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13143 * @cfg {Boolean} removable (true|false) special filter default false
13147 /** @cfg {Boolean} grow @hide */
13148 /** @cfg {Number} growMin @hide */
13149 /** @cfg {Number} growMax @hide */
13155 autoSize: Roo.emptyFn,
13159 deferHeight : true,
13162 actionMode : 'wrap',
13167 getAutoCreate : function(){
13169 var align = this.labelAlign || this.parentLabelAlign();
13174 cls: 'form-group' //input-group
13181 type : this.inputType,
13182 cls : 'form-control',
13183 autocomplete: 'new-password',
13184 placeholder : this.placeholder || ''
13188 input.name = this.name;
13191 input.cls += ' input-' + this.size;
13194 if (this.disabled) {
13195 input.disabled=true;
13198 var inputblock = input;
13200 if(this.hasFeedback && !this.allowBlank){
13204 cls: 'glyphicon form-control-feedback'
13207 if(this.removable && !this.editable ){
13209 cls : 'has-feedback',
13215 cls : 'roo-combo-removable-btn close'
13222 cls : 'has-feedback',
13231 if(this.removable && !this.editable ){
13233 cls : 'roo-removable',
13239 cls : 'roo-combo-removable-btn close'
13246 if (this.before || this.after) {
13249 cls : 'input-group',
13253 inputblock.cn.push({
13255 cls : 'input-group-addon input-group-prepend input-group-text',
13260 inputblock.cn.push(input);
13262 if(this.hasFeedback && !this.allowBlank){
13263 inputblock.cls += ' has-feedback';
13264 inputblock.cn.push(feedback);
13268 inputblock.cn.push({
13270 cls : 'input-group-addon input-group-append input-group-text',
13279 var ibwrap = inputblock;
13284 cls: 'roo-select2-choices',
13288 cls: 'roo-select2-search-field',
13300 cls: 'roo-select2-container input-group',
13305 cls: 'form-hidden-field'
13311 if(!this.multiple && this.showToggleBtn){
13317 if (this.caret != false) {
13320 cls: 'fa fa-' + this.caret
13327 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13329 Roo.bootstrap.version == 3 ? caret : '',
13332 cls: 'combobox-clear',
13346 combobox.cls += ' roo-select2-container-multi';
13350 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13351 tooltip : 'This field is required'
13353 if (Roo.bootstrap.version == 4) {
13356 style : 'display:none'
13361 if (align ==='left' && this.fieldLabel.length) {
13363 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13370 cls : 'control-label',
13371 html : this.fieldLabel
13383 var labelCfg = cfg.cn[1];
13384 var contentCfg = cfg.cn[2];
13386 if(this.indicatorpos == 'right'){
13391 cls : 'control-label',
13395 html : this.fieldLabel
13409 labelCfg = cfg.cn[0];
13410 contentCfg = cfg.cn[1];
13413 if(this.labelWidth > 12){
13414 labelCfg.style = "width: " + this.labelWidth + 'px';
13417 if(this.labelWidth < 13 && this.labelmd == 0){
13418 this.labelmd = this.labelWidth;
13421 if(this.labellg > 0){
13422 labelCfg.cls += ' col-lg-' + this.labellg;
13423 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13426 if(this.labelmd > 0){
13427 labelCfg.cls += ' col-md-' + this.labelmd;
13428 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13431 if(this.labelsm > 0){
13432 labelCfg.cls += ' col-sm-' + this.labelsm;
13433 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13436 if(this.labelxs > 0){
13437 labelCfg.cls += ' col-xs-' + this.labelxs;
13438 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13441 } else if ( this.fieldLabel.length) {
13442 // Roo.log(" label");
13447 //cls : 'input-group-addon',
13448 html : this.fieldLabel
13456 if(this.indicatorpos == 'right'){
13464 html : this.fieldLabel
13478 // Roo.log(" no label && no align");
13485 ['xs','sm','md','lg'].map(function(size){
13486 if (settings[size]) {
13487 cfg.cls += ' col-' + size + '-' + settings[size];
13498 onResize : function(w, h){
13499 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13500 // if(typeof w == 'number'){
13501 // var x = w - this.trigger.getWidth();
13502 // this.inputEl().setWidth(this.adjustWidth('input', x));
13503 // this.trigger.setStyle('left', x+'px');
13508 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13511 getResizeEl : function(){
13512 return this.inputEl();
13516 getPositionEl : function(){
13517 return this.inputEl();
13521 alignErrorIcon : function(){
13522 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13526 initEvents : function(){
13530 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13531 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13532 if(!this.multiple && this.showToggleBtn){
13533 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13534 if(this.hideTrigger){
13535 this.trigger.setDisplayed(false);
13537 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13541 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13544 if(this.removable && !this.editable && !this.tickable){
13545 var close = this.closeTriggerEl();
13548 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13549 close.on('click', this.removeBtnClick, this, close);
13553 //this.trigger.addClassOnOver('x-form-trigger-over');
13554 //this.trigger.addClassOnClick('x-form-trigger-click');
13557 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13561 closeTriggerEl : function()
13563 var close = this.el.select('.roo-combo-removable-btn', true).first();
13564 return close ? close : false;
13567 removeBtnClick : function(e, h, el)
13569 e.preventDefault();
13571 if(this.fireEvent("remove", this) !== false){
13573 this.fireEvent("afterremove", this)
13577 createList : function()
13579 this.list = Roo.get(document.body).createChild({
13580 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13581 cls: 'typeahead typeahead-long dropdown-menu shadow',
13582 style: 'display:none'
13585 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13590 initTrigger : function(){
13595 onDestroy : function(){
13597 this.trigger.removeAllListeners();
13598 // this.trigger.remove();
13601 // this.wrap.remove();
13603 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13607 onFocus : function(){
13608 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13610 if(!this.mimicing){
13611 this.wrap.addClass('x-trigger-wrap-focus');
13612 this.mimicing = true;
13613 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13614 if(this.monitorTab){
13615 this.el.on("keydown", this.checkTab, this);
13622 checkTab : function(e){
13623 if(e.getKey() == e.TAB){
13624 this.triggerBlur();
13629 onBlur : function(){
13634 mimicBlur : function(e, t){
13636 if(!this.wrap.contains(t) && this.validateBlur()){
13637 this.triggerBlur();
13643 triggerBlur : function(){
13644 this.mimicing = false;
13645 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13646 if(this.monitorTab){
13647 this.el.un("keydown", this.checkTab, this);
13649 //this.wrap.removeClass('x-trigger-wrap-focus');
13650 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13654 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13655 validateBlur : function(e, t){
13660 onDisable : function(){
13661 this.inputEl().dom.disabled = true;
13662 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13664 // this.wrap.addClass('x-item-disabled');
13669 onEnable : function(){
13670 this.inputEl().dom.disabled = false;
13671 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13673 // this.el.removeClass('x-item-disabled');
13678 onShow : function(){
13679 var ae = this.getActionEl();
13682 ae.dom.style.display = '';
13683 ae.dom.style.visibility = 'visible';
13689 onHide : function(){
13690 var ae = this.getActionEl();
13691 ae.dom.style.display = 'none';
13695 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13696 * by an implementing function.
13698 * @param {EventObject} e
13700 onTriggerClick : Roo.emptyFn
13708 * @class Roo.bootstrap.CardUploader
13709 * @extends Roo.bootstrap.Button
13710 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13711 * @cfg {Number} errorTimeout default 3000
13712 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13713 * @cfg {Array} html The button text.
13717 * Create a new CardUploader
13718 * @param {Object} config The config object
13721 Roo.bootstrap.CardUploader = function(config){
13725 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13728 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13736 * When a image is clicked on - and needs to display a slideshow or similar..
13737 * @param {Roo.bootstrap.Card} this
13738 * @param {Object} The image information data
13744 * When a the download link is clicked
13745 * @param {Roo.bootstrap.Card} this
13746 * @param {Object} The image information data contains
13753 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13756 errorTimeout : 3000,
13760 fileCollection : false,
13763 getAutoCreate : function()
13767 cls :'form-group' ,
13772 //cls : 'input-group-addon',
13773 html : this.fieldLabel
13781 value : this.value,
13782 cls : 'd-none form-control'
13787 multiple : 'multiple',
13789 cls : 'd-none roo-card-upload-selector'
13793 cls : 'roo-card-uploader-button-container w-100 mb-2'
13796 cls : 'card-columns roo-card-uploader-container'
13806 getChildContainer : function() /// what children are added to.
13808 return this.containerEl;
13811 getButtonContainer : function() /// what children are added to.
13813 return this.el.select(".roo-card-uploader-button-container").first();
13816 initEvents : function()
13819 Roo.bootstrap.Input.prototype.initEvents.call(this);
13823 xns: Roo.bootstrap,
13826 container_method : 'getButtonContainer' ,
13827 html : this.html, // fix changable?
13830 'click' : function(btn, e) {
13839 this.urlAPI = (window.createObjectURL && window) ||
13840 (window.URL && URL.revokeObjectURL && URL) ||
13841 (window.webkitURL && webkitURL);
13846 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13848 this.selectorEl.on('change', this.onFileSelected, this);
13851 this.images.forEach(function(img) {
13854 this.images = false;
13856 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13862 onClick : function(e)
13864 e.preventDefault();
13866 this.selectorEl.dom.click();
13870 onFileSelected : function(e)
13872 e.preventDefault();
13874 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13878 Roo.each(this.selectorEl.dom.files, function(file){
13879 this.addFile(file);
13888 addFile : function(file)
13891 if(typeof(file) === 'string'){
13892 throw "Add file by name?"; // should not happen
13896 if(!file || !this.urlAPI){
13906 var url = _this.urlAPI.createObjectURL( file);
13909 id : Roo.bootstrap.CardUploader.ID--,
13910 is_uploaded : false,
13914 mimetype : file.type,
13922 * addCard - add an Attachment to the uploader
13923 * @param data - the data about the image to upload
13927 title : "Title of file",
13928 is_uploaded : false,
13929 src : "http://.....",
13930 srcfile : { the File upload object },
13931 mimetype : file.type,
13934 .. any other data...
13940 addCard : function (data)
13942 // hidden input element?
13943 // if the file is not an image...
13944 //then we need to use something other that and header_image
13949 xns : Roo.bootstrap,
13950 xtype : 'CardFooter',
13953 xns : Roo.bootstrap,
13959 xns : Roo.bootstrap,
13961 html : String.format("<small>{0}</small>", data.title),
13962 cls : 'col-10 text-left',
13967 click : function() {
13969 t.fireEvent( "download", t, data );
13975 xns : Roo.bootstrap,
13977 style: 'max-height: 28px; ',
13983 click : function() {
13984 t.removeCard(data.id)
13996 var cn = this.addxtype(
13999 xns : Roo.bootstrap,
14002 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14003 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14004 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14009 initEvents : function() {
14010 Roo.bootstrap.Card.prototype.initEvents.call(this);
14012 this.imgEl = this.el.select('.card-img-top').first();
14014 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14015 this.imgEl.set({ 'pointer' : 'cursor' });
14018 this.getCardFooter().addClass('p-1');
14025 // dont' really need ot update items.
14026 // this.items.push(cn);
14027 this.fileCollection.add(cn);
14029 if (!data.srcfile) {
14030 this.updateInput();
14035 var reader = new FileReader();
14036 reader.addEventListener("load", function() {
14037 data.srcdata = reader.result;
14040 reader.readAsDataURL(data.srcfile);
14045 removeCard : function(id)
14048 var card = this.fileCollection.get(id);
14049 card.data.is_deleted = 1;
14050 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14051 //this.fileCollection.remove(card);
14052 //this.items = this.items.filter(function(e) { return e != card });
14053 // dont' really need ot update items.
14054 card.el.dom.parentNode.removeChild(card.el.dom);
14055 this.updateInput();
14061 this.fileCollection.each(function(card) {
14062 if (card.el.dom && card.el.dom.parentNode) {
14063 card.el.dom.parentNode.removeChild(card.el.dom);
14066 this.fileCollection.clear();
14067 this.updateInput();
14070 updateInput : function()
14073 this.fileCollection.each(function(e) {
14077 this.inputEl().dom.value = JSON.stringify(data);
14087 Roo.bootstrap.CardUploader.ID = -1;/*
14089 * Ext JS Library 1.1.1
14090 * Copyright(c) 2006-2007, Ext JS, LLC.
14092 * Originally Released Under LGPL - original licence link has changed is not relivant.
14095 * <script type="text/javascript">
14100 * @class Roo.data.SortTypes
14102 * Defines the default sorting (casting?) comparison functions used when sorting data.
14104 Roo.data.SortTypes = {
14106 * Default sort that does nothing
14107 * @param {Mixed} s The value being converted
14108 * @return {Mixed} The comparison value
14110 none : function(s){
14115 * The regular expression used to strip tags
14119 stripTagsRE : /<\/?[^>]+>/gi,
14122 * Strips all HTML tags to sort on text only
14123 * @param {Mixed} s The value being converted
14124 * @return {String} The comparison value
14126 asText : function(s){
14127 return String(s).replace(this.stripTagsRE, "");
14131 * Strips all HTML tags to sort on text only - Case insensitive
14132 * @param {Mixed} s The value being converted
14133 * @return {String} The comparison value
14135 asUCText : function(s){
14136 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14140 * Case insensitive string
14141 * @param {Mixed} s The value being converted
14142 * @return {String} The comparison value
14144 asUCString : function(s) {
14145 return String(s).toUpperCase();
14150 * @param {Mixed} s The value being converted
14151 * @return {Number} The comparison value
14153 asDate : function(s) {
14157 if(s instanceof Date){
14158 return s.getTime();
14160 return Date.parse(String(s));
14165 * @param {Mixed} s The value being converted
14166 * @return {Float} The comparison value
14168 asFloat : function(s) {
14169 var val = parseFloat(String(s).replace(/,/g, ""));
14178 * @param {Mixed} s The value being converted
14179 * @return {Number} The comparison value
14181 asInt : function(s) {
14182 var val = parseInt(String(s).replace(/,/g, ""));
14190 * Ext JS Library 1.1.1
14191 * Copyright(c) 2006-2007, Ext JS, LLC.
14193 * Originally Released Under LGPL - original licence link has changed is not relivant.
14196 * <script type="text/javascript">
14200 * @class Roo.data.Record
14201 * Instances of this class encapsulate both record <em>definition</em> information, and record
14202 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14203 * to access Records cached in an {@link Roo.data.Store} object.<br>
14205 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14206 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14209 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14211 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14212 * {@link #create}. The parameters are the same.
14213 * @param {Array} data An associative Array of data values keyed by the field name.
14214 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14215 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14216 * not specified an integer id is generated.
14218 Roo.data.Record = function(data, id){
14219 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14224 * Generate a constructor for a specific record layout.
14225 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14226 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14227 * Each field definition object may contain the following properties: <ul>
14228 * <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,
14229 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14230 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14231 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14232 * is being used, then this is a string containing the javascript expression to reference the data relative to
14233 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14234 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14235 * this may be omitted.</p></li>
14236 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14237 * <ul><li>auto (Default, implies no conversion)</li>
14242 * <li>date</li></ul></p></li>
14243 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14244 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14245 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14246 * by the Reader into an object that will be stored in the Record. It is passed the
14247 * following parameters:<ul>
14248 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14250 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14252 * <br>usage:<br><pre><code>
14253 var TopicRecord = Roo.data.Record.create(
14254 {name: 'title', mapping: 'topic_title'},
14255 {name: 'author', mapping: 'username'},
14256 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14257 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14258 {name: 'lastPoster', mapping: 'user2'},
14259 {name: 'excerpt', mapping: 'post_text'}
14262 var myNewRecord = new TopicRecord({
14263 title: 'Do my job please',
14266 lastPost: new Date(),
14267 lastPoster: 'Animal',
14268 excerpt: 'No way dude!'
14270 myStore.add(myNewRecord);
14275 Roo.data.Record.create = function(o){
14276 var f = function(){
14277 f.superclass.constructor.apply(this, arguments);
14279 Roo.extend(f, Roo.data.Record);
14280 var p = f.prototype;
14281 p.fields = new Roo.util.MixedCollection(false, function(field){
14284 for(var i = 0, len = o.length; i < len; i++){
14285 p.fields.add(new Roo.data.Field(o[i]));
14287 f.getField = function(name){
14288 return p.fields.get(name);
14293 Roo.data.Record.AUTO_ID = 1000;
14294 Roo.data.Record.EDIT = 'edit';
14295 Roo.data.Record.REJECT = 'reject';
14296 Roo.data.Record.COMMIT = 'commit';
14298 Roo.data.Record.prototype = {
14300 * Readonly flag - true if this record has been modified.
14309 join : function(store){
14310 this.store = store;
14314 * Set the named field to the specified value.
14315 * @param {String} name The name of the field to set.
14316 * @param {Object} value The value to set the field to.
14318 set : function(name, value){
14319 if(this.data[name] == value){
14323 if(!this.modified){
14324 this.modified = {};
14326 if(typeof this.modified[name] == 'undefined'){
14327 this.modified[name] = this.data[name];
14329 this.data[name] = value;
14330 if(!this.editing && this.store){
14331 this.store.afterEdit(this);
14336 * Get the value of the named field.
14337 * @param {String} name The name of the field to get the value of.
14338 * @return {Object} The value of the field.
14340 get : function(name){
14341 return this.data[name];
14345 beginEdit : function(){
14346 this.editing = true;
14347 this.modified = {};
14351 cancelEdit : function(){
14352 this.editing = false;
14353 delete this.modified;
14357 endEdit : function(){
14358 this.editing = false;
14359 if(this.dirty && this.store){
14360 this.store.afterEdit(this);
14365 * Usually called by the {@link Roo.data.Store} which owns the Record.
14366 * Rejects all changes made to the Record since either creation, or the last commit operation.
14367 * Modified fields are reverted to their original values.
14369 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14370 * of reject operations.
14372 reject : function(){
14373 var m = this.modified;
14375 if(typeof m[n] != "function"){
14376 this.data[n] = m[n];
14379 this.dirty = false;
14380 delete this.modified;
14381 this.editing = false;
14383 this.store.afterReject(this);
14388 * Usually called by the {@link Roo.data.Store} which owns the Record.
14389 * Commits all changes made to the Record since either creation, or the last commit operation.
14391 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14392 * of commit operations.
14394 commit : function(){
14395 this.dirty = false;
14396 delete this.modified;
14397 this.editing = false;
14399 this.store.afterCommit(this);
14404 hasError : function(){
14405 return this.error != null;
14409 clearError : function(){
14414 * Creates a copy of this record.
14415 * @param {String} id (optional) A new record id if you don't want to use this record's id
14418 copy : function(newId) {
14419 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14423 * Ext JS Library 1.1.1
14424 * Copyright(c) 2006-2007, Ext JS, LLC.
14426 * Originally Released Under LGPL - original licence link has changed is not relivant.
14429 * <script type="text/javascript">
14435 * @class Roo.data.Store
14436 * @extends Roo.util.Observable
14437 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14438 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14440 * 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
14441 * has no knowledge of the format of the data returned by the Proxy.<br>
14443 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14444 * instances from the data object. These records are cached and made available through accessor functions.
14446 * Creates a new Store.
14447 * @param {Object} config A config object containing the objects needed for the Store to access data,
14448 * and read the data into Records.
14450 Roo.data.Store = function(config){
14451 this.data = new Roo.util.MixedCollection(false);
14452 this.data.getKey = function(o){
14455 this.baseParams = {};
14457 this.paramNames = {
14462 "multisort" : "_multisort"
14465 if(config && config.data){
14466 this.inlineData = config.data;
14467 delete config.data;
14470 Roo.apply(this, config);
14472 if(this.reader){ // reader passed
14473 this.reader = Roo.factory(this.reader, Roo.data);
14474 this.reader.xmodule = this.xmodule || false;
14475 if(!this.recordType){
14476 this.recordType = this.reader.recordType;
14478 if(this.reader.onMetaChange){
14479 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14483 if(this.recordType){
14484 this.fields = this.recordType.prototype.fields;
14486 this.modified = [];
14490 * @event datachanged
14491 * Fires when the data cache has changed, and a widget which is using this Store
14492 * as a Record cache should refresh its view.
14493 * @param {Store} this
14495 datachanged : true,
14497 * @event metachange
14498 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14499 * @param {Store} this
14500 * @param {Object} meta The JSON metadata
14505 * Fires when Records have been added to the Store
14506 * @param {Store} this
14507 * @param {Roo.data.Record[]} records The array of Records added
14508 * @param {Number} index The index at which the record(s) were added
14513 * Fires when a Record has been removed from the Store
14514 * @param {Store} this
14515 * @param {Roo.data.Record} record The Record that was removed
14516 * @param {Number} index The index at which the record was removed
14521 * Fires when a Record has been updated
14522 * @param {Store} this
14523 * @param {Roo.data.Record} record The Record that was updated
14524 * @param {String} operation The update operation being performed. Value may be one of:
14526 Roo.data.Record.EDIT
14527 Roo.data.Record.REJECT
14528 Roo.data.Record.COMMIT
14534 * Fires when the data cache has been cleared.
14535 * @param {Store} this
14539 * @event beforeload
14540 * Fires before a request is made for a new data object. If the beforeload handler returns false
14541 * the load action will be canceled.
14542 * @param {Store} this
14543 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14547 * @event beforeloadadd
14548 * Fires after a new set of Records has been loaded.
14549 * @param {Store} this
14550 * @param {Roo.data.Record[]} records The Records that were loaded
14551 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14553 beforeloadadd : true,
14556 * Fires after a new set of Records has been loaded, before they are added to the store.
14557 * @param {Store} this
14558 * @param {Roo.data.Record[]} records The Records that were loaded
14559 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14560 * @params {Object} return from reader
14564 * @event loadexception
14565 * Fires if an exception occurs in the Proxy during loading.
14566 * Called with the signature of the Proxy's "loadexception" event.
14567 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14570 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14571 * @param {Object} load options
14572 * @param {Object} jsonData from your request (normally this contains the Exception)
14574 loadexception : true
14578 this.proxy = Roo.factory(this.proxy, Roo.data);
14579 this.proxy.xmodule = this.xmodule || false;
14580 this.relayEvents(this.proxy, ["loadexception"]);
14582 this.sortToggle = {};
14583 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14585 Roo.data.Store.superclass.constructor.call(this);
14587 if(this.inlineData){
14588 this.loadData(this.inlineData);
14589 delete this.inlineData;
14593 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14595 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14596 * without a remote query - used by combo/forms at present.
14600 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14603 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14606 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14607 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14610 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14611 * on any HTTP request
14614 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14617 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14621 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14622 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14624 remoteSort : false,
14627 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14628 * loaded or when a record is removed. (defaults to false).
14630 pruneModifiedRecords : false,
14633 lastOptions : null,
14636 * Add Records to the Store and fires the add event.
14637 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14639 add : function(records){
14640 records = [].concat(records);
14641 for(var i = 0, len = records.length; i < len; i++){
14642 records[i].join(this);
14644 var index = this.data.length;
14645 this.data.addAll(records);
14646 this.fireEvent("add", this, records, index);
14650 * Remove a Record from the Store and fires the remove event.
14651 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14653 remove : function(record){
14654 var index = this.data.indexOf(record);
14655 this.data.removeAt(index);
14657 if(this.pruneModifiedRecords){
14658 this.modified.remove(record);
14660 this.fireEvent("remove", this, record, index);
14664 * Remove all Records from the Store and fires the clear event.
14666 removeAll : function(){
14668 if(this.pruneModifiedRecords){
14669 this.modified = [];
14671 this.fireEvent("clear", this);
14675 * Inserts Records to the Store at the given index and fires the add event.
14676 * @param {Number} index The start index at which to insert the passed Records.
14677 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14679 insert : function(index, records){
14680 records = [].concat(records);
14681 for(var i = 0, len = records.length; i < len; i++){
14682 this.data.insert(index, records[i]);
14683 records[i].join(this);
14685 this.fireEvent("add", this, records, index);
14689 * Get the index within the cache of the passed Record.
14690 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14691 * @return {Number} The index of the passed Record. Returns -1 if not found.
14693 indexOf : function(record){
14694 return this.data.indexOf(record);
14698 * Get the index within the cache of the Record with the passed id.
14699 * @param {String} id The id of the Record to find.
14700 * @return {Number} The index of the Record. Returns -1 if not found.
14702 indexOfId : function(id){
14703 return this.data.indexOfKey(id);
14707 * Get the Record with the specified id.
14708 * @param {String} id The id of the Record to find.
14709 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14711 getById : function(id){
14712 return this.data.key(id);
14716 * Get the Record at the specified index.
14717 * @param {Number} index The index of the Record to find.
14718 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14720 getAt : function(index){
14721 return this.data.itemAt(index);
14725 * Returns a range of Records between specified indices.
14726 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14727 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14728 * @return {Roo.data.Record[]} An array of Records
14730 getRange : function(start, end){
14731 return this.data.getRange(start, end);
14735 storeOptions : function(o){
14736 o = Roo.apply({}, o);
14739 this.lastOptions = o;
14743 * Loads the Record cache from the configured Proxy using the configured Reader.
14745 * If using remote paging, then the first load call must specify the <em>start</em>
14746 * and <em>limit</em> properties in the options.params property to establish the initial
14747 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14749 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14750 * and this call will return before the new data has been loaded. Perform any post-processing
14751 * in a callback function, or in a "load" event handler.</strong>
14753 * @param {Object} options An object containing properties which control loading options:<ul>
14754 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14755 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14756 * passed the following arguments:<ul>
14757 * <li>r : Roo.data.Record[]</li>
14758 * <li>options: Options object from the load call</li>
14759 * <li>success: Boolean success indicator</li></ul></li>
14760 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14761 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14764 load : function(options){
14765 options = options || {};
14766 if(this.fireEvent("beforeload", this, options) !== false){
14767 this.storeOptions(options);
14768 var p = Roo.apply(options.params || {}, this.baseParams);
14769 // if meta was not loaded from remote source.. try requesting it.
14770 if (!this.reader.metaFromRemote) {
14771 p._requestMeta = 1;
14773 if(this.sortInfo && this.remoteSort){
14774 var pn = this.paramNames;
14775 p[pn["sort"]] = this.sortInfo.field;
14776 p[pn["dir"]] = this.sortInfo.direction;
14778 if (this.multiSort) {
14779 var pn = this.paramNames;
14780 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14783 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14788 * Reloads the Record cache from the configured Proxy using the configured Reader and
14789 * the options from the last load operation performed.
14790 * @param {Object} options (optional) An object containing properties which may override the options
14791 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14792 * the most recently used options are reused).
14794 reload : function(options){
14795 this.load(Roo.applyIf(options||{}, this.lastOptions));
14799 // Called as a callback by the Reader during a load operation.
14800 loadRecords : function(o, options, success){
14801 if(!o || success === false){
14802 if(success !== false){
14803 this.fireEvent("load", this, [], options, o);
14805 if(options.callback){
14806 options.callback.call(options.scope || this, [], options, false);
14810 // if data returned failure - throw an exception.
14811 if (o.success === false) {
14812 // show a message if no listener is registered.
14813 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14814 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14816 // loadmask wil be hooked into this..
14817 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14820 var r = o.records, t = o.totalRecords || r.length;
14822 this.fireEvent("beforeloadadd", this, r, options, o);
14824 if(!options || options.add !== true){
14825 if(this.pruneModifiedRecords){
14826 this.modified = [];
14828 for(var i = 0, len = r.length; i < len; i++){
14832 this.data = this.snapshot;
14833 delete this.snapshot;
14836 this.data.addAll(r);
14837 this.totalLength = t;
14839 this.fireEvent("datachanged", this);
14841 this.totalLength = Math.max(t, this.data.length+r.length);
14845 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14847 var e = new Roo.data.Record({});
14849 e.set(this.parent.displayField, this.parent.emptyTitle);
14850 e.set(this.parent.valueField, '');
14855 this.fireEvent("load", this, r, options, o);
14856 if(options.callback){
14857 options.callback.call(options.scope || this, r, options, true);
14863 * Loads data from a passed data block. A Reader which understands the format of the data
14864 * must have been configured in the constructor.
14865 * @param {Object} data The data block from which to read the Records. The format of the data expected
14866 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14867 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14869 loadData : function(o, append){
14870 var r = this.reader.readRecords(o);
14871 this.loadRecords(r, {add: append}, true);
14875 * using 'cn' the nested child reader read the child array into it's child stores.
14876 * @param {Object} rec The record with a 'children array
14878 loadDataFromChildren : function(rec)
14880 this.loadData(this.reader.toLoadData(rec));
14885 * Gets the number of cached records.
14887 * <em>If using paging, this may not be the total size of the dataset. If the data object
14888 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14889 * the data set size</em>
14891 getCount : function(){
14892 return this.data.length || 0;
14896 * Gets the total number of records in the dataset as returned by the server.
14898 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14899 * the dataset size</em>
14901 getTotalCount : function(){
14902 return this.totalLength || 0;
14906 * Returns the sort state of the Store as an object with two properties:
14908 field {String} The name of the field by which the Records are sorted
14909 direction {String} The sort order, "ASC" or "DESC"
14912 getSortState : function(){
14913 return this.sortInfo;
14917 applySort : function(){
14918 if(this.sortInfo && !this.remoteSort){
14919 var s = this.sortInfo, f = s.field;
14920 var st = this.fields.get(f).sortType;
14921 var fn = function(r1, r2){
14922 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14923 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14925 this.data.sort(s.direction, fn);
14926 if(this.snapshot && this.snapshot != this.data){
14927 this.snapshot.sort(s.direction, fn);
14933 * Sets the default sort column and order to be used by the next load operation.
14934 * @param {String} fieldName The name of the field to sort by.
14935 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14937 setDefaultSort : function(field, dir){
14938 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14942 * Sort the Records.
14943 * If remote sorting is used, the sort is performed on the server, and the cache is
14944 * reloaded. If local sorting is used, the cache is sorted internally.
14945 * @param {String} fieldName The name of the field to sort by.
14946 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14948 sort : function(fieldName, dir){
14949 var f = this.fields.get(fieldName);
14951 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14953 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14954 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14959 this.sortToggle[f.name] = dir;
14960 this.sortInfo = {field: f.name, direction: dir};
14961 if(!this.remoteSort){
14963 this.fireEvent("datachanged", this);
14965 this.load(this.lastOptions);
14970 * Calls the specified function for each of the Records in the cache.
14971 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14972 * Returning <em>false</em> aborts and exits the iteration.
14973 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14975 each : function(fn, scope){
14976 this.data.each(fn, scope);
14980 * Gets all records modified since the last commit. Modified records are persisted across load operations
14981 * (e.g., during paging).
14982 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14984 getModifiedRecords : function(){
14985 return this.modified;
14989 createFilterFn : function(property, value, anyMatch){
14990 if(!value.exec){ // not a regex
14991 value = String(value);
14992 if(value.length == 0){
14995 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14997 return function(r){
14998 return value.test(r.data[property]);
15003 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15004 * @param {String} property A field on your records
15005 * @param {Number} start The record index to start at (defaults to 0)
15006 * @param {Number} end The last record index to include (defaults to length - 1)
15007 * @return {Number} The sum
15009 sum : function(property, start, end){
15010 var rs = this.data.items, v = 0;
15011 start = start || 0;
15012 end = (end || end === 0) ? end : rs.length-1;
15014 for(var i = start; i <= end; i++){
15015 v += (rs[i].data[property] || 0);
15021 * Filter the records by a specified property.
15022 * @param {String} field A field on your records
15023 * @param {String/RegExp} value Either a string that the field
15024 * should start with or a RegExp to test against the field
15025 * @param {Boolean} anyMatch True to match any part not just the beginning
15027 filter : function(property, value, anyMatch){
15028 var fn = this.createFilterFn(property, value, anyMatch);
15029 return fn ? this.filterBy(fn) : this.clearFilter();
15033 * Filter by a function. The specified function will be called with each
15034 * record in this data source. If the function returns true the record is included,
15035 * otherwise it is filtered.
15036 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15037 * @param {Object} scope (optional) The scope of the function (defaults to this)
15039 filterBy : function(fn, scope){
15040 this.snapshot = this.snapshot || this.data;
15041 this.data = this.queryBy(fn, scope||this);
15042 this.fireEvent("datachanged", this);
15046 * Query the records by a specified property.
15047 * @param {String} field A field on your records
15048 * @param {String/RegExp} value Either a string that the field
15049 * should start with or a RegExp to test against the field
15050 * @param {Boolean} anyMatch True to match any part not just the beginning
15051 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15053 query : function(property, value, anyMatch){
15054 var fn = this.createFilterFn(property, value, anyMatch);
15055 return fn ? this.queryBy(fn) : this.data.clone();
15059 * Query by a function. The specified function will be called with each
15060 * record in this data source. If the function returns true the record is included
15062 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15063 * @param {Object} scope (optional) The scope of the function (defaults to this)
15064 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15066 queryBy : function(fn, scope){
15067 var data = this.snapshot || this.data;
15068 return data.filterBy(fn, scope||this);
15072 * Collects unique values for a particular dataIndex from this store.
15073 * @param {String} dataIndex The property to collect
15074 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15075 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15076 * @return {Array} An array of the unique values
15078 collect : function(dataIndex, allowNull, bypassFilter){
15079 var d = (bypassFilter === true && this.snapshot) ?
15080 this.snapshot.items : this.data.items;
15081 var v, sv, r = [], l = {};
15082 for(var i = 0, len = d.length; i < len; i++){
15083 v = d[i].data[dataIndex];
15085 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15094 * Revert to a view of the Record cache with no filtering applied.
15095 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15097 clearFilter : function(suppressEvent){
15098 if(this.snapshot && this.snapshot != this.data){
15099 this.data = this.snapshot;
15100 delete this.snapshot;
15101 if(suppressEvent !== true){
15102 this.fireEvent("datachanged", this);
15108 afterEdit : function(record){
15109 if(this.modified.indexOf(record) == -1){
15110 this.modified.push(record);
15112 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15116 afterReject : function(record){
15117 this.modified.remove(record);
15118 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15122 afterCommit : function(record){
15123 this.modified.remove(record);
15124 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15128 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15129 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15131 commitChanges : function(){
15132 var m = this.modified.slice(0);
15133 this.modified = [];
15134 for(var i = 0, len = m.length; i < len; i++){
15140 * Cancel outstanding changes on all changed records.
15142 rejectChanges : function(){
15143 var m = this.modified.slice(0);
15144 this.modified = [];
15145 for(var i = 0, len = m.length; i < len; i++){
15150 onMetaChange : function(meta, rtype, o){
15151 this.recordType = rtype;
15152 this.fields = rtype.prototype.fields;
15153 delete this.snapshot;
15154 this.sortInfo = meta.sortInfo || this.sortInfo;
15155 this.modified = [];
15156 this.fireEvent('metachange', this, this.reader.meta);
15159 moveIndex : function(data, type)
15161 var index = this.indexOf(data);
15163 var newIndex = index + type;
15167 this.insert(newIndex, data);
15172 * Ext JS Library 1.1.1
15173 * Copyright(c) 2006-2007, Ext JS, LLC.
15175 * Originally Released Under LGPL - original licence link has changed is not relivant.
15178 * <script type="text/javascript">
15182 * @class Roo.data.SimpleStore
15183 * @extends Roo.data.Store
15184 * Small helper class to make creating Stores from Array data easier.
15185 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15186 * @cfg {Array} fields An array of field definition objects, or field name strings.
15187 * @cfg {Object} an existing reader (eg. copied from another store)
15188 * @cfg {Array} data The multi-dimensional array of data
15190 * @param {Object} config
15192 Roo.data.SimpleStore = function(config)
15194 Roo.data.SimpleStore.superclass.constructor.call(this, {
15196 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15199 Roo.data.Record.create(config.fields)
15201 proxy : new Roo.data.MemoryProxy(config.data)
15205 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15207 * Ext JS Library 1.1.1
15208 * Copyright(c) 2006-2007, Ext JS, LLC.
15210 * Originally Released Under LGPL - original licence link has changed is not relivant.
15213 * <script type="text/javascript">
15218 * @extends Roo.data.Store
15219 * @class Roo.data.JsonStore
15220 * Small helper class to make creating Stores for JSON data easier. <br/>
15222 var store = new Roo.data.JsonStore({
15223 url: 'get-images.php',
15225 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15228 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15229 * JsonReader and HttpProxy (unless inline data is provided).</b>
15230 * @cfg {Array} fields An array of field definition objects, or field name strings.
15232 * @param {Object} config
15234 Roo.data.JsonStore = function(c){
15235 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15236 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15237 reader: new Roo.data.JsonReader(c, c.fields)
15240 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15242 * Ext JS Library 1.1.1
15243 * Copyright(c) 2006-2007, Ext JS, LLC.
15245 * Originally Released Under LGPL - original licence link has changed is not relivant.
15248 * <script type="text/javascript">
15252 Roo.data.Field = function(config){
15253 if(typeof config == "string"){
15254 config = {name: config};
15256 Roo.apply(this, config);
15259 this.type = "auto";
15262 var st = Roo.data.SortTypes;
15263 // named sortTypes are supported, here we look them up
15264 if(typeof this.sortType == "string"){
15265 this.sortType = st[this.sortType];
15268 // set default sortType for strings and dates
15269 if(!this.sortType){
15272 this.sortType = st.asUCString;
15275 this.sortType = st.asDate;
15278 this.sortType = st.none;
15283 var stripRe = /[\$,%]/g;
15285 // prebuilt conversion function for this field, instead of
15286 // switching every time we're reading a value
15288 var cv, dateFormat = this.dateFormat;
15293 cv = function(v){ return v; };
15296 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15300 return v !== undefined && v !== null && v !== '' ?
15301 parseInt(String(v).replace(stripRe, ""), 10) : '';
15306 return v !== undefined && v !== null && v !== '' ?
15307 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15312 cv = function(v){ return v === true || v === "true" || v == 1; };
15319 if(v instanceof Date){
15323 if(dateFormat == "timestamp"){
15324 return new Date(v*1000);
15326 return Date.parseDate(v, dateFormat);
15328 var parsed = Date.parse(v);
15329 return parsed ? new Date(parsed) : null;
15338 Roo.data.Field.prototype = {
15346 * Ext JS Library 1.1.1
15347 * Copyright(c) 2006-2007, Ext JS, LLC.
15349 * Originally Released Under LGPL - original licence link has changed is not relivant.
15352 * <script type="text/javascript">
15355 // Base class for reading structured data from a data source. This class is intended to be
15356 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15359 * @class Roo.data.DataReader
15360 * Base class for reading structured data from a data source. This class is intended to be
15361 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15364 Roo.data.DataReader = function(meta, recordType){
15368 this.recordType = recordType instanceof Array ?
15369 Roo.data.Record.create(recordType) : recordType;
15372 Roo.data.DataReader.prototype = {
15375 readerType : 'Data',
15377 * Create an empty record
15378 * @param {Object} data (optional) - overlay some values
15379 * @return {Roo.data.Record} record created.
15381 newRow : function(d) {
15383 this.recordType.prototype.fields.each(function(c) {
15385 case 'int' : da[c.name] = 0; break;
15386 case 'date' : da[c.name] = new Date(); break;
15387 case 'float' : da[c.name] = 0.0; break;
15388 case 'boolean' : da[c.name] = false; break;
15389 default : da[c.name] = ""; break;
15393 return new this.recordType(Roo.apply(da, d));
15399 * Ext JS Library 1.1.1
15400 * Copyright(c) 2006-2007, Ext JS, LLC.
15402 * Originally Released Under LGPL - original licence link has changed is not relivant.
15405 * <script type="text/javascript">
15409 * @class Roo.data.DataProxy
15410 * @extends Roo.data.Observable
15411 * This class is an abstract base class for implementations which provide retrieval of
15412 * unformatted data objects.<br>
15414 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15415 * (of the appropriate type which knows how to parse the data object) to provide a block of
15416 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15418 * Custom implementations must implement the load method as described in
15419 * {@link Roo.data.HttpProxy#load}.
15421 Roo.data.DataProxy = function(){
15424 * @event beforeload
15425 * Fires before a network request is made to retrieve a data object.
15426 * @param {Object} This DataProxy object.
15427 * @param {Object} params The params parameter to the load function.
15432 * Fires before the load method's callback is called.
15433 * @param {Object} This DataProxy object.
15434 * @param {Object} o The data object.
15435 * @param {Object} arg The callback argument object passed to the load function.
15439 * @event loadexception
15440 * Fires if an Exception occurs during data retrieval.
15441 * @param {Object} This DataProxy object.
15442 * @param {Object} o The data object.
15443 * @param {Object} arg The callback argument object passed to the load function.
15444 * @param {Object} e The Exception.
15446 loadexception : true
15448 Roo.data.DataProxy.superclass.constructor.call(this);
15451 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15454 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15458 * Ext JS Library 1.1.1
15459 * Copyright(c) 2006-2007, Ext JS, LLC.
15461 * Originally Released Under LGPL - original licence link has changed is not relivant.
15464 * <script type="text/javascript">
15467 * @class Roo.data.MemoryProxy
15468 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15469 * to the Reader when its load method is called.
15471 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15473 Roo.data.MemoryProxy = function(data){
15477 Roo.data.MemoryProxy.superclass.constructor.call(this);
15481 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15484 * Load data from the requested source (in this case an in-memory
15485 * data object passed to the constructor), read the data object into
15486 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15487 * process that block using the passed callback.
15488 * @param {Object} params This parameter is not used by the MemoryProxy class.
15489 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15490 * object into a block of Roo.data.Records.
15491 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15492 * The function must be passed <ul>
15493 * <li>The Record block object</li>
15494 * <li>The "arg" argument from the load function</li>
15495 * <li>A boolean success indicator</li>
15497 * @param {Object} scope The scope in which to call the callback
15498 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15500 load : function(params, reader, callback, scope, arg){
15501 params = params || {};
15504 result = reader.readRecords(params.data ? params.data :this.data);
15506 this.fireEvent("loadexception", this, arg, null, e);
15507 callback.call(scope, null, arg, false);
15510 callback.call(scope, result, arg, true);
15514 update : function(params, records){
15519 * Ext JS Library 1.1.1
15520 * Copyright(c) 2006-2007, Ext JS, LLC.
15522 * Originally Released Under LGPL - original licence link has changed is not relivant.
15525 * <script type="text/javascript">
15528 * @class Roo.data.HttpProxy
15529 * @extends Roo.data.DataProxy
15530 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15531 * configured to reference a certain URL.<br><br>
15533 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15534 * from which the running page was served.<br><br>
15536 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15538 * Be aware that to enable the browser to parse an XML document, the server must set
15539 * the Content-Type header in the HTTP response to "text/xml".
15541 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15542 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15543 * will be used to make the request.
15545 Roo.data.HttpProxy = function(conn){
15546 Roo.data.HttpProxy.superclass.constructor.call(this);
15547 // is conn a conn config or a real conn?
15549 this.useAjax = !conn || !conn.events;
15553 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15554 // thse are take from connection...
15557 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15560 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15561 * extra parameters to each request made by this object. (defaults to undefined)
15564 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15565 * to each request made by this object. (defaults to undefined)
15568 * @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)
15571 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15574 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15580 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15584 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15585 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15586 * a finer-grained basis than the DataProxy events.
15588 getConnection : function(){
15589 return this.useAjax ? Roo.Ajax : this.conn;
15593 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15594 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15595 * process that block using the passed callback.
15596 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15597 * for the request to the remote server.
15598 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15599 * object into a block of Roo.data.Records.
15600 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15601 * The function must be passed <ul>
15602 * <li>The Record block object</li>
15603 * <li>The "arg" argument from the load function</li>
15604 * <li>A boolean success indicator</li>
15606 * @param {Object} scope The scope in which to call the callback
15607 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15609 load : function(params, reader, callback, scope, arg){
15610 if(this.fireEvent("beforeload", this, params) !== false){
15612 params : params || {},
15614 callback : callback,
15619 callback : this.loadResponse,
15623 Roo.applyIf(o, this.conn);
15624 if(this.activeRequest){
15625 Roo.Ajax.abort(this.activeRequest);
15627 this.activeRequest = Roo.Ajax.request(o);
15629 this.conn.request(o);
15632 callback.call(scope||this, null, arg, false);
15637 loadResponse : function(o, success, response){
15638 delete this.activeRequest;
15640 this.fireEvent("loadexception", this, o, response);
15641 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15646 result = o.reader.read(response);
15648 this.fireEvent("loadexception", this, o, response, e);
15649 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15653 this.fireEvent("load", this, o, o.request.arg);
15654 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15658 update : function(dataSet){
15663 updateResponse : function(dataSet){
15668 * Ext JS Library 1.1.1
15669 * Copyright(c) 2006-2007, Ext JS, LLC.
15671 * Originally Released Under LGPL - original licence link has changed is not relivant.
15674 * <script type="text/javascript">
15678 * @class Roo.data.ScriptTagProxy
15679 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15680 * other than the originating domain of the running page.<br><br>
15682 * <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
15683 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15685 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15686 * source code that is used as the source inside a <script> tag.<br><br>
15688 * In order for the browser to process the returned data, the server must wrap the data object
15689 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15690 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15691 * depending on whether the callback name was passed:
15694 boolean scriptTag = false;
15695 String cb = request.getParameter("callback");
15698 response.setContentType("text/javascript");
15700 response.setContentType("application/x-json");
15702 Writer out = response.getWriter();
15704 out.write(cb + "(");
15706 out.print(dataBlock.toJsonString());
15713 * @param {Object} config A configuration object.
15715 Roo.data.ScriptTagProxy = function(config){
15716 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15717 Roo.apply(this, config);
15718 this.head = document.getElementsByTagName("head")[0];
15721 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15723 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15725 * @cfg {String} url The URL from which to request the data object.
15728 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15732 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15733 * the server the name of the callback function set up by the load call to process the returned data object.
15734 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15735 * javascript output which calls this named function passing the data object as its only parameter.
15737 callbackParam : "callback",
15739 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15740 * name to the request.
15745 * Load data from the configured URL, read the data object into
15746 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15747 * process that block using the passed callback.
15748 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15749 * for the request to the remote server.
15750 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15751 * object into a block of Roo.data.Records.
15752 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15753 * The function must be passed <ul>
15754 * <li>The Record block object</li>
15755 * <li>The "arg" argument from the load function</li>
15756 * <li>A boolean success indicator</li>
15758 * @param {Object} scope The scope in which to call the callback
15759 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15761 load : function(params, reader, callback, scope, arg){
15762 if(this.fireEvent("beforeload", this, params) !== false){
15764 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15766 var url = this.url;
15767 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15769 url += "&_dc=" + (new Date().getTime());
15771 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15774 cb : "stcCallback"+transId,
15775 scriptId : "stcScript"+transId,
15779 callback : callback,
15785 window[trans.cb] = function(o){
15786 conn.handleResponse(o, trans);
15789 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15791 if(this.autoAbort !== false){
15795 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15797 var script = document.createElement("script");
15798 script.setAttribute("src", url);
15799 script.setAttribute("type", "text/javascript");
15800 script.setAttribute("id", trans.scriptId);
15801 this.head.appendChild(script);
15803 this.trans = trans;
15805 callback.call(scope||this, null, arg, false);
15810 isLoading : function(){
15811 return this.trans ? true : false;
15815 * Abort the current server request.
15817 abort : function(){
15818 if(this.isLoading()){
15819 this.destroyTrans(this.trans);
15824 destroyTrans : function(trans, isLoaded){
15825 this.head.removeChild(document.getElementById(trans.scriptId));
15826 clearTimeout(trans.timeoutId);
15828 window[trans.cb] = undefined;
15830 delete window[trans.cb];
15833 // if hasn't been loaded, wait for load to remove it to prevent script error
15834 window[trans.cb] = function(){
15835 window[trans.cb] = undefined;
15837 delete window[trans.cb];
15844 handleResponse : function(o, trans){
15845 this.trans = false;
15846 this.destroyTrans(trans, true);
15849 result = trans.reader.readRecords(o);
15851 this.fireEvent("loadexception", this, o, trans.arg, e);
15852 trans.callback.call(trans.scope||window, null, trans.arg, false);
15855 this.fireEvent("load", this, o, trans.arg);
15856 trans.callback.call(trans.scope||window, result, trans.arg, true);
15860 handleFailure : function(trans){
15861 this.trans = false;
15862 this.destroyTrans(trans, false);
15863 this.fireEvent("loadexception", this, null, trans.arg);
15864 trans.callback.call(trans.scope||window, null, trans.arg, false);
15868 * Ext JS Library 1.1.1
15869 * Copyright(c) 2006-2007, Ext JS, LLC.
15871 * Originally Released Under LGPL - original licence link has changed is not relivant.
15874 * <script type="text/javascript">
15878 * @class Roo.data.JsonReader
15879 * @extends Roo.data.DataReader
15880 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15881 * based on mappings in a provided Roo.data.Record constructor.
15883 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15884 * in the reply previously.
15889 var RecordDef = Roo.data.Record.create([
15890 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15891 {name: 'occupation'} // This field will use "occupation" as the mapping.
15893 var myReader = new Roo.data.JsonReader({
15894 totalProperty: "results", // The property which contains the total dataset size (optional)
15895 root: "rows", // The property which contains an Array of row objects
15896 id: "id" // The property within each row object that provides an ID for the record (optional)
15900 * This would consume a JSON file like this:
15902 { 'results': 2, 'rows': [
15903 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15904 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15907 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15908 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15909 * paged from the remote server.
15910 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15911 * @cfg {String} root name of the property which contains the Array of row objects.
15912 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15913 * @cfg {Array} fields Array of field definition objects
15915 * Create a new JsonReader
15916 * @param {Object} meta Metadata configuration options
15917 * @param {Object} recordType Either an Array of field definition objects,
15918 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15920 Roo.data.JsonReader = function(meta, recordType){
15923 // set some defaults:
15924 Roo.applyIf(meta, {
15925 totalProperty: 'total',
15926 successProperty : 'success',
15931 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15933 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15935 readerType : 'Json',
15938 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15939 * Used by Store query builder to append _requestMeta to params.
15942 metaFromRemote : false,
15944 * This method is only used by a DataProxy which has retrieved data from a remote server.
15945 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15946 * @return {Object} data A data block which is used by an Roo.data.Store object as
15947 * a cache of Roo.data.Records.
15949 read : function(response){
15950 var json = response.responseText;
15952 var o = /* eval:var:o */ eval("("+json+")");
15954 throw {message: "JsonReader.read: Json object not found"};
15960 this.metaFromRemote = true;
15961 this.meta = o.metaData;
15962 this.recordType = Roo.data.Record.create(o.metaData.fields);
15963 this.onMetaChange(this.meta, this.recordType, o);
15965 return this.readRecords(o);
15968 // private function a store will implement
15969 onMetaChange : function(meta, recordType, o){
15976 simpleAccess: function(obj, subsc) {
15983 getJsonAccessor: function(){
15985 return function(expr) {
15987 return(re.test(expr))
15988 ? new Function("obj", "return obj." + expr)
15993 return Roo.emptyFn;
15998 * Create a data block containing Roo.data.Records from an XML document.
15999 * @param {Object} o An object which contains an Array of row objects in the property specified
16000 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16001 * which contains the total size of the dataset.
16002 * @return {Object} data A data block which is used by an Roo.data.Store object as
16003 * a cache of Roo.data.Records.
16005 readRecords : function(o){
16007 * After any data loads, the raw JSON data is available for further custom processing.
16011 var s = this.meta, Record = this.recordType,
16012 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16014 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16016 if(s.totalProperty) {
16017 this.getTotal = this.getJsonAccessor(s.totalProperty);
16019 if(s.successProperty) {
16020 this.getSuccess = this.getJsonAccessor(s.successProperty);
16022 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16024 var g = this.getJsonAccessor(s.id);
16025 this.getId = function(rec) {
16027 return (r === undefined || r === "") ? null : r;
16030 this.getId = function(){return null;};
16033 for(var jj = 0; jj < fl; jj++){
16035 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16036 this.ef[jj] = this.getJsonAccessor(map);
16040 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16041 if(s.totalProperty){
16042 var vt = parseInt(this.getTotal(o), 10);
16047 if(s.successProperty){
16048 var vs = this.getSuccess(o);
16049 if(vs === false || vs === 'false'){
16054 for(var i = 0; i < c; i++){
16057 var id = this.getId(n);
16058 for(var j = 0; j < fl; j++){
16060 var v = this.ef[j](n);
16062 Roo.log('missing convert for ' + f.name);
16066 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16068 var record = new Record(values, id);
16070 records[i] = record;
16076 totalRecords : totalRecords
16079 // used when loading children.. @see loadDataFromChildren
16080 toLoadData: function(rec)
16082 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16083 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16084 return { data : data, total : data.length };
16089 * Ext JS Library 1.1.1
16090 * Copyright(c) 2006-2007, Ext JS, LLC.
16092 * Originally Released Under LGPL - original licence link has changed is not relivant.
16095 * <script type="text/javascript">
16099 * @class Roo.data.ArrayReader
16100 * @extends Roo.data.DataReader
16101 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16102 * Each element of that Array represents a row of data fields. The
16103 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16104 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16108 var RecordDef = Roo.data.Record.create([
16109 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16110 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16112 var myReader = new Roo.data.ArrayReader({
16113 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16117 * This would consume an Array like this:
16119 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16123 * Create a new JsonReader
16124 * @param {Object} meta Metadata configuration options.
16125 * @param {Object|Array} recordType Either an Array of field definition objects
16127 * @cfg {Array} fields Array of field definition objects
16128 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16129 * as specified to {@link Roo.data.Record#create},
16130 * or an {@link Roo.data.Record} object
16133 * created using {@link Roo.data.Record#create}.
16135 Roo.data.ArrayReader = function(meta, recordType)
16137 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16140 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16143 * Create a data block containing Roo.data.Records from an XML document.
16144 * @param {Object} o An Array of row objects which represents the dataset.
16145 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16146 * a cache of Roo.data.Records.
16148 readRecords : function(o)
16150 var sid = this.meta ? this.meta.id : null;
16151 var recordType = this.recordType, fields = recordType.prototype.fields;
16154 for(var i = 0; i < root.length; i++){
16157 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16158 for(var j = 0, jlen = fields.length; j < jlen; j++){
16159 var f = fields.items[j];
16160 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16161 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16163 values[f.name] = v;
16165 var record = new recordType(values, id);
16167 records[records.length] = record;
16171 totalRecords : records.length
16174 // used when loading children.. @see loadDataFromChildren
16175 toLoadData: function(rec)
16177 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16178 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16189 * @class Roo.bootstrap.ComboBox
16190 * @extends Roo.bootstrap.TriggerField
16191 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16192 * @cfg {Boolean} append (true|false) default false
16193 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16194 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16195 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16196 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16197 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16198 * @cfg {Boolean} animate default true
16199 * @cfg {Boolean} emptyResultText only for touch device
16200 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16201 * @cfg {String} emptyTitle default ''
16202 * @cfg {Number} width fixed with? experimental
16204 * Create a new ComboBox.
16205 * @param {Object} config Configuration options
16207 Roo.bootstrap.ComboBox = function(config){
16208 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16212 * Fires when the dropdown list is expanded
16213 * @param {Roo.bootstrap.ComboBox} combo This combo box
16218 * Fires when the dropdown list is collapsed
16219 * @param {Roo.bootstrap.ComboBox} combo This combo box
16223 * @event beforeselect
16224 * Fires before a list item is selected. Return false to cancel the selection.
16225 * @param {Roo.bootstrap.ComboBox} combo This combo box
16226 * @param {Roo.data.Record} record The data record returned from the underlying store
16227 * @param {Number} index The index of the selected item in the dropdown list
16229 'beforeselect' : true,
16232 * Fires when a list item is selected
16233 * @param {Roo.bootstrap.ComboBox} combo This combo box
16234 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16235 * @param {Number} index The index of the selected item in the dropdown list
16239 * @event beforequery
16240 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16241 * The event object passed has these properties:
16242 * @param {Roo.bootstrap.ComboBox} combo This combo box
16243 * @param {String} query The query
16244 * @param {Boolean} forceAll true to force "all" query
16245 * @param {Boolean} cancel true to cancel the query
16246 * @param {Object} e The query event object
16248 'beforequery': true,
16251 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16252 * @param {Roo.bootstrap.ComboBox} combo This combo box
16257 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16258 * @param {Roo.bootstrap.ComboBox} combo This combo box
16259 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16264 * Fires when the remove value from the combobox array
16265 * @param {Roo.bootstrap.ComboBox} combo This combo box
16269 * @event afterremove
16270 * Fires when the remove value from the combobox array
16271 * @param {Roo.bootstrap.ComboBox} combo This combo box
16273 'afterremove' : true,
16275 * @event specialfilter
16276 * Fires when specialfilter
16277 * @param {Roo.bootstrap.ComboBox} combo This combo box
16279 'specialfilter' : true,
16282 * Fires when tick the element
16283 * @param {Roo.bootstrap.ComboBox} combo This combo box
16287 * @event touchviewdisplay
16288 * Fires when touch view require special display (default is using displayField)
16289 * @param {Roo.bootstrap.ComboBox} combo This combo box
16290 * @param {Object} cfg set html .
16292 'touchviewdisplay' : true
16297 this.tickItems = [];
16299 this.selectedIndex = -1;
16300 if(this.mode == 'local'){
16301 if(config.queryDelay === undefined){
16302 this.queryDelay = 10;
16304 if(config.minChars === undefined){
16310 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16313 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16314 * rendering into an Roo.Editor, defaults to false)
16317 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16318 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16321 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16324 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16325 * the dropdown list (defaults to undefined, with no header element)
16329 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16333 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16335 listWidth: undefined,
16337 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16338 * mode = 'remote' or 'text' if mode = 'local')
16340 displayField: undefined,
16343 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16344 * mode = 'remote' or 'value' if mode = 'local').
16345 * Note: use of a valueField requires the user make a selection
16346 * in order for a value to be mapped.
16348 valueField: undefined,
16350 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16355 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16356 * field's data value (defaults to the underlying DOM element's name)
16358 hiddenName: undefined,
16360 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16364 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16366 selectedClass: 'active',
16369 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16373 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16374 * anchor positions (defaults to 'tl-bl')
16376 listAlign: 'tl-bl?',
16378 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16382 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16383 * query specified by the allQuery config option (defaults to 'query')
16385 triggerAction: 'query',
16387 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16388 * (defaults to 4, does not apply if editable = false)
16392 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16393 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16397 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16398 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16402 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16403 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16407 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16408 * when editable = true (defaults to false)
16410 selectOnFocus:false,
16412 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16414 queryParam: 'query',
16416 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16417 * when mode = 'remote' (defaults to 'Loading...')
16419 loadingText: 'Loading...',
16421 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16425 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16429 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16430 * traditional select (defaults to true)
16434 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16438 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16442 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16443 * listWidth has a higher value)
16447 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16448 * allow the user to set arbitrary text into the field (defaults to false)
16450 forceSelection:false,
16452 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16453 * if typeAhead = true (defaults to 250)
16455 typeAheadDelay : 250,
16457 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16458 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16460 valueNotFoundText : undefined,
16462 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16464 blockFocus : false,
16467 * @cfg {Boolean} disableClear Disable showing of clear button.
16469 disableClear : false,
16471 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16473 alwaysQuery : false,
16476 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16481 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16483 invalidClass : "has-warning",
16486 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16488 validClass : "has-success",
16491 * @cfg {Boolean} specialFilter (true|false) special filter default false
16493 specialFilter : false,
16496 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16498 mobileTouchView : true,
16501 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16503 useNativeIOS : false,
16506 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16508 mobile_restrict_height : false,
16510 ios_options : false,
16522 btnPosition : 'right',
16523 triggerList : true,
16524 showToggleBtn : true,
16526 emptyResultText: 'Empty',
16527 triggerText : 'Select',
16531 // element that contains real text value.. (when hidden is used..)
16533 getAutoCreate : function()
16538 * Render classic select for iso
16541 if(Roo.isIOS && this.useNativeIOS){
16542 cfg = this.getAutoCreateNativeIOS();
16550 if(Roo.isTouch && this.mobileTouchView){
16551 cfg = this.getAutoCreateTouchView();
16558 if(!this.tickable){
16559 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16564 * ComboBox with tickable selections
16567 var align = this.labelAlign || this.parentLabelAlign();
16570 cls : 'form-group roo-combobox-tickable' //input-group
16573 var btn_text_select = '';
16574 var btn_text_done = '';
16575 var btn_text_cancel = '';
16577 if (this.btn_text_show) {
16578 btn_text_select = 'Select';
16579 btn_text_done = 'Done';
16580 btn_text_cancel = 'Cancel';
16585 cls : 'tickable-buttons',
16590 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16591 //html : this.triggerText
16592 html: btn_text_select
16598 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16600 html: btn_text_done
16606 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16608 html: btn_text_cancel
16614 buttons.cn.unshift({
16616 cls: 'roo-select2-search-field-input'
16622 Roo.each(buttons.cn, function(c){
16624 c.cls += ' btn-' + _this.size;
16627 if (_this.disabled) {
16634 style : 'display: contents',
16639 cls: 'form-hidden-field'
16643 cls: 'roo-select2-choices',
16647 cls: 'roo-select2-search-field',
16658 cls: 'roo-select2-container input-group roo-select2-container-multi',
16664 // cls: 'typeahead typeahead-long dropdown-menu',
16665 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16670 if(this.hasFeedback && !this.allowBlank){
16674 cls: 'glyphicon form-control-feedback'
16677 combobox.cn.push(feedback);
16684 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16685 tooltip : 'This field is required'
16687 if (Roo.bootstrap.version == 4) {
16690 style : 'display:none'
16693 if (align ==='left' && this.fieldLabel.length) {
16695 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16702 cls : 'control-label col-form-label',
16703 html : this.fieldLabel
16715 var labelCfg = cfg.cn[1];
16716 var contentCfg = cfg.cn[2];
16719 if(this.indicatorpos == 'right'){
16725 cls : 'control-label col-form-label',
16729 html : this.fieldLabel
16745 labelCfg = cfg.cn[0];
16746 contentCfg = cfg.cn[1];
16750 if(this.labelWidth > 12){
16751 labelCfg.style = "width: " + this.labelWidth + 'px';
16753 if(this.width * 1 > 0){
16754 contentCfg.style = "width: " + this.width + 'px';
16756 if(this.labelWidth < 13 && this.labelmd == 0){
16757 this.labelmd = this.labelWidth;
16760 if(this.labellg > 0){
16761 labelCfg.cls += ' col-lg-' + this.labellg;
16762 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16765 if(this.labelmd > 0){
16766 labelCfg.cls += ' col-md-' + this.labelmd;
16767 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16770 if(this.labelsm > 0){
16771 labelCfg.cls += ' col-sm-' + this.labelsm;
16772 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16775 if(this.labelxs > 0){
16776 labelCfg.cls += ' col-xs-' + this.labelxs;
16777 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16781 } else if ( this.fieldLabel.length) {
16782 // Roo.log(" label");
16787 //cls : 'input-group-addon',
16788 html : this.fieldLabel
16793 if(this.indicatorpos == 'right'){
16797 //cls : 'input-group-addon',
16798 html : this.fieldLabel
16808 // Roo.log(" no label && no align");
16815 ['xs','sm','md','lg'].map(function(size){
16816 if (settings[size]) {
16817 cfg.cls += ' col-' + size + '-' + settings[size];
16825 _initEventsCalled : false,
16828 initEvents: function()
16830 if (this._initEventsCalled) { // as we call render... prevent looping...
16833 this._initEventsCalled = true;
16836 throw "can not find store for combo";
16839 this.indicator = this.indicatorEl();
16841 this.store = Roo.factory(this.store, Roo.data);
16842 this.store.parent = this;
16844 // if we are building from html. then this element is so complex, that we can not really
16845 // use the rendered HTML.
16846 // so we have to trash and replace the previous code.
16847 if (Roo.XComponent.build_from_html) {
16848 // remove this element....
16849 var e = this.el.dom, k=0;
16850 while (e ) { e = e.previousSibling; ++k;}
16855 this.rendered = false;
16857 this.render(this.parent().getChildContainer(true), k);
16860 if(Roo.isIOS && this.useNativeIOS){
16861 this.initIOSView();
16869 if(Roo.isTouch && this.mobileTouchView){
16870 this.initTouchView();
16875 this.initTickableEvents();
16879 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16881 if(this.hiddenName){
16883 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16885 this.hiddenField.dom.value =
16886 this.hiddenValue !== undefined ? this.hiddenValue :
16887 this.value !== undefined ? this.value : '';
16889 // prevent input submission
16890 this.el.dom.removeAttribute('name');
16891 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16896 // this.el.dom.setAttribute('autocomplete', 'off');
16899 var cls = 'x-combo-list';
16901 //this.list = new Roo.Layer({
16902 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16908 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16909 _this.list.setWidth(lw);
16912 this.list.on('mouseover', this.onViewOver, this);
16913 this.list.on('mousemove', this.onViewMove, this);
16914 this.list.on('scroll', this.onViewScroll, this);
16917 this.list.swallowEvent('mousewheel');
16918 this.assetHeight = 0;
16921 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16922 this.assetHeight += this.header.getHeight();
16925 this.innerList = this.list.createChild({cls:cls+'-inner'});
16926 this.innerList.on('mouseover', this.onViewOver, this);
16927 this.innerList.on('mousemove', this.onViewMove, this);
16928 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16930 if(this.allowBlank && !this.pageSize && !this.disableClear){
16931 this.footer = this.list.createChild({cls:cls+'-ft'});
16932 this.pageTb = new Roo.Toolbar(this.footer);
16936 this.footer = this.list.createChild({cls:cls+'-ft'});
16937 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16938 {pageSize: this.pageSize});
16942 if (this.pageTb && this.allowBlank && !this.disableClear) {
16944 this.pageTb.add(new Roo.Toolbar.Fill(), {
16945 cls: 'x-btn-icon x-btn-clear',
16947 handler: function()
16950 _this.clearValue();
16951 _this.onSelect(false, -1);
16956 this.assetHeight += this.footer.getHeight();
16961 this.tpl = Roo.bootstrap.version == 4 ?
16962 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16963 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16966 this.view = new Roo.View(this.list, this.tpl, {
16967 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16969 //this.view.wrapEl.setDisplayed(false);
16970 this.view.on('click', this.onViewClick, this);
16973 this.store.on('beforeload', this.onBeforeLoad, this);
16974 this.store.on('load', this.onLoad, this);
16975 this.store.on('loadexception', this.onLoadException, this);
16977 if(this.resizable){
16978 this.resizer = new Roo.Resizable(this.list, {
16979 pinned:true, handles:'se'
16981 this.resizer.on('resize', function(r, w, h){
16982 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16983 this.listWidth = w;
16984 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16985 this.restrictHeight();
16987 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16990 if(!this.editable){
16991 this.editable = true;
16992 this.setEditable(false);
16997 if (typeof(this.events.add.listeners) != 'undefined') {
16999 this.addicon = this.wrap.createChild(
17000 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17002 this.addicon.on('click', function(e) {
17003 this.fireEvent('add', this);
17006 if (typeof(this.events.edit.listeners) != 'undefined') {
17008 this.editicon = this.wrap.createChild(
17009 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17010 if (this.addicon) {
17011 this.editicon.setStyle('margin-left', '40px');
17013 this.editicon.on('click', function(e) {
17015 // we fire even if inothing is selected..
17016 this.fireEvent('edit', this, this.lastData );
17022 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17023 "up" : function(e){
17024 this.inKeyMode = true;
17028 "down" : function(e){
17029 if(!this.isExpanded()){
17030 this.onTriggerClick();
17032 this.inKeyMode = true;
17037 "enter" : function(e){
17038 // this.onViewClick();
17042 if(this.fireEvent("specialkey", this, e)){
17043 this.onViewClick(false);
17049 "esc" : function(e){
17053 "tab" : function(e){
17056 if(this.fireEvent("specialkey", this, e)){
17057 this.onViewClick(false);
17065 doRelay : function(foo, bar, hname){
17066 if(hname == 'down' || this.scope.isExpanded()){
17067 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17076 this.queryDelay = Math.max(this.queryDelay || 10,
17077 this.mode == 'local' ? 10 : 250);
17080 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17082 if(this.typeAhead){
17083 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17085 if(this.editable !== false){
17086 this.inputEl().on("keyup", this.onKeyUp, this);
17088 if(this.forceSelection){
17089 this.inputEl().on('blur', this.doForce, this);
17093 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17094 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17098 initTickableEvents: function()
17102 if(this.hiddenName){
17104 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17106 this.hiddenField.dom.value =
17107 this.hiddenValue !== undefined ? this.hiddenValue :
17108 this.value !== undefined ? this.value : '';
17110 // prevent input submission
17111 this.el.dom.removeAttribute('name');
17112 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17117 // this.list = this.el.select('ul.dropdown-menu',true).first();
17119 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17120 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17121 if(this.triggerList){
17122 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17125 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17126 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17128 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17129 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17131 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17132 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17134 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17135 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17136 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17139 this.cancelBtn.hide();
17144 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17145 _this.list.setWidth(lw);
17148 this.list.on('mouseover', this.onViewOver, this);
17149 this.list.on('mousemove', this.onViewMove, this);
17151 this.list.on('scroll', this.onViewScroll, this);
17154 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17155 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17158 this.view = new Roo.View(this.list, this.tpl, {
17163 selectedClass: this.selectedClass
17166 //this.view.wrapEl.setDisplayed(false);
17167 this.view.on('click', this.onViewClick, this);
17171 this.store.on('beforeload', this.onBeforeLoad, this);
17172 this.store.on('load', this.onLoad, this);
17173 this.store.on('loadexception', this.onLoadException, this);
17176 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17177 "up" : function(e){
17178 this.inKeyMode = true;
17182 "down" : function(e){
17183 this.inKeyMode = true;
17187 "enter" : function(e){
17188 if(this.fireEvent("specialkey", this, e)){
17189 this.onViewClick(false);
17195 "esc" : function(e){
17196 this.onTickableFooterButtonClick(e, false, false);
17199 "tab" : function(e){
17200 this.fireEvent("specialkey", this, e);
17202 this.onTickableFooterButtonClick(e, false, false);
17209 doRelay : function(e, fn, key){
17210 if(this.scope.isExpanded()){
17211 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17220 this.queryDelay = Math.max(this.queryDelay || 10,
17221 this.mode == 'local' ? 10 : 250);
17224 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17226 if(this.typeAhead){
17227 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17230 if(this.editable !== false){
17231 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17234 this.indicator = this.indicatorEl();
17236 if(this.indicator){
17237 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17238 this.indicator.hide();
17243 onDestroy : function(){
17245 this.view.setStore(null);
17246 this.view.el.removeAllListeners();
17247 this.view.el.remove();
17248 this.view.purgeListeners();
17251 this.list.dom.innerHTML = '';
17255 this.store.un('beforeload', this.onBeforeLoad, this);
17256 this.store.un('load', this.onLoad, this);
17257 this.store.un('loadexception', this.onLoadException, this);
17259 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17263 fireKey : function(e){
17264 if(e.isNavKeyPress() && !this.list.isVisible()){
17265 this.fireEvent("specialkey", this, e);
17270 onResize: function(w, h)
17274 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17276 // if(typeof w != 'number'){
17277 // // we do not handle it!?!?
17280 // var tw = this.trigger.getWidth();
17281 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17282 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17284 // this.inputEl().setWidth( this.adjustWidth('input', x));
17286 // //this.trigger.setStyle('left', x+'px');
17288 // if(this.list && this.listWidth === undefined){
17289 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17290 // this.list.setWidth(lw);
17291 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17299 * Allow or prevent the user from directly editing the field text. If false is passed,
17300 * the user will only be able to select from the items defined in the dropdown list. This method
17301 * is the runtime equivalent of setting the 'editable' config option at config time.
17302 * @param {Boolean} value True to allow the user to directly edit the field text
17304 setEditable : function(value){
17305 if(value == this.editable){
17308 this.editable = value;
17310 this.inputEl().dom.setAttribute('readOnly', true);
17311 this.inputEl().on('mousedown', this.onTriggerClick, this);
17312 this.inputEl().addClass('x-combo-noedit');
17314 this.inputEl().dom.removeAttribute('readOnly');
17315 this.inputEl().un('mousedown', this.onTriggerClick, this);
17316 this.inputEl().removeClass('x-combo-noedit');
17322 onBeforeLoad : function(combo,opts){
17323 if(!this.hasFocus){
17327 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17329 this.restrictHeight();
17330 this.selectedIndex = -1;
17334 onLoad : function(){
17336 this.hasQuery = false;
17338 if(!this.hasFocus){
17342 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17343 this.loading.hide();
17346 if(this.store.getCount() > 0){
17349 this.restrictHeight();
17350 if(this.lastQuery == this.allQuery){
17351 if(this.editable && !this.tickable){
17352 this.inputEl().dom.select();
17356 !this.selectByValue(this.value, true) &&
17359 !this.store.lastOptions ||
17360 typeof(this.store.lastOptions.add) == 'undefined' ||
17361 this.store.lastOptions.add != true
17364 this.select(0, true);
17367 if(this.autoFocus){
17370 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17371 this.taTask.delay(this.typeAheadDelay);
17375 this.onEmptyResults();
17381 onLoadException : function()
17383 this.hasQuery = false;
17385 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17386 this.loading.hide();
17389 if(this.tickable && this.editable){
17394 // only causes errors at present
17395 //Roo.log(this.store.reader.jsonData);
17396 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17398 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17404 onTypeAhead : function(){
17405 if(this.store.getCount() > 0){
17406 var r = this.store.getAt(0);
17407 var newValue = r.data[this.displayField];
17408 var len = newValue.length;
17409 var selStart = this.getRawValue().length;
17411 if(selStart != len){
17412 this.setRawValue(newValue);
17413 this.selectText(selStart, newValue.length);
17419 onSelect : function(record, index){
17421 if(this.fireEvent('beforeselect', this, record, index) !== false){
17423 this.setFromData(index > -1 ? record.data : false);
17426 this.fireEvent('select', this, record, index);
17431 * Returns the currently selected field value or empty string if no value is set.
17432 * @return {String} value The selected value
17434 getValue : function()
17436 if(Roo.isIOS && this.useNativeIOS){
17437 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17441 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17444 if(this.valueField){
17445 return typeof this.value != 'undefined' ? this.value : '';
17447 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17451 getRawValue : function()
17453 if(Roo.isIOS && this.useNativeIOS){
17454 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17457 var v = this.inputEl().getValue();
17463 * Clears any text/value currently set in the field
17465 clearValue : function(){
17467 if(this.hiddenField){
17468 this.hiddenField.dom.value = '';
17471 this.setRawValue('');
17472 this.lastSelectionText = '';
17473 this.lastData = false;
17475 var close = this.closeTriggerEl();
17486 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17487 * will be displayed in the field. If the value does not match the data value of an existing item,
17488 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17489 * Otherwise the field will be blank (although the value will still be set).
17490 * @param {String} value The value to match
17492 setValue : function(v)
17494 if(Roo.isIOS && this.useNativeIOS){
17495 this.setIOSValue(v);
17505 if(this.valueField){
17506 var r = this.findRecord(this.valueField, v);
17508 text = r.data[this.displayField];
17509 }else if(this.valueNotFoundText !== undefined){
17510 text = this.valueNotFoundText;
17513 this.lastSelectionText = text;
17514 if(this.hiddenField){
17515 this.hiddenField.dom.value = v;
17517 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17520 var close = this.closeTriggerEl();
17523 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17529 * @property {Object} the last set data for the element
17534 * Sets the value of the field based on a object which is related to the record format for the store.
17535 * @param {Object} value the value to set as. or false on reset?
17537 setFromData : function(o){
17544 var dv = ''; // display value
17545 var vv = ''; // value value..
17547 if (this.displayField) {
17548 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17550 // this is an error condition!!!
17551 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17554 if(this.valueField){
17555 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17558 var close = this.closeTriggerEl();
17561 if(dv.length || vv * 1 > 0){
17563 this.blockFocus=true;
17569 if(this.hiddenField){
17570 this.hiddenField.dom.value = vv;
17572 this.lastSelectionText = dv;
17573 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17577 // no hidden field.. - we store the value in 'value', but still display
17578 // display field!!!!
17579 this.lastSelectionText = dv;
17580 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17587 reset : function(){
17588 // overridden so that last data is reset..
17595 this.setValue(this.originalValue);
17596 //this.clearInvalid();
17597 this.lastData = false;
17599 this.view.clearSelections();
17605 findRecord : function(prop, value){
17607 if(this.store.getCount() > 0){
17608 this.store.each(function(r){
17609 if(r.data[prop] == value){
17619 getName: function()
17621 // returns hidden if it's set..
17622 if (!this.rendered) {return ''};
17623 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17627 onViewMove : function(e, t){
17628 this.inKeyMode = false;
17632 onViewOver : function(e, t){
17633 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17636 var item = this.view.findItemFromChild(t);
17639 var index = this.view.indexOf(item);
17640 this.select(index, false);
17645 onViewClick : function(view, doFocus, el, e)
17647 var index = this.view.getSelectedIndexes()[0];
17649 var r = this.store.getAt(index);
17653 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17660 Roo.each(this.tickItems, function(v,k){
17662 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17664 _this.tickItems.splice(k, 1);
17666 if(typeof(e) == 'undefined' && view == false){
17667 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17679 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17680 this.tickItems.push(r.data);
17683 if(typeof(e) == 'undefined' && view == false){
17684 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17691 this.onSelect(r, index);
17693 if(doFocus !== false && !this.blockFocus){
17694 this.inputEl().focus();
17699 restrictHeight : function(){
17700 //this.innerList.dom.style.height = '';
17701 //var inner = this.innerList.dom;
17702 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17703 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17704 //this.list.beginUpdate();
17705 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17706 this.list.alignTo(this.inputEl(), this.listAlign);
17707 this.list.alignTo(this.inputEl(), this.listAlign);
17708 //this.list.endUpdate();
17712 onEmptyResults : function(){
17714 if(this.tickable && this.editable){
17715 this.hasFocus = false;
17716 this.restrictHeight();
17724 * Returns true if the dropdown list is expanded, else false.
17726 isExpanded : function(){
17727 return this.list.isVisible();
17731 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17732 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17733 * @param {String} value The data value of the item to select
17734 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17735 * selected item if it is not currently in view (defaults to true)
17736 * @return {Boolean} True if the value matched an item in the list, else false
17738 selectByValue : function(v, scrollIntoView){
17739 if(v !== undefined && v !== null){
17740 var r = this.findRecord(this.valueField || this.displayField, v);
17742 this.select(this.store.indexOf(r), scrollIntoView);
17750 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17751 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17752 * @param {Number} index The zero-based index of the list item to select
17753 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17754 * selected item if it is not currently in view (defaults to true)
17756 select : function(index, scrollIntoView){
17757 this.selectedIndex = index;
17758 this.view.select(index);
17759 if(scrollIntoView !== false){
17760 var el = this.view.getNode(index);
17762 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17765 this.list.scrollChildIntoView(el, false);
17771 selectNext : function(){
17772 var ct = this.store.getCount();
17774 if(this.selectedIndex == -1){
17776 }else if(this.selectedIndex < ct-1){
17777 this.select(this.selectedIndex+1);
17783 selectPrev : function(){
17784 var ct = this.store.getCount();
17786 if(this.selectedIndex == -1){
17788 }else if(this.selectedIndex != 0){
17789 this.select(this.selectedIndex-1);
17795 onKeyUp : function(e){
17796 if(this.editable !== false && !e.isSpecialKey()){
17797 this.lastKey = e.getKey();
17798 this.dqTask.delay(this.queryDelay);
17803 validateBlur : function(){
17804 return !this.list || !this.list.isVisible();
17808 initQuery : function(){
17810 var v = this.getRawValue();
17812 if(this.tickable && this.editable){
17813 v = this.tickableInputEl().getValue();
17820 doForce : function(){
17821 if(this.inputEl().dom.value.length > 0){
17822 this.inputEl().dom.value =
17823 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17829 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17830 * query allowing the query action to be canceled if needed.
17831 * @param {String} query The SQL query to execute
17832 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17833 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17834 * saved in the current store (defaults to false)
17836 doQuery : function(q, forceAll){
17838 if(q === undefined || q === null){
17843 forceAll: forceAll,
17847 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17852 forceAll = qe.forceAll;
17853 if(forceAll === true || (q.length >= this.minChars)){
17855 this.hasQuery = true;
17857 if(this.lastQuery != q || this.alwaysQuery){
17858 this.lastQuery = q;
17859 if(this.mode == 'local'){
17860 this.selectedIndex = -1;
17862 this.store.clearFilter();
17865 if(this.specialFilter){
17866 this.fireEvent('specialfilter', this);
17871 this.store.filter(this.displayField, q);
17874 this.store.fireEvent("datachanged", this.store);
17881 this.store.baseParams[this.queryParam] = q;
17883 var options = {params : this.getParams(q)};
17886 options.add = true;
17887 options.params.start = this.page * this.pageSize;
17890 this.store.load(options);
17893 * this code will make the page width larger, at the beginning, the list not align correctly,
17894 * we should expand the list on onLoad
17895 * so command out it
17900 this.selectedIndex = -1;
17905 this.loadNext = false;
17909 getParams : function(q){
17911 //p[this.queryParam] = q;
17915 p.limit = this.pageSize;
17921 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17923 collapse : function(){
17924 if(!this.isExpanded()){
17930 this.hasFocus = false;
17934 this.cancelBtn.hide();
17935 this.trigger.show();
17938 this.tickableInputEl().dom.value = '';
17939 this.tickableInputEl().blur();
17944 Roo.get(document).un('mousedown', this.collapseIf, this);
17945 Roo.get(document).un('mousewheel', this.collapseIf, this);
17946 if (!this.editable) {
17947 Roo.get(document).un('keydown', this.listKeyPress, this);
17949 this.fireEvent('collapse', this);
17955 collapseIf : function(e){
17956 var in_combo = e.within(this.el);
17957 var in_list = e.within(this.list);
17958 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17960 if (in_combo || in_list || is_list) {
17961 //e.stopPropagation();
17966 this.onTickableFooterButtonClick(e, false, false);
17974 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17976 expand : function(){
17978 if(this.isExpanded() || !this.hasFocus){
17982 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17983 this.list.setWidth(lw);
17989 this.restrictHeight();
17993 this.tickItems = Roo.apply([], this.item);
17996 this.cancelBtn.show();
17997 this.trigger.hide();
18000 this.tickableInputEl().focus();
18005 Roo.get(document).on('mousedown', this.collapseIf, this);
18006 Roo.get(document).on('mousewheel', this.collapseIf, this);
18007 if (!this.editable) {
18008 Roo.get(document).on('keydown', this.listKeyPress, this);
18011 this.fireEvent('expand', this);
18015 // Implements the default empty TriggerField.onTriggerClick function
18016 onTriggerClick : function(e)
18018 Roo.log('trigger click');
18020 if(this.disabled || !this.triggerList){
18025 this.loadNext = false;
18027 if(this.isExpanded()){
18029 if (!this.blockFocus) {
18030 this.inputEl().focus();
18034 this.hasFocus = true;
18035 if(this.triggerAction == 'all') {
18036 this.doQuery(this.allQuery, true);
18038 this.doQuery(this.getRawValue());
18040 if (!this.blockFocus) {
18041 this.inputEl().focus();
18046 onTickableTriggerClick : function(e)
18053 this.loadNext = false;
18054 this.hasFocus = true;
18056 if(this.triggerAction == 'all') {
18057 this.doQuery(this.allQuery, true);
18059 this.doQuery(this.getRawValue());
18063 onSearchFieldClick : function(e)
18065 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18066 this.onTickableFooterButtonClick(e, false, false);
18070 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18075 this.loadNext = false;
18076 this.hasFocus = true;
18078 if(this.triggerAction == 'all') {
18079 this.doQuery(this.allQuery, true);
18081 this.doQuery(this.getRawValue());
18085 listKeyPress : function(e)
18087 //Roo.log('listkeypress');
18088 // scroll to first matching element based on key pres..
18089 if (e.isSpecialKey()) {
18092 var k = String.fromCharCode(e.getKey()).toUpperCase();
18095 var csel = this.view.getSelectedNodes();
18096 var cselitem = false;
18098 var ix = this.view.indexOf(csel[0]);
18099 cselitem = this.store.getAt(ix);
18100 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18106 this.store.each(function(v) {
18108 // start at existing selection.
18109 if (cselitem.id == v.id) {
18115 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18116 match = this.store.indexOf(v);
18122 if (match === false) {
18123 return true; // no more action?
18126 this.view.select(match);
18127 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18128 sn.scrollIntoView(sn.dom.parentNode, false);
18131 onViewScroll : function(e, t){
18133 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){
18137 this.hasQuery = true;
18139 this.loading = this.list.select('.loading', true).first();
18141 if(this.loading === null){
18142 this.list.createChild({
18144 cls: 'loading roo-select2-more-results roo-select2-active',
18145 html: 'Loading more results...'
18148 this.loading = this.list.select('.loading', true).first();
18150 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18152 this.loading.hide();
18155 this.loading.show();
18160 this.loadNext = true;
18162 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18167 addItem : function(o)
18169 var dv = ''; // display value
18171 if (this.displayField) {
18172 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18174 // this is an error condition!!!
18175 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18182 var choice = this.choices.createChild({
18184 cls: 'roo-select2-search-choice',
18193 cls: 'roo-select2-search-choice-close fa fa-times',
18198 }, this.searchField);
18200 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18202 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18210 this.inputEl().dom.value = '';
18215 onRemoveItem : function(e, _self, o)
18217 e.preventDefault();
18219 this.lastItem = Roo.apply([], this.item);
18221 var index = this.item.indexOf(o.data) * 1;
18224 Roo.log('not this item?!');
18228 this.item.splice(index, 1);
18233 this.fireEvent('remove', this, e);
18239 syncValue : function()
18241 if(!this.item.length){
18248 Roo.each(this.item, function(i){
18249 if(_this.valueField){
18250 value.push(i[_this.valueField]);
18257 this.value = value.join(',');
18259 if(this.hiddenField){
18260 this.hiddenField.dom.value = this.value;
18263 this.store.fireEvent("datachanged", this.store);
18268 clearItem : function()
18270 if(!this.multiple){
18276 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18284 if(this.tickable && !Roo.isTouch){
18285 this.view.refresh();
18289 inputEl: function ()
18291 if(Roo.isIOS && this.useNativeIOS){
18292 return this.el.select('select.roo-ios-select', true).first();
18295 if(Roo.isTouch && this.mobileTouchView){
18296 return this.el.select('input.form-control',true).first();
18300 return this.searchField;
18303 return this.el.select('input.form-control',true).first();
18306 onTickableFooterButtonClick : function(e, btn, el)
18308 e.preventDefault();
18310 this.lastItem = Roo.apply([], this.item);
18312 if(btn && btn.name == 'cancel'){
18313 this.tickItems = Roo.apply([], this.item);
18322 Roo.each(this.tickItems, function(o){
18330 validate : function()
18332 if(this.getVisibilityEl().hasClass('hidden')){
18336 var v = this.getRawValue();
18339 v = this.getValue();
18342 if(this.disabled || this.allowBlank || v.length){
18347 this.markInvalid();
18351 tickableInputEl : function()
18353 if(!this.tickable || !this.editable){
18354 return this.inputEl();
18357 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18361 getAutoCreateTouchView : function()
18366 cls: 'form-group' //input-group
18372 type : this.inputType,
18373 cls : 'form-control x-combo-noedit',
18374 autocomplete: 'new-password',
18375 placeholder : this.placeholder || '',
18380 input.name = this.name;
18384 input.cls += ' input-' + this.size;
18387 if (this.disabled) {
18388 input.disabled = true;
18392 cls : 'roo-combobox-wrap',
18399 inputblock.cls += ' input-group';
18401 inputblock.cn.unshift({
18403 cls : 'input-group-addon input-group-prepend input-group-text',
18408 if(this.removable && !this.multiple){
18409 inputblock.cls += ' roo-removable';
18411 inputblock.cn.push({
18414 cls : 'roo-combo-removable-btn close'
18418 if(this.hasFeedback && !this.allowBlank){
18420 inputblock.cls += ' has-feedback';
18422 inputblock.cn.push({
18424 cls: 'glyphicon form-control-feedback'
18431 inputblock.cls += (this.before) ? '' : ' input-group';
18433 inputblock.cn.push({
18435 cls : 'input-group-addon input-group-append input-group-text',
18441 var ibwrap = inputblock;
18446 cls: 'roo-select2-choices',
18450 cls: 'roo-select2-search-field',
18463 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18468 cls: 'form-hidden-field'
18474 if(!this.multiple && this.showToggleBtn){
18480 if (this.caret != false) {
18483 cls: 'fa fa-' + this.caret
18490 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18492 Roo.bootstrap.version == 3 ? caret : '',
18495 cls: 'combobox-clear',
18509 combobox.cls += ' roo-select2-container-multi';
18512 var required = this.allowBlank ? {
18514 style: 'display: none'
18517 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18518 tooltip : 'This field is required'
18521 var align = this.labelAlign || this.parentLabelAlign();
18523 if (align ==='left' && this.fieldLabel.length) {
18529 cls : 'control-label col-form-label',
18530 html : this.fieldLabel
18534 cls : 'roo-combobox-wrap ',
18541 var labelCfg = cfg.cn[1];
18542 var contentCfg = cfg.cn[2];
18545 if(this.indicatorpos == 'right'){
18550 cls : 'control-label col-form-label',
18554 html : this.fieldLabel
18560 cls : "roo-combobox-wrap ",
18568 labelCfg = cfg.cn[0];
18569 contentCfg = cfg.cn[1];
18574 if(this.labelWidth > 12){
18575 labelCfg.style = "width: " + this.labelWidth + 'px';
18578 if(this.labelWidth < 13 && this.labelmd == 0){
18579 this.labelmd = this.labelWidth;
18582 if(this.labellg > 0){
18583 labelCfg.cls += ' col-lg-' + this.labellg;
18584 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18587 if(this.labelmd > 0){
18588 labelCfg.cls += ' col-md-' + this.labelmd;
18589 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18592 if(this.labelsm > 0){
18593 labelCfg.cls += ' col-sm-' + this.labelsm;
18594 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18597 if(this.labelxs > 0){
18598 labelCfg.cls += ' col-xs-' + this.labelxs;
18599 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18603 } else if ( this.fieldLabel.length) {
18608 cls : 'control-label',
18609 html : this.fieldLabel
18620 if(this.indicatorpos == 'right'){
18624 cls : 'control-label',
18625 html : this.fieldLabel,
18643 var settings = this;
18645 ['xs','sm','md','lg'].map(function(size){
18646 if (settings[size]) {
18647 cfg.cls += ' col-' + size + '-' + settings[size];
18654 initTouchView : function()
18656 this.renderTouchView();
18658 this.touchViewEl.on('scroll', function(){
18659 this.el.dom.scrollTop = 0;
18662 this.originalValue = this.getValue();
18664 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18666 this.inputEl().on("click", this.showTouchView, this);
18667 if (this.triggerEl) {
18668 this.triggerEl.on("click", this.showTouchView, this);
18672 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18673 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18675 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18677 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18678 this.store.on('load', this.onTouchViewLoad, this);
18679 this.store.on('loadexception', this.onTouchViewLoadException, this);
18681 if(this.hiddenName){
18683 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18685 this.hiddenField.dom.value =
18686 this.hiddenValue !== undefined ? this.hiddenValue :
18687 this.value !== undefined ? this.value : '';
18689 this.el.dom.removeAttribute('name');
18690 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18694 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18695 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18698 if(this.removable && !this.multiple){
18699 var close = this.closeTriggerEl();
18701 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18702 close.on('click', this.removeBtnClick, this, close);
18706 * fix the bug in Safari iOS8
18708 this.inputEl().on("focus", function(e){
18709 document.activeElement.blur();
18712 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18719 renderTouchView : function()
18721 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18722 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18724 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18725 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18727 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18728 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18729 this.touchViewBodyEl.setStyle('overflow', 'auto');
18731 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18732 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18734 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18735 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18739 showTouchView : function()
18745 this.touchViewHeaderEl.hide();
18747 if(this.modalTitle.length){
18748 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18749 this.touchViewHeaderEl.show();
18752 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18753 this.touchViewEl.show();
18755 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18757 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18758 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18760 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18762 if(this.modalTitle.length){
18763 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18766 this.touchViewBodyEl.setHeight(bodyHeight);
18770 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18772 this.touchViewEl.addClass(['in','show']);
18775 if(this._touchViewMask){
18776 Roo.get(document.body).addClass("x-body-masked");
18777 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18778 this._touchViewMask.setStyle('z-index', 10000);
18779 this._touchViewMask.addClass('show');
18782 this.doTouchViewQuery();
18786 hideTouchView : function()
18788 this.touchViewEl.removeClass(['in','show']);
18792 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18794 this.touchViewEl.setStyle('display', 'none');
18797 if(this._touchViewMask){
18798 this._touchViewMask.removeClass('show');
18799 Roo.get(document.body).removeClass("x-body-masked");
18803 setTouchViewValue : function()
18810 Roo.each(this.tickItems, function(o){
18815 this.hideTouchView();
18818 doTouchViewQuery : function()
18827 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18831 if(!this.alwaysQuery || this.mode == 'local'){
18832 this.onTouchViewLoad();
18839 onTouchViewBeforeLoad : function(combo,opts)
18845 onTouchViewLoad : function()
18847 if(this.store.getCount() < 1){
18848 this.onTouchViewEmptyResults();
18852 this.clearTouchView();
18854 var rawValue = this.getRawValue();
18856 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18858 this.tickItems = [];
18860 this.store.data.each(function(d, rowIndex){
18861 var row = this.touchViewListGroup.createChild(template);
18863 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18864 row.addClass(d.data.cls);
18867 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18870 html : d.data[this.displayField]
18873 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18874 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18877 row.removeClass('selected');
18878 if(!this.multiple && this.valueField &&
18879 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18882 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18883 row.addClass('selected');
18886 if(this.multiple && this.valueField &&
18887 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18891 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18892 this.tickItems.push(d.data);
18895 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18899 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18901 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18903 if(this.modalTitle.length){
18904 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18907 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18909 if(this.mobile_restrict_height && listHeight < bodyHeight){
18910 this.touchViewBodyEl.setHeight(listHeight);
18915 if(firstChecked && listHeight > bodyHeight){
18916 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18921 onTouchViewLoadException : function()
18923 this.hideTouchView();
18926 onTouchViewEmptyResults : function()
18928 this.clearTouchView();
18930 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18932 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18936 clearTouchView : function()
18938 this.touchViewListGroup.dom.innerHTML = '';
18941 onTouchViewClick : function(e, el, o)
18943 e.preventDefault();
18946 var rowIndex = o.rowIndex;
18948 var r = this.store.getAt(rowIndex);
18950 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18952 if(!this.multiple){
18953 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18954 c.dom.removeAttribute('checked');
18957 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18959 this.setFromData(r.data);
18961 var close = this.closeTriggerEl();
18967 this.hideTouchView();
18969 this.fireEvent('select', this, r, rowIndex);
18974 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18975 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18976 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18980 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18981 this.addItem(r.data);
18982 this.tickItems.push(r.data);
18986 getAutoCreateNativeIOS : function()
18989 cls: 'form-group' //input-group,
18994 cls : 'roo-ios-select'
18998 combobox.name = this.name;
19001 if (this.disabled) {
19002 combobox.disabled = true;
19005 var settings = this;
19007 ['xs','sm','md','lg'].map(function(size){
19008 if (settings[size]) {
19009 cfg.cls += ' col-' + size + '-' + settings[size];
19019 initIOSView : function()
19021 this.store.on('load', this.onIOSViewLoad, this);
19026 onIOSViewLoad : function()
19028 if(this.store.getCount() < 1){
19032 this.clearIOSView();
19034 if(this.allowBlank) {
19036 var default_text = '-- SELECT --';
19038 if(this.placeholder.length){
19039 default_text = this.placeholder;
19042 if(this.emptyTitle.length){
19043 default_text += ' - ' + this.emptyTitle + ' -';
19046 var opt = this.inputEl().createChild({
19049 html : default_text
19053 o[this.valueField] = 0;
19054 o[this.displayField] = default_text;
19056 this.ios_options.push({
19063 this.store.data.each(function(d, rowIndex){
19067 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19068 html = d.data[this.displayField];
19073 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19074 value = d.data[this.valueField];
19083 if(this.value == d.data[this.valueField]){
19084 option['selected'] = true;
19087 var opt = this.inputEl().createChild(option);
19089 this.ios_options.push({
19096 this.inputEl().on('change', function(){
19097 this.fireEvent('select', this);
19102 clearIOSView: function()
19104 this.inputEl().dom.innerHTML = '';
19106 this.ios_options = [];
19109 setIOSValue: function(v)
19113 if(!this.ios_options){
19117 Roo.each(this.ios_options, function(opts){
19119 opts.el.dom.removeAttribute('selected');
19121 if(opts.data[this.valueField] != v){
19125 opts.el.dom.setAttribute('selected', true);
19131 * @cfg {Boolean} grow
19135 * @cfg {Number} growMin
19139 * @cfg {Number} growMax
19148 Roo.apply(Roo.bootstrap.ComboBox, {
19152 cls: 'modal-header',
19174 cls: 'list-group-item',
19178 cls: 'roo-combobox-list-group-item-value'
19182 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19196 listItemCheckbox : {
19198 cls: 'list-group-item',
19202 cls: 'roo-combobox-list-group-item-value'
19206 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19222 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19227 cls: 'modal-footer',
19235 cls: 'col-xs-6 text-left',
19238 cls: 'btn btn-danger roo-touch-view-cancel',
19244 cls: 'col-xs-6 text-right',
19247 cls: 'btn btn-success roo-touch-view-ok',
19258 Roo.apply(Roo.bootstrap.ComboBox, {
19260 touchViewTemplate : {
19262 cls: 'modal fade roo-combobox-touch-view',
19266 cls: 'modal-dialog',
19267 style : 'position:fixed', // we have to fix position....
19271 cls: 'modal-content',
19273 Roo.bootstrap.ComboBox.header,
19274 Roo.bootstrap.ComboBox.body,
19275 Roo.bootstrap.ComboBox.footer
19284 * Ext JS Library 1.1.1
19285 * Copyright(c) 2006-2007, Ext JS, LLC.
19287 * Originally Released Under LGPL - original licence link has changed is not relivant.
19290 * <script type="text/javascript">
19295 * @extends Roo.util.Observable
19296 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19297 * This class also supports single and multi selection modes. <br>
19298 * Create a data model bound view:
19300 var store = new Roo.data.Store(...);
19302 var view = new Roo.View({
19304 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19306 singleSelect: true,
19307 selectedClass: "ydataview-selected",
19311 // listen for node click?
19312 view.on("click", function(vw, index, node, e){
19313 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19317 dataModel.load("foobar.xml");
19319 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19321 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19322 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19324 * Note: old style constructor is still suported (container, template, config)
19327 * Create a new View
19328 * @param {Object} config The config object
19331 Roo.View = function(config, depreciated_tpl, depreciated_config){
19333 this.parent = false;
19335 if (typeof(depreciated_tpl) == 'undefined') {
19336 // new way.. - universal constructor.
19337 Roo.apply(this, config);
19338 this.el = Roo.get(this.el);
19341 this.el = Roo.get(config);
19342 this.tpl = depreciated_tpl;
19343 Roo.apply(this, depreciated_config);
19345 this.wrapEl = this.el.wrap().wrap();
19346 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19349 if(typeof(this.tpl) == "string"){
19350 this.tpl = new Roo.Template(this.tpl);
19352 // support xtype ctors..
19353 this.tpl = new Roo.factory(this.tpl, Roo);
19357 this.tpl.compile();
19362 * @event beforeclick
19363 * Fires before a click is processed. Returns false to cancel the default action.
19364 * @param {Roo.View} this
19365 * @param {Number} index The index of the target node
19366 * @param {HTMLElement} node The target node
19367 * @param {Roo.EventObject} e The raw event object
19369 "beforeclick" : true,
19372 * Fires when a template node is clicked.
19373 * @param {Roo.View} this
19374 * @param {Number} index The index of the target node
19375 * @param {HTMLElement} node The target node
19376 * @param {Roo.EventObject} e The raw event object
19381 * Fires when a template node is double clicked.
19382 * @param {Roo.View} this
19383 * @param {Number} index The index of the target node
19384 * @param {HTMLElement} node The target node
19385 * @param {Roo.EventObject} e The raw event object
19389 * @event contextmenu
19390 * Fires when a template node is right clicked.
19391 * @param {Roo.View} this
19392 * @param {Number} index The index of the target node
19393 * @param {HTMLElement} node The target node
19394 * @param {Roo.EventObject} e The raw event object
19396 "contextmenu" : true,
19398 * @event selectionchange
19399 * Fires when the selected nodes change.
19400 * @param {Roo.View} this
19401 * @param {Array} selections Array of the selected nodes
19403 "selectionchange" : true,
19406 * @event beforeselect
19407 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19408 * @param {Roo.View} this
19409 * @param {HTMLElement} node The node to be selected
19410 * @param {Array} selections Array of currently selected nodes
19412 "beforeselect" : true,
19414 * @event preparedata
19415 * Fires on every row to render, to allow you to change the data.
19416 * @param {Roo.View} this
19417 * @param {Object} data to be rendered (change this)
19419 "preparedata" : true
19427 "click": this.onClick,
19428 "dblclick": this.onDblClick,
19429 "contextmenu": this.onContextMenu,
19433 this.selections = [];
19435 this.cmp = new Roo.CompositeElementLite([]);
19437 this.store = Roo.factory(this.store, Roo.data);
19438 this.setStore(this.store, true);
19441 if ( this.footer && this.footer.xtype) {
19443 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19445 this.footer.dataSource = this.store;
19446 this.footer.container = fctr;
19447 this.footer = Roo.factory(this.footer, Roo);
19448 fctr.insertFirst(this.el);
19450 // this is a bit insane - as the paging toolbar seems to detach the el..
19451 // dom.parentNode.parentNode.parentNode
19452 // they get detached?
19456 Roo.View.superclass.constructor.call(this);
19461 Roo.extend(Roo.View, Roo.util.Observable, {
19464 * @cfg {Roo.data.Store} store Data store to load data from.
19469 * @cfg {String|Roo.Element} el The container element.
19474 * @cfg {String|Roo.Template} tpl The template used by this View
19478 * @cfg {String} dataName the named area of the template to use as the data area
19479 * Works with domtemplates roo-name="name"
19483 * @cfg {String} selectedClass The css class to add to selected nodes
19485 selectedClass : "x-view-selected",
19487 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19492 * @cfg {String} text to display on mask (default Loading)
19496 * @cfg {Boolean} multiSelect Allow multiple selection
19498 multiSelect : false,
19500 * @cfg {Boolean} singleSelect Allow single selection
19502 singleSelect: false,
19505 * @cfg {Boolean} toggleSelect - selecting
19507 toggleSelect : false,
19510 * @cfg {Boolean} tickable - selecting
19515 * Returns the element this view is bound to.
19516 * @return {Roo.Element}
19518 getEl : function(){
19519 return this.wrapEl;
19525 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19527 refresh : function(){
19528 //Roo.log('refresh');
19531 // if we are using something like 'domtemplate', then
19532 // the what gets used is:
19533 // t.applySubtemplate(NAME, data, wrapping data..)
19534 // the outer template then get' applied with
19535 // the store 'extra data'
19536 // and the body get's added to the
19537 // roo-name="data" node?
19538 // <span class='roo-tpl-{name}'></span> ?????
19542 this.clearSelections();
19543 this.el.update("");
19545 var records = this.store.getRange();
19546 if(records.length < 1) {
19548 // is this valid?? = should it render a template??
19550 this.el.update(this.emptyText);
19554 if (this.dataName) {
19555 this.el.update(t.apply(this.store.meta)); //????
19556 el = this.el.child('.roo-tpl-' + this.dataName);
19559 for(var i = 0, len = records.length; i < len; i++){
19560 var data = this.prepareData(records[i].data, i, records[i]);
19561 this.fireEvent("preparedata", this, data, i, records[i]);
19563 var d = Roo.apply({}, data);
19566 Roo.apply(d, {'roo-id' : Roo.id()});
19570 Roo.each(this.parent.item, function(item){
19571 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19574 Roo.apply(d, {'roo-data-checked' : 'checked'});
19578 html[html.length] = Roo.util.Format.trim(
19580 t.applySubtemplate(this.dataName, d, this.store.meta) :
19587 el.update(html.join(""));
19588 this.nodes = el.dom.childNodes;
19589 this.updateIndexes(0);
19594 * Function to override to reformat the data that is sent to
19595 * the template for each node.
19596 * DEPRICATED - use the preparedata event handler.
19597 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19598 * a JSON object for an UpdateManager bound view).
19600 prepareData : function(data, index, record)
19602 this.fireEvent("preparedata", this, data, index, record);
19606 onUpdate : function(ds, record){
19607 // Roo.log('on update');
19608 this.clearSelections();
19609 var index = this.store.indexOf(record);
19610 var n = this.nodes[index];
19611 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19612 n.parentNode.removeChild(n);
19613 this.updateIndexes(index, index);
19619 onAdd : function(ds, records, index)
19621 //Roo.log(['on Add', ds, records, index] );
19622 this.clearSelections();
19623 if(this.nodes.length == 0){
19627 var n = this.nodes[index];
19628 for(var i = 0, len = records.length; i < len; i++){
19629 var d = this.prepareData(records[i].data, i, records[i]);
19631 this.tpl.insertBefore(n, d);
19634 this.tpl.append(this.el, d);
19637 this.updateIndexes(index);
19640 onRemove : function(ds, record, index){
19641 // Roo.log('onRemove');
19642 this.clearSelections();
19643 var el = this.dataName ?
19644 this.el.child('.roo-tpl-' + this.dataName) :
19647 el.dom.removeChild(this.nodes[index]);
19648 this.updateIndexes(index);
19652 * Refresh an individual node.
19653 * @param {Number} index
19655 refreshNode : function(index){
19656 this.onUpdate(this.store, this.store.getAt(index));
19659 updateIndexes : function(startIndex, endIndex){
19660 var ns = this.nodes;
19661 startIndex = startIndex || 0;
19662 endIndex = endIndex || ns.length - 1;
19663 for(var i = startIndex; i <= endIndex; i++){
19664 ns[i].nodeIndex = i;
19669 * Changes the data store this view uses and refresh the view.
19670 * @param {Store} store
19672 setStore : function(store, initial){
19673 if(!initial && this.store){
19674 this.store.un("datachanged", this.refresh);
19675 this.store.un("add", this.onAdd);
19676 this.store.un("remove", this.onRemove);
19677 this.store.un("update", this.onUpdate);
19678 this.store.un("clear", this.refresh);
19679 this.store.un("beforeload", this.onBeforeLoad);
19680 this.store.un("load", this.onLoad);
19681 this.store.un("loadexception", this.onLoad);
19685 store.on("datachanged", this.refresh, this);
19686 store.on("add", this.onAdd, this);
19687 store.on("remove", this.onRemove, this);
19688 store.on("update", this.onUpdate, this);
19689 store.on("clear", this.refresh, this);
19690 store.on("beforeload", this.onBeforeLoad, this);
19691 store.on("load", this.onLoad, this);
19692 store.on("loadexception", this.onLoad, this);
19700 * onbeforeLoad - masks the loading area.
19703 onBeforeLoad : function(store,opts)
19705 //Roo.log('onBeforeLoad');
19707 this.el.update("");
19709 this.el.mask(this.mask ? this.mask : "Loading" );
19711 onLoad : function ()
19718 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19719 * @param {HTMLElement} node
19720 * @return {HTMLElement} The template node
19722 findItemFromChild : function(node){
19723 var el = this.dataName ?
19724 this.el.child('.roo-tpl-' + this.dataName,true) :
19727 if(!node || node.parentNode == el){
19730 var p = node.parentNode;
19731 while(p && p != el){
19732 if(p.parentNode == el){
19741 onClick : function(e){
19742 var item = this.findItemFromChild(e.getTarget());
19744 var index = this.indexOf(item);
19745 if(this.onItemClick(item, index, e) !== false){
19746 this.fireEvent("click", this, index, item, e);
19749 this.clearSelections();
19754 onContextMenu : function(e){
19755 var item = this.findItemFromChild(e.getTarget());
19757 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19762 onDblClick : function(e){
19763 var item = this.findItemFromChild(e.getTarget());
19765 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19769 onItemClick : function(item, index, e)
19771 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19774 if (this.toggleSelect) {
19775 var m = this.isSelected(item) ? 'unselect' : 'select';
19778 _t[m](item, true, false);
19781 if(this.multiSelect || this.singleSelect){
19782 if(this.multiSelect && e.shiftKey && this.lastSelection){
19783 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19785 this.select(item, this.multiSelect && e.ctrlKey);
19786 this.lastSelection = item;
19789 if(!this.tickable){
19790 e.preventDefault();
19798 * Get the number of selected nodes.
19801 getSelectionCount : function(){
19802 return this.selections.length;
19806 * Get the currently selected nodes.
19807 * @return {Array} An array of HTMLElements
19809 getSelectedNodes : function(){
19810 return this.selections;
19814 * Get the indexes of the selected nodes.
19817 getSelectedIndexes : function(){
19818 var indexes = [], s = this.selections;
19819 for(var i = 0, len = s.length; i < len; i++){
19820 indexes.push(s[i].nodeIndex);
19826 * Clear all selections
19827 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19829 clearSelections : function(suppressEvent){
19830 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19831 this.cmp.elements = this.selections;
19832 this.cmp.removeClass(this.selectedClass);
19833 this.selections = [];
19834 if(!suppressEvent){
19835 this.fireEvent("selectionchange", this, this.selections);
19841 * Returns true if the passed node is selected
19842 * @param {HTMLElement/Number} node The node or node index
19843 * @return {Boolean}
19845 isSelected : function(node){
19846 var s = this.selections;
19850 node = this.getNode(node);
19851 return s.indexOf(node) !== -1;
19856 * @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
19857 * @param {Boolean} keepExisting (optional) true to keep existing selections
19858 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19860 select : function(nodeInfo, keepExisting, suppressEvent){
19861 if(nodeInfo instanceof Array){
19863 this.clearSelections(true);
19865 for(var i = 0, len = nodeInfo.length; i < len; i++){
19866 this.select(nodeInfo[i], true, true);
19870 var node = this.getNode(nodeInfo);
19871 if(!node || this.isSelected(node)){
19872 return; // already selected.
19875 this.clearSelections(true);
19878 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19879 Roo.fly(node).addClass(this.selectedClass);
19880 this.selections.push(node);
19881 if(!suppressEvent){
19882 this.fireEvent("selectionchange", this, this.selections);
19890 * @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
19891 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19892 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19894 unselect : function(nodeInfo, keepExisting, suppressEvent)
19896 if(nodeInfo instanceof Array){
19897 Roo.each(this.selections, function(s) {
19898 this.unselect(s, nodeInfo);
19902 var node = this.getNode(nodeInfo);
19903 if(!node || !this.isSelected(node)){
19904 //Roo.log("not selected");
19905 return; // not selected.
19909 Roo.each(this.selections, function(s) {
19911 Roo.fly(node).removeClass(this.selectedClass);
19918 this.selections= ns;
19919 this.fireEvent("selectionchange", this, this.selections);
19923 * Gets a template node.
19924 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19925 * @return {HTMLElement} The node or null if it wasn't found
19927 getNode : function(nodeInfo){
19928 if(typeof nodeInfo == "string"){
19929 return document.getElementById(nodeInfo);
19930 }else if(typeof nodeInfo == "number"){
19931 return this.nodes[nodeInfo];
19937 * Gets a range template nodes.
19938 * @param {Number} startIndex
19939 * @param {Number} endIndex
19940 * @return {Array} An array of nodes
19942 getNodes : function(start, end){
19943 var ns = this.nodes;
19944 start = start || 0;
19945 end = typeof end == "undefined" ? ns.length - 1 : end;
19948 for(var i = start; i <= end; i++){
19952 for(var i = start; i >= end; i--){
19960 * Finds the index of the passed node
19961 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19962 * @return {Number} The index of the node or -1
19964 indexOf : function(node){
19965 node = this.getNode(node);
19966 if(typeof node.nodeIndex == "number"){
19967 return node.nodeIndex;
19969 var ns = this.nodes;
19970 for(var i = 0, len = ns.length; i < len; i++){
19981 * based on jquery fullcalendar
19985 Roo.bootstrap = Roo.bootstrap || {};
19987 * @class Roo.bootstrap.Calendar
19988 * @extends Roo.bootstrap.Component
19989 * Bootstrap Calendar class
19990 * @cfg {Boolean} loadMask (true|false) default false
19991 * @cfg {Object} header generate the user specific header of the calendar, default false
19994 * Create a new Container
19995 * @param {Object} config The config object
20000 Roo.bootstrap.Calendar = function(config){
20001 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20005 * Fires when a date is selected
20006 * @param {DatePicker} this
20007 * @param {Date} date The selected date
20011 * @event monthchange
20012 * Fires when the displayed month changes
20013 * @param {DatePicker} this
20014 * @param {Date} date The selected month
20016 'monthchange': true,
20018 * @event evententer
20019 * Fires when mouse over an event
20020 * @param {Calendar} this
20021 * @param {event} Event
20023 'evententer': true,
20025 * @event eventleave
20026 * Fires when the mouse leaves an
20027 * @param {Calendar} this
20030 'eventleave': true,
20032 * @event eventclick
20033 * Fires when the mouse click an
20034 * @param {Calendar} this
20043 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20046 * @cfg {Number} startDay
20047 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20055 getAutoCreate : function(){
20058 var fc_button = function(name, corner, style, content ) {
20059 return Roo.apply({},{
20061 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20063 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20066 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20077 style : 'width:100%',
20084 cls : 'fc-header-left',
20086 fc_button('prev', 'left', 'arrow', '‹' ),
20087 fc_button('next', 'right', 'arrow', '›' ),
20088 { tag: 'span', cls: 'fc-header-space' },
20089 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20097 cls : 'fc-header-center',
20101 cls: 'fc-header-title',
20104 html : 'month / year'
20112 cls : 'fc-header-right',
20114 /* fc_button('month', 'left', '', 'month' ),
20115 fc_button('week', '', '', 'week' ),
20116 fc_button('day', 'right', '', 'day' )
20128 header = this.header;
20131 var cal_heads = function() {
20133 // fixme - handle this.
20135 for (var i =0; i < Date.dayNames.length; i++) {
20136 var d = Date.dayNames[i];
20139 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20140 html : d.substring(0,3)
20144 ret[0].cls += ' fc-first';
20145 ret[6].cls += ' fc-last';
20148 var cal_cell = function(n) {
20151 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20156 cls: 'fc-day-number',
20160 cls: 'fc-day-content',
20164 style: 'position: relative;' // height: 17px;
20176 var cal_rows = function() {
20179 for (var r = 0; r < 6; r++) {
20186 for (var i =0; i < Date.dayNames.length; i++) {
20187 var d = Date.dayNames[i];
20188 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20191 row.cn[0].cls+=' fc-first';
20192 row.cn[0].cn[0].style = 'min-height:90px';
20193 row.cn[6].cls+=' fc-last';
20197 ret[0].cls += ' fc-first';
20198 ret[4].cls += ' fc-prev-last';
20199 ret[5].cls += ' fc-last';
20206 cls: 'fc-border-separate',
20207 style : 'width:100%',
20215 cls : 'fc-first fc-last',
20233 cls : 'fc-content',
20234 style : "position: relative;",
20237 cls : 'fc-view fc-view-month fc-grid',
20238 style : 'position: relative',
20239 unselectable : 'on',
20242 cls : 'fc-event-container',
20243 style : 'position:absolute;z-index:8;top:0;left:0;'
20261 initEvents : function()
20264 throw "can not find store for calendar";
20270 style: "text-align:center",
20274 style: "background-color:white;width:50%;margin:250 auto",
20278 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20289 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20291 var size = this.el.select('.fc-content', true).first().getSize();
20292 this.maskEl.setSize(size.width, size.height);
20293 this.maskEl.enableDisplayMode("block");
20294 if(!this.loadMask){
20295 this.maskEl.hide();
20298 this.store = Roo.factory(this.store, Roo.data);
20299 this.store.on('load', this.onLoad, this);
20300 this.store.on('beforeload', this.onBeforeLoad, this);
20304 this.cells = this.el.select('.fc-day',true);
20305 //Roo.log(this.cells);
20306 this.textNodes = this.el.query('.fc-day-number');
20307 this.cells.addClassOnOver('fc-state-hover');
20309 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20310 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20311 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20312 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20314 this.on('monthchange', this.onMonthChange, this);
20316 this.update(new Date().clearTime());
20319 resize : function() {
20320 var sz = this.el.getSize();
20322 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20323 this.el.select('.fc-day-content div',true).setHeight(34);
20328 showPrevMonth : function(e){
20329 this.update(this.activeDate.add("mo", -1));
20331 showToday : function(e){
20332 this.update(new Date().clearTime());
20335 showNextMonth : function(e){
20336 this.update(this.activeDate.add("mo", 1));
20340 showPrevYear : function(){
20341 this.update(this.activeDate.add("y", -1));
20345 showNextYear : function(){
20346 this.update(this.activeDate.add("y", 1));
20351 update : function(date)
20353 var vd = this.activeDate;
20354 this.activeDate = date;
20355 // if(vd && this.el){
20356 // var t = date.getTime();
20357 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20358 // Roo.log('using add remove');
20360 // this.fireEvent('monthchange', this, date);
20362 // this.cells.removeClass("fc-state-highlight");
20363 // this.cells.each(function(c){
20364 // if(c.dateValue == t){
20365 // c.addClass("fc-state-highlight");
20366 // setTimeout(function(){
20367 // try{c.dom.firstChild.focus();}catch(e){}
20377 var days = date.getDaysInMonth();
20379 var firstOfMonth = date.getFirstDateOfMonth();
20380 var startingPos = firstOfMonth.getDay()-this.startDay;
20382 if(startingPos < this.startDay){
20386 var pm = date.add(Date.MONTH, -1);
20387 var prevStart = pm.getDaysInMonth()-startingPos;
20389 this.cells = this.el.select('.fc-day',true);
20390 this.textNodes = this.el.query('.fc-day-number');
20391 this.cells.addClassOnOver('fc-state-hover');
20393 var cells = this.cells.elements;
20394 var textEls = this.textNodes;
20396 Roo.each(cells, function(cell){
20397 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20400 days += startingPos;
20402 // convert everything to numbers so it's fast
20403 var day = 86400000;
20404 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20407 //Roo.log(prevStart);
20409 var today = new Date().clearTime().getTime();
20410 var sel = date.clearTime().getTime();
20411 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20412 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20413 var ddMatch = this.disabledDatesRE;
20414 var ddText = this.disabledDatesText;
20415 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20416 var ddaysText = this.disabledDaysText;
20417 var format = this.format;
20419 var setCellClass = function(cal, cell){
20423 //Roo.log('set Cell Class');
20425 var t = d.getTime();
20429 cell.dateValue = t;
20431 cell.className += " fc-today";
20432 cell.className += " fc-state-highlight";
20433 cell.title = cal.todayText;
20436 // disable highlight in other month..
20437 //cell.className += " fc-state-highlight";
20442 cell.className = " fc-state-disabled";
20443 cell.title = cal.minText;
20447 cell.className = " fc-state-disabled";
20448 cell.title = cal.maxText;
20452 if(ddays.indexOf(d.getDay()) != -1){
20453 cell.title = ddaysText;
20454 cell.className = " fc-state-disabled";
20457 if(ddMatch && format){
20458 var fvalue = d.dateFormat(format);
20459 if(ddMatch.test(fvalue)){
20460 cell.title = ddText.replace("%0", fvalue);
20461 cell.className = " fc-state-disabled";
20465 if (!cell.initialClassName) {
20466 cell.initialClassName = cell.dom.className;
20469 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20474 for(; i < startingPos; i++) {
20475 textEls[i].innerHTML = (++prevStart);
20476 d.setDate(d.getDate()+1);
20478 cells[i].className = "fc-past fc-other-month";
20479 setCellClass(this, cells[i]);
20484 for(; i < days; i++){
20485 intDay = i - startingPos + 1;
20486 textEls[i].innerHTML = (intDay);
20487 d.setDate(d.getDate()+1);
20489 cells[i].className = ''; // "x-date-active";
20490 setCellClass(this, cells[i]);
20494 for(; i < 42; i++) {
20495 textEls[i].innerHTML = (++extraDays);
20496 d.setDate(d.getDate()+1);
20498 cells[i].className = "fc-future fc-other-month";
20499 setCellClass(this, cells[i]);
20502 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20504 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20506 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20507 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20509 if(totalRows != 6){
20510 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20511 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20514 this.fireEvent('monthchange', this, date);
20518 if(!this.internalRender){
20519 var main = this.el.dom.firstChild;
20520 var w = main.offsetWidth;
20521 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20522 Roo.fly(main).setWidth(w);
20523 this.internalRender = true;
20524 // opera does not respect the auto grow header center column
20525 // then, after it gets a width opera refuses to recalculate
20526 // without a second pass
20527 if(Roo.isOpera && !this.secondPass){
20528 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20529 this.secondPass = true;
20530 this.update.defer(10, this, [date]);
20537 findCell : function(dt) {
20538 dt = dt.clearTime().getTime();
20540 this.cells.each(function(c){
20541 //Roo.log("check " +c.dateValue + '?=' + dt);
20542 if(c.dateValue == dt){
20552 findCells : function(ev) {
20553 var s = ev.start.clone().clearTime().getTime();
20555 var e= ev.end.clone().clearTime().getTime();
20558 this.cells.each(function(c){
20559 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20561 if(c.dateValue > e){
20564 if(c.dateValue < s){
20573 // findBestRow: function(cells)
20577 // for (var i =0 ; i < cells.length;i++) {
20578 // ret = Math.max(cells[i].rows || 0,ret);
20585 addItem : function(ev)
20587 // look for vertical location slot in
20588 var cells = this.findCells(ev);
20590 // ev.row = this.findBestRow(cells);
20592 // work out the location.
20596 for(var i =0; i < cells.length; i++) {
20598 cells[i].row = cells[0].row;
20601 cells[i].row = cells[i].row + 1;
20611 if (crow.start.getY() == cells[i].getY()) {
20613 crow.end = cells[i];
20630 cells[0].events.push(ev);
20632 this.calevents.push(ev);
20635 clearEvents: function() {
20637 if(!this.calevents){
20641 Roo.each(this.cells.elements, function(c){
20647 Roo.each(this.calevents, function(e) {
20648 Roo.each(e.els, function(el) {
20649 el.un('mouseenter' ,this.onEventEnter, this);
20650 el.un('mouseleave' ,this.onEventLeave, this);
20655 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20661 renderEvents: function()
20665 this.cells.each(function(c) {
20674 if(c.row != c.events.length){
20675 r = 4 - (4 - (c.row - c.events.length));
20678 c.events = ev.slice(0, r);
20679 c.more = ev.slice(r);
20681 if(c.more.length && c.more.length == 1){
20682 c.events.push(c.more.pop());
20685 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20689 this.cells.each(function(c) {
20691 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20694 for (var e = 0; e < c.events.length; e++){
20695 var ev = c.events[e];
20696 var rows = ev.rows;
20698 for(var i = 0; i < rows.length; i++) {
20700 // how many rows should it span..
20703 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20704 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20706 unselectable : "on",
20709 cls: 'fc-event-inner',
20713 // cls: 'fc-event-time',
20714 // html : cells.length > 1 ? '' : ev.time
20718 cls: 'fc-event-title',
20719 html : String.format('{0}', ev.title)
20726 cls: 'ui-resizable-handle ui-resizable-e',
20727 html : '  '
20734 cfg.cls += ' fc-event-start';
20736 if ((i+1) == rows.length) {
20737 cfg.cls += ' fc-event-end';
20740 var ctr = _this.el.select('.fc-event-container',true).first();
20741 var cg = ctr.createChild(cfg);
20743 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20744 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20746 var r = (c.more.length) ? 1 : 0;
20747 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20748 cg.setWidth(ebox.right - sbox.x -2);
20750 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20751 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20752 cg.on('click', _this.onEventClick, _this, ev);
20763 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20764 style : 'position: absolute',
20765 unselectable : "on",
20768 cls: 'fc-event-inner',
20772 cls: 'fc-event-title',
20780 cls: 'ui-resizable-handle ui-resizable-e',
20781 html : '  '
20787 var ctr = _this.el.select('.fc-event-container',true).first();
20788 var cg = ctr.createChild(cfg);
20790 var sbox = c.select('.fc-day-content',true).first().getBox();
20791 var ebox = c.select('.fc-day-content',true).first().getBox();
20793 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20794 cg.setWidth(ebox.right - sbox.x -2);
20796 cg.on('click', _this.onMoreEventClick, _this, c.more);
20806 onEventEnter: function (e, el,event,d) {
20807 this.fireEvent('evententer', this, el, event);
20810 onEventLeave: function (e, el,event,d) {
20811 this.fireEvent('eventleave', this, el, event);
20814 onEventClick: function (e, el,event,d) {
20815 this.fireEvent('eventclick', this, el, event);
20818 onMonthChange: function () {
20822 onMoreEventClick: function(e, el, more)
20826 this.calpopover.placement = 'right';
20827 this.calpopover.setTitle('More');
20829 this.calpopover.setContent('');
20831 var ctr = this.calpopover.el.select('.popover-content', true).first();
20833 Roo.each(more, function(m){
20835 cls : 'fc-event-hori fc-event-draggable',
20838 var cg = ctr.createChild(cfg);
20840 cg.on('click', _this.onEventClick, _this, m);
20843 this.calpopover.show(el);
20848 onLoad: function ()
20850 this.calevents = [];
20853 if(this.store.getCount() > 0){
20854 this.store.data.each(function(d){
20857 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20858 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20859 time : d.data.start_time,
20860 title : d.data.title,
20861 description : d.data.description,
20862 venue : d.data.venue
20867 this.renderEvents();
20869 if(this.calevents.length && this.loadMask){
20870 this.maskEl.hide();
20874 onBeforeLoad: function()
20876 this.clearEvents();
20878 this.maskEl.show();
20892 * @class Roo.bootstrap.Popover
20893 * @extends Roo.bootstrap.Component
20894 * Bootstrap Popover class
20895 * @cfg {String} html contents of the popover (or false to use children..)
20896 * @cfg {String} title of popover (or false to hide)
20897 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20898 * @cfg {String} trigger click || hover (or false to trigger manually)
20899 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20900 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20901 * - if false and it has a 'parent' then it will be automatically added to that element
20902 * - if string - Roo.get will be called
20903 * @cfg {Number} delay - delay before showing
20906 * Create a new Popover
20907 * @param {Object} config The config object
20910 Roo.bootstrap.Popover = function(config){
20911 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20917 * After the popover show
20919 * @param {Roo.bootstrap.Popover} this
20924 * After the popover hide
20926 * @param {Roo.bootstrap.Popover} this
20932 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20937 placement : 'right',
20938 trigger : 'hover', // hover
20944 can_build_overlaid : false,
20946 maskEl : false, // the mask element
20949 alignEl : false, // when show is called with an element - this get's stored.
20951 getChildContainer : function()
20953 return this.contentEl;
20956 getPopoverHeader : function()
20958 this.title = true; // flag not to hide it..
20959 this.headerEl.addClass('p-0');
20960 return this.headerEl
20964 getAutoCreate : function(){
20967 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20968 style: 'display:block',
20974 cls : 'popover-inner ',
20978 cls: 'popover-title popover-header',
20979 html : this.title === false ? '' : this.title
20982 cls : 'popover-content popover-body ' + (this.cls || ''),
20983 html : this.html || ''
20994 * @param {string} the title
20996 setTitle: function(str)
21000 this.headerEl.dom.innerHTML = str;
21005 * @param {string} the body content
21007 setContent: function(str)
21010 if (this.contentEl) {
21011 this.contentEl.dom.innerHTML = str;
21015 // as it get's added to the bottom of the page.
21016 onRender : function(ct, position)
21018 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21023 var cfg = Roo.apply({}, this.getAutoCreate());
21027 cfg.cls += ' ' + this.cls;
21030 cfg.style = this.style;
21032 //Roo.log("adding to ");
21033 this.el = Roo.get(document.body).createChild(cfg, position);
21034 // Roo.log(this.el);
21037 this.contentEl = this.el.select('.popover-content',true).first();
21038 this.headerEl = this.el.select('.popover-title',true).first();
21041 if(typeof(this.items) != 'undefined'){
21042 var items = this.items;
21045 for(var i =0;i < items.length;i++) {
21046 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21050 this.items = nitems;
21052 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21053 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21060 resizeMask : function()
21062 this.maskEl.setSize(
21063 Roo.lib.Dom.getViewWidth(true),
21064 Roo.lib.Dom.getViewHeight(true)
21068 initEvents : function()
21072 Roo.bootstrap.Popover.register(this);
21075 this.arrowEl = this.el.select('.arrow',true).first();
21076 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21077 this.el.enableDisplayMode('block');
21081 if (this.over === false && !this.parent()) {
21084 if (this.triggers === false) {
21089 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21090 var triggers = this.trigger ? this.trigger.split(' ') : [];
21091 Roo.each(triggers, function(trigger) {
21093 if (trigger == 'click') {
21094 on_el.on('click', this.toggle, this);
21095 } else if (trigger != 'manual') {
21096 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21097 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21099 on_el.on(eventIn ,this.enter, this);
21100 on_el.on(eventOut, this.leave, this);
21110 toggle : function () {
21111 this.hoverState == 'in' ? this.leave() : this.enter();
21114 enter : function () {
21116 clearTimeout(this.timeout);
21118 this.hoverState = 'in';
21120 if (!this.delay || !this.delay.show) {
21125 this.timeout = setTimeout(function () {
21126 if (_t.hoverState == 'in') {
21129 }, this.delay.show)
21132 leave : function() {
21133 clearTimeout(this.timeout);
21135 this.hoverState = 'out';
21137 if (!this.delay || !this.delay.hide) {
21142 this.timeout = setTimeout(function () {
21143 if (_t.hoverState == 'out') {
21146 }, this.delay.hide)
21150 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21151 * @param {string} (left|right|top|bottom) position
21153 show : function (on_el, placement)
21155 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21156 on_el = on_el || false; // default to false
21159 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21160 on_el = this.parent().el;
21161 } else if (this.over) {
21162 on_el = Roo.get(this.over);
21167 this.alignEl = Roo.get( on_el );
21170 this.render(document.body);
21176 if (this.title === false) {
21177 this.headerEl.hide();
21182 this.el.dom.style.display = 'block';
21185 if (this.alignEl) {
21186 this.updatePosition(this.placement, true);
21189 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21190 var es = this.el.getSize();
21191 var x = Roo.lib.Dom.getViewWidth()/2;
21192 var y = Roo.lib.Dom.getViewHeight()/2;
21193 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21198 //var arrow = this.el.select('.arrow',true).first();
21199 //arrow.set(align[2],
21201 this.el.addClass('in');
21205 this.hoverState = 'in';
21208 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21209 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21210 this.maskEl.dom.style.display = 'block';
21211 this.maskEl.addClass('show');
21213 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21215 this.fireEvent('show', this);
21219 * fire this manually after loading a grid in the table for example
21220 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21221 * @param {Boolean} try and move it if we cant get right position.
21223 updatePosition : function(placement, try_move)
21225 // allow for calling with no parameters
21226 placement = placement ? placement : this.placement;
21227 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21229 this.el.removeClass([
21230 'fade','top','bottom', 'left', 'right','in',
21231 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21233 this.el.addClass(placement + ' bs-popover-' + placement);
21235 if (!this.alignEl ) {
21239 switch (placement) {
21241 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21242 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21243 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21244 //normal display... or moved up/down.
21245 this.el.setXY(offset);
21246 var xy = this.alignEl.getAnchorXY('tr', false);
21248 this.arrowEl.setXY(xy);
21251 // continue through...
21252 return this.updatePosition('left', false);
21256 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21257 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21258 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21259 //normal display... or moved up/down.
21260 this.el.setXY(offset);
21261 var xy = this.alignEl.getAnchorXY('tl', false);
21262 xy[0]-=10;xy[1]+=5; // << fix me
21263 this.arrowEl.setXY(xy);
21267 return this.updatePosition('right', false);
21270 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21271 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21272 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21273 //normal display... or moved up/down.
21274 this.el.setXY(offset);
21275 var xy = this.alignEl.getAnchorXY('t', false);
21276 xy[1]-=10; // << fix me
21277 this.arrowEl.setXY(xy);
21281 return this.updatePosition('bottom', false);
21284 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21285 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21286 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21287 //normal display... or moved up/down.
21288 this.el.setXY(offset);
21289 var xy = this.alignEl.getAnchorXY('b', false);
21290 xy[1]+=2; // << fix me
21291 this.arrowEl.setXY(xy);
21295 return this.updatePosition('top', false);
21306 this.el.setXY([0,0]);
21307 this.el.removeClass('in');
21309 this.hoverState = null;
21310 this.maskEl.hide(); // always..
21311 this.fireEvent('hide', this);
21317 Roo.apply(Roo.bootstrap.Popover, {
21320 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21321 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21322 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21323 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21328 clickHander : false,
21332 onMouseDown : function(e)
21334 if (this.popups.length && !e.getTarget(".roo-popover")) {
21335 /// what is nothing is showing..
21344 register : function(popup)
21346 if (!Roo.bootstrap.Popover.clickHandler) {
21347 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21349 // hide other popups.
21350 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21351 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21352 this.hideAll(); //<< why?
21353 //this.popups.push(popup);
21355 hideAll : function()
21357 this.popups.forEach(function(p) {
21361 onShow : function() {
21362 Roo.bootstrap.Popover.popups.push(this);
21364 onHide : function() {
21365 Roo.bootstrap.Popover.popups.remove(this);
21371 * Card header - holder for the card header elements.
21376 * @class Roo.bootstrap.PopoverNav
21377 * @extends Roo.bootstrap.NavGroup
21378 * Bootstrap Popover header navigation class
21380 * Create a new Popover Header Navigation
21381 * @param {Object} config The config object
21384 Roo.bootstrap.PopoverNav = function(config){
21385 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21388 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21391 container_method : 'getPopoverHeader'
21409 * @class Roo.bootstrap.Progress
21410 * @extends Roo.bootstrap.Component
21411 * Bootstrap Progress class
21412 * @cfg {Boolean} striped striped of the progress bar
21413 * @cfg {Boolean} active animated of the progress bar
21417 * Create a new Progress
21418 * @param {Object} config The config object
21421 Roo.bootstrap.Progress = function(config){
21422 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21425 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21430 getAutoCreate : function(){
21438 cfg.cls += ' progress-striped';
21442 cfg.cls += ' active';
21461 * @class Roo.bootstrap.ProgressBar
21462 * @extends Roo.bootstrap.Component
21463 * Bootstrap ProgressBar class
21464 * @cfg {Number} aria_valuenow aria-value now
21465 * @cfg {Number} aria_valuemin aria-value min
21466 * @cfg {Number} aria_valuemax aria-value max
21467 * @cfg {String} label label for the progress bar
21468 * @cfg {String} panel (success | info | warning | danger )
21469 * @cfg {String} role role of the progress bar
21470 * @cfg {String} sr_only text
21474 * Create a new ProgressBar
21475 * @param {Object} config The config object
21478 Roo.bootstrap.ProgressBar = function(config){
21479 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21482 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21486 aria_valuemax : 100,
21492 getAutoCreate : function()
21497 cls: 'progress-bar',
21498 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21510 cfg.role = this.role;
21513 if(this.aria_valuenow){
21514 cfg['aria-valuenow'] = this.aria_valuenow;
21517 if(this.aria_valuemin){
21518 cfg['aria-valuemin'] = this.aria_valuemin;
21521 if(this.aria_valuemax){
21522 cfg['aria-valuemax'] = this.aria_valuemax;
21525 if(this.label && !this.sr_only){
21526 cfg.html = this.label;
21530 cfg.cls += ' progress-bar-' + this.panel;
21536 update : function(aria_valuenow)
21538 this.aria_valuenow = aria_valuenow;
21540 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21555 * @class Roo.bootstrap.TabGroup
21556 * @extends Roo.bootstrap.Column
21557 * Bootstrap Column class
21558 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21559 * @cfg {Boolean} carousel true to make the group behave like a carousel
21560 * @cfg {Boolean} bullets show bullets for the panels
21561 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21562 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21563 * @cfg {Boolean} showarrow (true|false) show arrow default true
21566 * Create a new TabGroup
21567 * @param {Object} config The config object
21570 Roo.bootstrap.TabGroup = function(config){
21571 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21573 this.navId = Roo.id();
21576 Roo.bootstrap.TabGroup.register(this);
21580 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21583 transition : false,
21588 slideOnTouch : false,
21591 getAutoCreate : function()
21593 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21595 cfg.cls += ' tab-content';
21597 if (this.carousel) {
21598 cfg.cls += ' carousel slide';
21601 cls : 'carousel-inner',
21605 if(this.bullets && !Roo.isTouch){
21608 cls : 'carousel-bullets',
21612 if(this.bullets_cls){
21613 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21620 cfg.cn[0].cn.push(bullets);
21623 if(this.showarrow){
21624 cfg.cn[0].cn.push({
21626 class : 'carousel-arrow',
21630 class : 'carousel-prev',
21634 class : 'fa fa-chevron-left'
21640 class : 'carousel-next',
21644 class : 'fa fa-chevron-right'
21657 initEvents: function()
21659 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21660 // this.el.on("touchstart", this.onTouchStart, this);
21663 if(this.autoslide){
21666 this.slideFn = window.setInterval(function() {
21667 _this.showPanelNext();
21671 if(this.showarrow){
21672 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21673 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21679 // onTouchStart : function(e, el, o)
21681 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21685 // this.showPanelNext();
21689 getChildContainer : function()
21691 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21695 * register a Navigation item
21696 * @param {Roo.bootstrap.NavItem} the navitem to add
21698 register : function(item)
21700 this.tabs.push( item);
21701 item.navId = this.navId; // not really needed..
21706 getActivePanel : function()
21709 Roo.each(this.tabs, function(t) {
21719 getPanelByName : function(n)
21722 Roo.each(this.tabs, function(t) {
21723 if (t.tabId == n) {
21731 indexOfPanel : function(p)
21734 Roo.each(this.tabs, function(t,i) {
21735 if (t.tabId == p.tabId) {
21744 * show a specific panel
21745 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21746 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21748 showPanel : function (pan)
21750 if(this.transition || typeof(pan) == 'undefined'){
21751 Roo.log("waiting for the transitionend");
21755 if (typeof(pan) == 'number') {
21756 pan = this.tabs[pan];
21759 if (typeof(pan) == 'string') {
21760 pan = this.getPanelByName(pan);
21763 var cur = this.getActivePanel();
21766 Roo.log('pan or acitve pan is undefined');
21770 if (pan.tabId == this.getActivePanel().tabId) {
21774 if (false === cur.fireEvent('beforedeactivate')) {
21778 if(this.bullets > 0 && !Roo.isTouch){
21779 this.setActiveBullet(this.indexOfPanel(pan));
21782 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21784 //class="carousel-item carousel-item-next carousel-item-left"
21786 this.transition = true;
21787 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21788 var lr = dir == 'next' ? 'left' : 'right';
21789 pan.el.addClass(dir); // or prev
21790 pan.el.addClass('carousel-item-' + dir); // or prev
21791 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21792 cur.el.addClass(lr); // or right
21793 pan.el.addClass(lr);
21794 cur.el.addClass('carousel-item-' +lr); // or right
21795 pan.el.addClass('carousel-item-' +lr);
21799 cur.el.on('transitionend', function() {
21800 Roo.log("trans end?");
21802 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21803 pan.setActive(true);
21805 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21806 cur.setActive(false);
21808 _this.transition = false;
21810 }, this, { single: true } );
21815 cur.setActive(false);
21816 pan.setActive(true);
21821 showPanelNext : function()
21823 var i = this.indexOfPanel(this.getActivePanel());
21825 if (i >= this.tabs.length - 1 && !this.autoslide) {
21829 if (i >= this.tabs.length - 1 && this.autoslide) {
21833 this.showPanel(this.tabs[i+1]);
21836 showPanelPrev : function()
21838 var i = this.indexOfPanel(this.getActivePanel());
21840 if (i < 1 && !this.autoslide) {
21844 if (i < 1 && this.autoslide) {
21845 i = this.tabs.length;
21848 this.showPanel(this.tabs[i-1]);
21852 addBullet: function()
21854 if(!this.bullets || Roo.isTouch){
21857 var ctr = this.el.select('.carousel-bullets',true).first();
21858 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21859 var bullet = ctr.createChild({
21860 cls : 'bullet bullet-' + i
21861 },ctr.dom.lastChild);
21866 bullet.on('click', (function(e, el, o, ii, t){
21868 e.preventDefault();
21870 this.showPanel(ii);
21872 if(this.autoslide && this.slideFn){
21873 clearInterval(this.slideFn);
21874 this.slideFn = window.setInterval(function() {
21875 _this.showPanelNext();
21879 }).createDelegate(this, [i, bullet], true));
21884 setActiveBullet : function(i)
21890 Roo.each(this.el.select('.bullet', true).elements, function(el){
21891 el.removeClass('selected');
21894 var bullet = this.el.select('.bullet-' + i, true).first();
21900 bullet.addClass('selected');
21911 Roo.apply(Roo.bootstrap.TabGroup, {
21915 * register a Navigation Group
21916 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21918 register : function(navgrp)
21920 this.groups[navgrp.navId] = navgrp;
21924 * fetch a Navigation Group based on the navigation ID
21925 * if one does not exist , it will get created.
21926 * @param {string} the navgroup to add
21927 * @returns {Roo.bootstrap.NavGroup} the navgroup
21929 get: function(navId) {
21930 if (typeof(this.groups[navId]) == 'undefined') {
21931 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21933 return this.groups[navId] ;
21948 * @class Roo.bootstrap.TabPanel
21949 * @extends Roo.bootstrap.Component
21950 * Bootstrap TabPanel class
21951 * @cfg {Boolean} active panel active
21952 * @cfg {String} html panel content
21953 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21954 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21955 * @cfg {String} href click to link..
21956 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21960 * Create a new TabPanel
21961 * @param {Object} config The config object
21964 Roo.bootstrap.TabPanel = function(config){
21965 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21969 * Fires when the active status changes
21970 * @param {Roo.bootstrap.TabPanel} this
21971 * @param {Boolean} state the new state
21976 * @event beforedeactivate
21977 * Fires before a tab is de-activated - can be used to do validation on a form.
21978 * @param {Roo.bootstrap.TabPanel} this
21979 * @return {Boolean} false if there is an error
21982 'beforedeactivate': true
21985 this.tabId = this.tabId || Roo.id();
21989 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21996 touchSlide : false,
21997 getAutoCreate : function(){
22002 // item is needed for carousel - not sure if it has any effect otherwise
22003 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22004 html: this.html || ''
22008 cfg.cls += ' active';
22012 cfg.tabId = this.tabId;
22020 initEvents: function()
22022 var p = this.parent();
22024 this.navId = this.navId || p.navId;
22026 if (typeof(this.navId) != 'undefined') {
22027 // not really needed.. but just in case.. parent should be a NavGroup.
22028 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22032 var i = tg.tabs.length - 1;
22034 if(this.active && tg.bullets > 0 && i < tg.bullets){
22035 tg.setActiveBullet(i);
22039 this.el.on('click', this.onClick, this);
22041 if(Roo.isTouch && this.touchSlide){
22042 this.el.on("touchstart", this.onTouchStart, this);
22043 this.el.on("touchmove", this.onTouchMove, this);
22044 this.el.on("touchend", this.onTouchEnd, this);
22049 onRender : function(ct, position)
22051 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22054 setActive : function(state)
22056 Roo.log("panel - set active " + this.tabId + "=" + state);
22058 this.active = state;
22060 this.el.removeClass('active');
22062 } else if (!this.el.hasClass('active')) {
22063 this.el.addClass('active');
22066 this.fireEvent('changed', this, state);
22069 onClick : function(e)
22071 e.preventDefault();
22073 if(!this.href.length){
22077 window.location.href = this.href;
22086 onTouchStart : function(e)
22088 this.swiping = false;
22090 this.startX = e.browserEvent.touches[0].clientX;
22091 this.startY = e.browserEvent.touches[0].clientY;
22094 onTouchMove : function(e)
22096 this.swiping = true;
22098 this.endX = e.browserEvent.touches[0].clientX;
22099 this.endY = e.browserEvent.touches[0].clientY;
22102 onTouchEnd : function(e)
22109 var tabGroup = this.parent();
22111 if(this.endX > this.startX){ // swiping right
22112 tabGroup.showPanelPrev();
22116 if(this.startX > this.endX){ // swiping left
22117 tabGroup.showPanelNext();
22136 * @class Roo.bootstrap.DateField
22137 * @extends Roo.bootstrap.Input
22138 * Bootstrap DateField class
22139 * @cfg {Number} weekStart default 0
22140 * @cfg {String} viewMode default empty, (months|years)
22141 * @cfg {String} minViewMode default empty, (months|years)
22142 * @cfg {Number} startDate default -Infinity
22143 * @cfg {Number} endDate default Infinity
22144 * @cfg {Boolean} todayHighlight default false
22145 * @cfg {Boolean} todayBtn default false
22146 * @cfg {Boolean} calendarWeeks default false
22147 * @cfg {Object} daysOfWeekDisabled default empty
22148 * @cfg {Boolean} singleMode default false (true | false)
22150 * @cfg {Boolean} keyboardNavigation default true
22151 * @cfg {String} language default en
22154 * Create a new DateField
22155 * @param {Object} config The config object
22158 Roo.bootstrap.DateField = function(config){
22159 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22163 * Fires when this field show.
22164 * @param {Roo.bootstrap.DateField} this
22165 * @param {Mixed} date The date value
22170 * Fires when this field hide.
22171 * @param {Roo.bootstrap.DateField} this
22172 * @param {Mixed} date The date value
22177 * Fires when select a date.
22178 * @param {Roo.bootstrap.DateField} this
22179 * @param {Mixed} date The date value
22183 * @event beforeselect
22184 * Fires when before select a date.
22185 * @param {Roo.bootstrap.DateField} this
22186 * @param {Mixed} date The date value
22188 beforeselect : true
22192 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22195 * @cfg {String} format
22196 * The default date format string which can be overriden for localization support. The format must be
22197 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22201 * @cfg {String} altFormats
22202 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22203 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22205 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22213 todayHighlight : false,
22219 keyboardNavigation: true,
22221 calendarWeeks: false,
22223 startDate: -Infinity,
22227 daysOfWeekDisabled: [],
22231 singleMode : false,
22233 UTCDate: function()
22235 return new Date(Date.UTC.apply(Date, arguments));
22238 UTCToday: function()
22240 var today = new Date();
22241 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22244 getDate: function() {
22245 var d = this.getUTCDate();
22246 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22249 getUTCDate: function() {
22253 setDate: function(d) {
22254 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22257 setUTCDate: function(d) {
22259 this.setValue(this.formatDate(this.date));
22262 onRender: function(ct, position)
22265 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22267 this.language = this.language || 'en';
22268 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22269 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22271 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22272 this.format = this.format || 'm/d/y';
22273 this.isInline = false;
22274 this.isInput = true;
22275 this.component = this.el.select('.add-on', true).first() || false;
22276 this.component = (this.component && this.component.length === 0) ? false : this.component;
22277 this.hasInput = this.component && this.inputEl().length;
22279 if (typeof(this.minViewMode === 'string')) {
22280 switch (this.minViewMode) {
22282 this.minViewMode = 1;
22285 this.minViewMode = 2;
22288 this.minViewMode = 0;
22293 if (typeof(this.viewMode === 'string')) {
22294 switch (this.viewMode) {
22307 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22309 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22311 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22313 this.picker().on('mousedown', this.onMousedown, this);
22314 this.picker().on('click', this.onClick, this);
22316 this.picker().addClass('datepicker-dropdown');
22318 this.startViewMode = this.viewMode;
22320 if(this.singleMode){
22321 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22322 v.setVisibilityMode(Roo.Element.DISPLAY);
22326 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22327 v.setStyle('width', '189px');
22331 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22332 if(!this.calendarWeeks){
22337 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22338 v.attr('colspan', function(i, val){
22339 return parseInt(val) + 1;
22344 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22346 this.setStartDate(this.startDate);
22347 this.setEndDate(this.endDate);
22349 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22356 if(this.isInline) {
22361 picker : function()
22363 return this.pickerEl;
22364 // return this.el.select('.datepicker', true).first();
22367 fillDow: function()
22369 var dowCnt = this.weekStart;
22378 if(this.calendarWeeks){
22386 while (dowCnt < this.weekStart + 7) {
22390 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22394 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22397 fillMonths: function()
22400 var months = this.picker().select('>.datepicker-months td', true).first();
22402 months.dom.innerHTML = '';
22408 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22411 months.createChild(month);
22418 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;
22420 if (this.date < this.startDate) {
22421 this.viewDate = new Date(this.startDate);
22422 } else if (this.date > this.endDate) {
22423 this.viewDate = new Date(this.endDate);
22425 this.viewDate = new Date(this.date);
22433 var d = new Date(this.viewDate),
22434 year = d.getUTCFullYear(),
22435 month = d.getUTCMonth(),
22436 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22437 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22438 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22439 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22440 currentDate = this.date && this.date.valueOf(),
22441 today = this.UTCToday();
22443 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22445 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22447 // this.picker.select('>tfoot th.today').
22448 // .text(dates[this.language].today)
22449 // .toggle(this.todayBtn !== false);
22451 this.updateNavArrows();
22454 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22456 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22458 prevMonth.setUTCDate(day);
22460 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22462 var nextMonth = new Date(prevMonth);
22464 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22466 nextMonth = nextMonth.valueOf();
22468 var fillMonths = false;
22470 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22472 while(prevMonth.valueOf() <= nextMonth) {
22475 if (prevMonth.getUTCDay() === this.weekStart) {
22477 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22485 if(this.calendarWeeks){
22486 // ISO 8601: First week contains first thursday.
22487 // ISO also states week starts on Monday, but we can be more abstract here.
22489 // Start of current week: based on weekstart/current date
22490 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22491 // Thursday of this week
22492 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22493 // First Thursday of year, year from thursday
22494 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22495 // Calendar week: ms between thursdays, div ms per day, div 7 days
22496 calWeek = (th - yth) / 864e5 / 7 + 1;
22498 fillMonths.cn.push({
22506 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22508 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22511 if (this.todayHighlight &&
22512 prevMonth.getUTCFullYear() == today.getFullYear() &&
22513 prevMonth.getUTCMonth() == today.getMonth() &&
22514 prevMonth.getUTCDate() == today.getDate()) {
22515 clsName += ' today';
22518 if (currentDate && prevMonth.valueOf() === currentDate) {
22519 clsName += ' active';
22522 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22523 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22524 clsName += ' disabled';
22527 fillMonths.cn.push({
22529 cls: 'day ' + clsName,
22530 html: prevMonth.getDate()
22533 prevMonth.setDate(prevMonth.getDate()+1);
22536 var currentYear = this.date && this.date.getUTCFullYear();
22537 var currentMonth = this.date && this.date.getUTCMonth();
22539 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22541 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22542 v.removeClass('active');
22544 if(currentYear === year && k === currentMonth){
22545 v.addClass('active');
22548 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22549 v.addClass('disabled');
22555 year = parseInt(year/10, 10) * 10;
22557 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22559 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22562 for (var i = -1; i < 11; i++) {
22563 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22565 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22573 showMode: function(dir)
22576 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22579 Roo.each(this.picker().select('>div',true).elements, function(v){
22580 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22583 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22588 if(this.isInline) {
22592 this.picker().removeClass(['bottom', 'top']);
22594 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22596 * place to the top of element!
22600 this.picker().addClass('top');
22601 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22606 this.picker().addClass('bottom');
22608 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22611 parseDate : function(value)
22613 if(!value || value instanceof Date){
22616 var v = Date.parseDate(value, this.format);
22617 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22618 v = Date.parseDate(value, 'Y-m-d');
22620 if(!v && this.altFormats){
22621 if(!this.altFormatsArray){
22622 this.altFormatsArray = this.altFormats.split("|");
22624 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22625 v = Date.parseDate(value, this.altFormatsArray[i]);
22631 formatDate : function(date, fmt)
22633 return (!date || !(date instanceof Date)) ?
22634 date : date.dateFormat(fmt || this.format);
22637 onFocus : function()
22639 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22643 onBlur : function()
22645 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22647 var d = this.inputEl().getValue();
22654 showPopup : function()
22656 this.picker().show();
22660 this.fireEvent('showpopup', this, this.date);
22663 hidePopup : function()
22665 if(this.isInline) {
22668 this.picker().hide();
22669 this.viewMode = this.startViewMode;
22672 this.fireEvent('hidepopup', this, this.date);
22676 onMousedown: function(e)
22678 e.stopPropagation();
22679 e.preventDefault();
22684 Roo.bootstrap.DateField.superclass.keyup.call(this);
22688 setValue: function(v)
22690 if(this.fireEvent('beforeselect', this, v) !== false){
22691 var d = new Date(this.parseDate(v) ).clearTime();
22693 if(isNaN(d.getTime())){
22694 this.date = this.viewDate = '';
22695 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22699 v = this.formatDate(d);
22701 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22703 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22707 this.fireEvent('select', this, this.date);
22711 getValue: function()
22713 return this.formatDate(this.date);
22716 fireKey: function(e)
22718 if (!this.picker().isVisible()){
22719 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22725 var dateChanged = false,
22727 newDate, newViewDate;
22732 e.preventDefault();
22736 if (!this.keyboardNavigation) {
22739 dir = e.keyCode == 37 ? -1 : 1;
22742 newDate = this.moveYear(this.date, dir);
22743 newViewDate = this.moveYear(this.viewDate, dir);
22744 } else if (e.shiftKey){
22745 newDate = this.moveMonth(this.date, dir);
22746 newViewDate = this.moveMonth(this.viewDate, dir);
22748 newDate = new Date(this.date);
22749 newDate.setUTCDate(this.date.getUTCDate() + dir);
22750 newViewDate = new Date(this.viewDate);
22751 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22753 if (this.dateWithinRange(newDate)){
22754 this.date = newDate;
22755 this.viewDate = newViewDate;
22756 this.setValue(this.formatDate(this.date));
22758 e.preventDefault();
22759 dateChanged = true;
22764 if (!this.keyboardNavigation) {
22767 dir = e.keyCode == 38 ? -1 : 1;
22769 newDate = this.moveYear(this.date, dir);
22770 newViewDate = this.moveYear(this.viewDate, dir);
22771 } else if (e.shiftKey){
22772 newDate = this.moveMonth(this.date, dir);
22773 newViewDate = this.moveMonth(this.viewDate, dir);
22775 newDate = new Date(this.date);
22776 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22777 newViewDate = new Date(this.viewDate);
22778 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22780 if (this.dateWithinRange(newDate)){
22781 this.date = newDate;
22782 this.viewDate = newViewDate;
22783 this.setValue(this.formatDate(this.date));
22785 e.preventDefault();
22786 dateChanged = true;
22790 this.setValue(this.formatDate(this.date));
22792 e.preventDefault();
22795 this.setValue(this.formatDate(this.date));
22809 onClick: function(e)
22811 e.stopPropagation();
22812 e.preventDefault();
22814 var target = e.getTarget();
22816 if(target.nodeName.toLowerCase() === 'i'){
22817 target = Roo.get(target).dom.parentNode;
22820 var nodeName = target.nodeName;
22821 var className = target.className;
22822 var html = target.innerHTML;
22823 //Roo.log(nodeName);
22825 switch(nodeName.toLowerCase()) {
22827 switch(className) {
22833 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22834 switch(this.viewMode){
22836 this.viewDate = this.moveMonth(this.viewDate, dir);
22840 this.viewDate = this.moveYear(this.viewDate, dir);
22846 var date = new Date();
22847 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22849 this.setValue(this.formatDate(this.date));
22856 if (className.indexOf('disabled') < 0) {
22857 if (!this.viewDate) {
22858 this.viewDate = new Date();
22860 this.viewDate.setUTCDate(1);
22861 if (className.indexOf('month') > -1) {
22862 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22864 var year = parseInt(html, 10) || 0;
22865 this.viewDate.setUTCFullYear(year);
22869 if(this.singleMode){
22870 this.setValue(this.formatDate(this.viewDate));
22881 //Roo.log(className);
22882 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22883 var day = parseInt(html, 10) || 1;
22884 var year = (this.viewDate || new Date()).getUTCFullYear(),
22885 month = (this.viewDate || new Date()).getUTCMonth();
22887 if (className.indexOf('old') > -1) {
22894 } else if (className.indexOf('new') > -1) {
22902 //Roo.log([year,month,day]);
22903 this.date = this.UTCDate(year, month, day,0,0,0,0);
22904 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22906 //Roo.log(this.formatDate(this.date));
22907 this.setValue(this.formatDate(this.date));
22914 setStartDate: function(startDate)
22916 this.startDate = startDate || -Infinity;
22917 if (this.startDate !== -Infinity) {
22918 this.startDate = this.parseDate(this.startDate);
22921 this.updateNavArrows();
22924 setEndDate: function(endDate)
22926 this.endDate = endDate || Infinity;
22927 if (this.endDate !== Infinity) {
22928 this.endDate = this.parseDate(this.endDate);
22931 this.updateNavArrows();
22934 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22936 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22937 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22938 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22940 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22941 return parseInt(d, 10);
22944 this.updateNavArrows();
22947 updateNavArrows: function()
22949 if(this.singleMode){
22953 var d = new Date(this.viewDate),
22954 year = d.getUTCFullYear(),
22955 month = d.getUTCMonth();
22957 Roo.each(this.picker().select('.prev', true).elements, function(v){
22959 switch (this.viewMode) {
22962 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22968 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22975 Roo.each(this.picker().select('.next', true).elements, function(v){
22977 switch (this.viewMode) {
22980 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22986 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22994 moveMonth: function(date, dir)
22999 var new_date = new Date(date.valueOf()),
23000 day = new_date.getUTCDate(),
23001 month = new_date.getUTCMonth(),
23002 mag = Math.abs(dir),
23004 dir = dir > 0 ? 1 : -1;
23007 // If going back one month, make sure month is not current month
23008 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23010 return new_date.getUTCMonth() == month;
23012 // If going forward one month, make sure month is as expected
23013 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23015 return new_date.getUTCMonth() != new_month;
23017 new_month = month + dir;
23018 new_date.setUTCMonth(new_month);
23019 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23020 if (new_month < 0 || new_month > 11) {
23021 new_month = (new_month + 12) % 12;
23024 // For magnitudes >1, move one month at a time...
23025 for (var i=0; i<mag; i++) {
23026 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23027 new_date = this.moveMonth(new_date, dir);
23029 // ...then reset the day, keeping it in the new month
23030 new_month = new_date.getUTCMonth();
23031 new_date.setUTCDate(day);
23033 return new_month != new_date.getUTCMonth();
23036 // Common date-resetting loop -- if date is beyond end of month, make it
23039 new_date.setUTCDate(--day);
23040 new_date.setUTCMonth(new_month);
23045 moveYear: function(date, dir)
23047 return this.moveMonth(date, dir*12);
23050 dateWithinRange: function(date)
23052 return date >= this.startDate && date <= this.endDate;
23058 this.picker().remove();
23061 validateValue : function(value)
23063 if(this.getVisibilityEl().hasClass('hidden')){
23067 if(value.length < 1) {
23068 if(this.allowBlank){
23074 if(value.length < this.minLength){
23077 if(value.length > this.maxLength){
23081 var vt = Roo.form.VTypes;
23082 if(!vt[this.vtype](value, this)){
23086 if(typeof this.validator == "function"){
23087 var msg = this.validator(value);
23093 if(this.regex && !this.regex.test(value)){
23097 if(typeof(this.parseDate(value)) == 'undefined'){
23101 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23105 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23115 this.date = this.viewDate = '';
23117 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23122 Roo.apply(Roo.bootstrap.DateField, {
23133 html: '<i class="fa fa-arrow-left"/>'
23143 html: '<i class="fa fa-arrow-right"/>'
23185 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23186 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23187 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23188 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23189 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23202 navFnc: 'FullYear',
23207 navFnc: 'FullYear',
23212 Roo.apply(Roo.bootstrap.DateField, {
23216 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23220 cls: 'datepicker-days',
23224 cls: 'table-condensed',
23226 Roo.bootstrap.DateField.head,
23230 Roo.bootstrap.DateField.footer
23237 cls: 'datepicker-months',
23241 cls: 'table-condensed',
23243 Roo.bootstrap.DateField.head,
23244 Roo.bootstrap.DateField.content,
23245 Roo.bootstrap.DateField.footer
23252 cls: 'datepicker-years',
23256 cls: 'table-condensed',
23258 Roo.bootstrap.DateField.head,
23259 Roo.bootstrap.DateField.content,
23260 Roo.bootstrap.DateField.footer
23279 * @class Roo.bootstrap.TimeField
23280 * @extends Roo.bootstrap.Input
23281 * Bootstrap DateField class
23285 * Create a new TimeField
23286 * @param {Object} config The config object
23289 Roo.bootstrap.TimeField = function(config){
23290 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23294 * Fires when this field show.
23295 * @param {Roo.bootstrap.DateField} thisthis
23296 * @param {Mixed} date The date value
23301 * Fires when this field hide.
23302 * @param {Roo.bootstrap.DateField} this
23303 * @param {Mixed} date The date value
23308 * Fires when select a date.
23309 * @param {Roo.bootstrap.DateField} this
23310 * @param {Mixed} date The date value
23316 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23319 * @cfg {String} format
23320 * The default time format string which can be overriden for localization support. The format must be
23321 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23325 getAutoCreate : function()
23327 this.after = '<i class="fa far fa-clock"></i>';
23328 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23332 onRender: function(ct, position)
23335 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23337 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23339 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23341 this.pop = this.picker().select('>.datepicker-time',true).first();
23342 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23344 this.picker().on('mousedown', this.onMousedown, this);
23345 this.picker().on('click', this.onClick, this);
23347 this.picker().addClass('datepicker-dropdown');
23352 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23353 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23354 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23355 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23356 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23357 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23361 fireKey: function(e){
23362 if (!this.picker().isVisible()){
23363 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23369 e.preventDefault();
23377 this.onTogglePeriod();
23380 this.onIncrementMinutes();
23383 this.onDecrementMinutes();
23392 onClick: function(e) {
23393 e.stopPropagation();
23394 e.preventDefault();
23397 picker : function()
23399 return this.pickerEl;
23402 fillTime: function()
23404 var time = this.pop.select('tbody', true).first();
23406 time.dom.innerHTML = '';
23421 cls: 'hours-up fa fas fa-chevron-up'
23441 cls: 'minutes-up fa fas fa-chevron-up'
23462 cls: 'timepicker-hour',
23477 cls: 'timepicker-minute',
23492 cls: 'btn btn-primary period',
23514 cls: 'hours-down fa fas fa-chevron-down'
23534 cls: 'minutes-down fa fas fa-chevron-down'
23552 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23559 var hours = this.time.getHours();
23560 var minutes = this.time.getMinutes();
23573 hours = hours - 12;
23577 hours = '0' + hours;
23581 minutes = '0' + minutes;
23584 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23585 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23586 this.pop.select('button', true).first().dom.innerHTML = period;
23592 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23594 var cls = ['bottom'];
23596 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23603 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23607 //this.picker().setXY(20000,20000);
23608 this.picker().addClass(cls.join('-'));
23612 Roo.each(cls, function(c){
23617 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23618 //_this.picker().setTop(_this.inputEl().getHeight());
23622 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23624 //_this.picker().setTop(0 - _this.picker().getHeight());
23629 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23633 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23641 onFocus : function()
23643 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23647 onBlur : function()
23649 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23655 this.picker().show();
23660 this.fireEvent('show', this, this.date);
23665 this.picker().hide();
23668 this.fireEvent('hide', this, this.date);
23671 setTime : function()
23674 this.setValue(this.time.format(this.format));
23676 this.fireEvent('select', this, this.date);
23681 onMousedown: function(e){
23682 e.stopPropagation();
23683 e.preventDefault();
23686 onIncrementHours: function()
23688 Roo.log('onIncrementHours');
23689 this.time = this.time.add(Date.HOUR, 1);
23694 onDecrementHours: function()
23696 Roo.log('onDecrementHours');
23697 this.time = this.time.add(Date.HOUR, -1);
23701 onIncrementMinutes: function()
23703 Roo.log('onIncrementMinutes');
23704 this.time = this.time.add(Date.MINUTE, 1);
23708 onDecrementMinutes: function()
23710 Roo.log('onDecrementMinutes');
23711 this.time = this.time.add(Date.MINUTE, -1);
23715 onTogglePeriod: function()
23717 Roo.log('onTogglePeriod');
23718 this.time = this.time.add(Date.HOUR, 12);
23726 Roo.apply(Roo.bootstrap.TimeField, {
23730 cls: 'datepicker dropdown-menu',
23734 cls: 'datepicker-time',
23738 cls: 'table-condensed',
23767 cls: 'btn btn-info ok',
23795 * @class Roo.bootstrap.MonthField
23796 * @extends Roo.bootstrap.Input
23797 * Bootstrap MonthField class
23799 * @cfg {String} language default en
23802 * Create a new MonthField
23803 * @param {Object} config The config object
23806 Roo.bootstrap.MonthField = function(config){
23807 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23812 * Fires when this field show.
23813 * @param {Roo.bootstrap.MonthField} this
23814 * @param {Mixed} date The date value
23819 * Fires when this field hide.
23820 * @param {Roo.bootstrap.MonthField} this
23821 * @param {Mixed} date The date value
23826 * Fires when select a date.
23827 * @param {Roo.bootstrap.MonthField} this
23828 * @param {String} oldvalue The old value
23829 * @param {String} newvalue The new value
23835 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23837 onRender: function(ct, position)
23840 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23842 this.language = this.language || 'en';
23843 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23844 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23846 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23847 this.isInline = false;
23848 this.isInput = true;
23849 this.component = this.el.select('.add-on', true).first() || false;
23850 this.component = (this.component && this.component.length === 0) ? false : this.component;
23851 this.hasInput = this.component && this.inputEL().length;
23853 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23855 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23857 this.picker().on('mousedown', this.onMousedown, this);
23858 this.picker().on('click', this.onClick, this);
23860 this.picker().addClass('datepicker-dropdown');
23862 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23863 v.setStyle('width', '189px');
23870 if(this.isInline) {
23876 setValue: function(v, suppressEvent)
23878 var o = this.getValue();
23880 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23884 if(suppressEvent !== true){
23885 this.fireEvent('select', this, o, v);
23890 getValue: function()
23895 onClick: function(e)
23897 e.stopPropagation();
23898 e.preventDefault();
23900 var target = e.getTarget();
23902 if(target.nodeName.toLowerCase() === 'i'){
23903 target = Roo.get(target).dom.parentNode;
23906 var nodeName = target.nodeName;
23907 var className = target.className;
23908 var html = target.innerHTML;
23910 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23914 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23916 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23922 picker : function()
23924 return this.pickerEl;
23927 fillMonths: function()
23930 var months = this.picker().select('>.datepicker-months td', true).first();
23932 months.dom.innerHTML = '';
23938 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23941 months.createChild(month);
23950 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23951 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23954 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23955 e.removeClass('active');
23957 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23958 e.addClass('active');
23965 if(this.isInline) {
23969 this.picker().removeClass(['bottom', 'top']);
23971 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23973 * place to the top of element!
23977 this.picker().addClass('top');
23978 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23983 this.picker().addClass('bottom');
23985 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23988 onFocus : function()
23990 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23994 onBlur : function()
23996 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23998 var d = this.inputEl().getValue();
24007 this.picker().show();
24008 this.picker().select('>.datepicker-months', true).first().show();
24012 this.fireEvent('show', this, this.date);
24017 if(this.isInline) {
24020 this.picker().hide();
24021 this.fireEvent('hide', this, this.date);
24025 onMousedown: function(e)
24027 e.stopPropagation();
24028 e.preventDefault();
24033 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24037 fireKey: function(e)
24039 if (!this.picker().isVisible()){
24040 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24051 e.preventDefault();
24055 dir = e.keyCode == 37 ? -1 : 1;
24057 this.vIndex = this.vIndex + dir;
24059 if(this.vIndex < 0){
24063 if(this.vIndex > 11){
24067 if(isNaN(this.vIndex)){
24071 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24077 dir = e.keyCode == 38 ? -1 : 1;
24079 this.vIndex = this.vIndex + dir * 4;
24081 if(this.vIndex < 0){
24085 if(this.vIndex > 11){
24089 if(isNaN(this.vIndex)){
24093 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24098 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24099 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24103 e.preventDefault();
24106 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24107 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24123 this.picker().remove();
24128 Roo.apply(Roo.bootstrap.MonthField, {
24147 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24148 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24153 Roo.apply(Roo.bootstrap.MonthField, {
24157 cls: 'datepicker dropdown-menu roo-dynamic',
24161 cls: 'datepicker-months',
24165 cls: 'table-condensed',
24167 Roo.bootstrap.DateField.content
24187 * @class Roo.bootstrap.CheckBox
24188 * @extends Roo.bootstrap.Input
24189 * Bootstrap CheckBox class
24191 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24192 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24193 * @cfg {String} boxLabel The text that appears beside the checkbox
24194 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24195 * @cfg {Boolean} checked initnal the element
24196 * @cfg {Boolean} inline inline the element (default false)
24197 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24198 * @cfg {String} tooltip label tooltip
24201 * Create a new CheckBox
24202 * @param {Object} config The config object
24205 Roo.bootstrap.CheckBox = function(config){
24206 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24211 * Fires when the element is checked or unchecked.
24212 * @param {Roo.bootstrap.CheckBox} this This input
24213 * @param {Boolean} checked The new checked value
24218 * Fires when the element is click.
24219 * @param {Roo.bootstrap.CheckBox} this This input
24226 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24228 inputType: 'checkbox',
24237 // checkbox success does not make any sense really..
24242 getAutoCreate : function()
24244 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24250 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24253 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24259 type : this.inputType,
24260 value : this.inputValue,
24261 cls : 'roo-' + this.inputType, //'form-box',
24262 placeholder : this.placeholder || ''
24266 if(this.inputType != 'radio'){
24270 cls : 'roo-hidden-value',
24271 value : this.checked ? this.inputValue : this.valueOff
24276 if (this.weight) { // Validity check?
24277 cfg.cls += " " + this.inputType + "-" + this.weight;
24280 if (this.disabled) {
24281 input.disabled=true;
24285 input.checked = this.checked;
24290 input.name = this.name;
24292 if(this.inputType != 'radio'){
24293 hidden.name = this.name;
24294 input.name = '_hidden_' + this.name;
24299 input.cls += ' input-' + this.size;
24304 ['xs','sm','md','lg'].map(function(size){
24305 if (settings[size]) {
24306 cfg.cls += ' col-' + size + '-' + settings[size];
24310 var inputblock = input;
24312 if (this.before || this.after) {
24315 cls : 'input-group',
24320 inputblock.cn.push({
24322 cls : 'input-group-addon',
24327 inputblock.cn.push(input);
24329 if(this.inputType != 'radio'){
24330 inputblock.cn.push(hidden);
24334 inputblock.cn.push({
24336 cls : 'input-group-addon',
24342 var boxLabelCfg = false;
24348 //'for': id, // box label is handled by onclick - so no for...
24350 html: this.boxLabel
24353 boxLabelCfg.tooltip = this.tooltip;
24359 if (align ==='left' && this.fieldLabel.length) {
24360 // Roo.log("left and has label");
24365 cls : 'control-label',
24366 html : this.fieldLabel
24377 cfg.cn[1].cn.push(boxLabelCfg);
24380 if(this.labelWidth > 12){
24381 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24384 if(this.labelWidth < 13 && this.labelmd == 0){
24385 this.labelmd = this.labelWidth;
24388 if(this.labellg > 0){
24389 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24390 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24393 if(this.labelmd > 0){
24394 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24395 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24398 if(this.labelsm > 0){
24399 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24400 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24403 if(this.labelxs > 0){
24404 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24405 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24408 } else if ( this.fieldLabel.length) {
24409 // Roo.log(" label");
24413 tag: this.boxLabel ? 'span' : 'label',
24415 cls: 'control-label box-input-label',
24416 //cls : 'input-group-addon',
24417 html : this.fieldLabel
24424 cfg.cn.push(boxLabelCfg);
24429 // Roo.log(" no label && no align");
24430 cfg.cn = [ inputblock ] ;
24432 cfg.cn.push(boxLabelCfg);
24440 if(this.inputType != 'radio'){
24441 cfg.cn.push(hidden);
24449 * return the real input element.
24451 inputEl: function ()
24453 return this.el.select('input.roo-' + this.inputType,true).first();
24455 hiddenEl: function ()
24457 return this.el.select('input.roo-hidden-value',true).first();
24460 labelEl: function()
24462 return this.el.select('label.control-label',true).first();
24464 /* depricated... */
24468 return this.labelEl();
24471 boxLabelEl: function()
24473 return this.el.select('label.box-label',true).first();
24476 initEvents : function()
24478 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24480 this.inputEl().on('click', this.onClick, this);
24482 if (this.boxLabel) {
24483 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24486 this.startValue = this.getValue();
24489 Roo.bootstrap.CheckBox.register(this);
24493 onClick : function(e)
24495 if(this.fireEvent('click', this, e) !== false){
24496 this.setChecked(!this.checked);
24501 setChecked : function(state,suppressEvent)
24503 this.startValue = this.getValue();
24505 if(this.inputType == 'radio'){
24507 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24508 e.dom.checked = false;
24511 this.inputEl().dom.checked = true;
24513 this.inputEl().dom.value = this.inputValue;
24515 if(suppressEvent !== true){
24516 this.fireEvent('check', this, true);
24524 this.checked = state;
24526 this.inputEl().dom.checked = state;
24529 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24531 if(suppressEvent !== true){
24532 this.fireEvent('check', this, state);
24538 getValue : function()
24540 if(this.inputType == 'radio'){
24541 return this.getGroupValue();
24544 return this.hiddenEl().dom.value;
24548 getGroupValue : function()
24550 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24554 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24557 setValue : function(v,suppressEvent)
24559 if(this.inputType == 'radio'){
24560 this.setGroupValue(v, suppressEvent);
24564 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24569 setGroupValue : function(v, suppressEvent)
24571 this.startValue = this.getValue();
24573 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24574 e.dom.checked = false;
24576 if(e.dom.value == v){
24577 e.dom.checked = true;
24581 if(suppressEvent !== true){
24582 this.fireEvent('check', this, true);
24590 validate : function()
24592 if(this.getVisibilityEl().hasClass('hidden')){
24598 (this.inputType == 'radio' && this.validateRadio()) ||
24599 (this.inputType == 'checkbox' && this.validateCheckbox())
24605 this.markInvalid();
24609 validateRadio : function()
24611 if(this.getVisibilityEl().hasClass('hidden')){
24615 if(this.allowBlank){
24621 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24622 if(!e.dom.checked){
24634 validateCheckbox : function()
24637 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24638 //return (this.getValue() == this.inputValue) ? true : false;
24641 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24649 for(var i in group){
24650 if(group[i].el.isVisible(true)){
24658 for(var i in group){
24663 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24670 * Mark this field as valid
24672 markValid : function()
24676 this.fireEvent('valid', this);
24678 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24681 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24688 if(this.inputType == 'radio'){
24689 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24690 var fg = e.findParent('.form-group', false, true);
24691 if (Roo.bootstrap.version == 3) {
24692 fg.removeClass([_this.invalidClass, _this.validClass]);
24693 fg.addClass(_this.validClass);
24695 fg.removeClass(['is-valid', 'is-invalid']);
24696 fg.addClass('is-valid');
24704 var fg = this.el.findParent('.form-group', false, true);
24705 if (Roo.bootstrap.version == 3) {
24706 fg.removeClass([this.invalidClass, this.validClass]);
24707 fg.addClass(this.validClass);
24709 fg.removeClass(['is-valid', 'is-invalid']);
24710 fg.addClass('is-valid');
24715 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24721 for(var i in group){
24722 var fg = group[i].el.findParent('.form-group', false, true);
24723 if (Roo.bootstrap.version == 3) {
24724 fg.removeClass([this.invalidClass, this.validClass]);
24725 fg.addClass(this.validClass);
24727 fg.removeClass(['is-valid', 'is-invalid']);
24728 fg.addClass('is-valid');
24734 * Mark this field as invalid
24735 * @param {String} msg The validation message
24737 markInvalid : function(msg)
24739 if(this.allowBlank){
24745 this.fireEvent('invalid', this, msg);
24747 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24750 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24754 label.markInvalid();
24757 if(this.inputType == 'radio'){
24759 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24760 var fg = e.findParent('.form-group', false, true);
24761 if (Roo.bootstrap.version == 3) {
24762 fg.removeClass([_this.invalidClass, _this.validClass]);
24763 fg.addClass(_this.invalidClass);
24765 fg.removeClass(['is-invalid', 'is-valid']);
24766 fg.addClass('is-invalid');
24774 var fg = this.el.findParent('.form-group', false, true);
24775 if (Roo.bootstrap.version == 3) {
24776 fg.removeClass([_this.invalidClass, _this.validClass]);
24777 fg.addClass(_this.invalidClass);
24779 fg.removeClass(['is-invalid', 'is-valid']);
24780 fg.addClass('is-invalid');
24785 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24791 for(var i in group){
24792 var fg = group[i].el.findParent('.form-group', false, true);
24793 if (Roo.bootstrap.version == 3) {
24794 fg.removeClass([_this.invalidClass, _this.validClass]);
24795 fg.addClass(_this.invalidClass);
24797 fg.removeClass(['is-invalid', 'is-valid']);
24798 fg.addClass('is-invalid');
24804 clearInvalid : function()
24806 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24808 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24810 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24812 if (label && label.iconEl) {
24813 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24814 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24818 disable : function()
24820 if(this.inputType != 'radio'){
24821 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24828 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24829 _this.getActionEl().addClass(this.disabledClass);
24830 e.dom.disabled = true;
24834 this.disabled = true;
24835 this.fireEvent("disable", this);
24839 enable : function()
24841 if(this.inputType != 'radio'){
24842 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24849 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24850 _this.getActionEl().removeClass(this.disabledClass);
24851 e.dom.disabled = false;
24855 this.disabled = false;
24856 this.fireEvent("enable", this);
24860 setBoxLabel : function(v)
24865 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24871 Roo.apply(Roo.bootstrap.CheckBox, {
24876 * register a CheckBox Group
24877 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24879 register : function(checkbox)
24881 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24882 this.groups[checkbox.groupId] = {};
24885 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24889 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24893 * fetch a CheckBox Group based on the group ID
24894 * @param {string} the group ID
24895 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24897 get: function(groupId) {
24898 if (typeof(this.groups[groupId]) == 'undefined') {
24902 return this.groups[groupId] ;
24915 * @class Roo.bootstrap.Radio
24916 * @extends Roo.bootstrap.Component
24917 * Bootstrap Radio class
24918 * @cfg {String} boxLabel - the label associated
24919 * @cfg {String} value - the value of radio
24922 * Create a new Radio
24923 * @param {Object} config The config object
24925 Roo.bootstrap.Radio = function(config){
24926 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24930 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24936 getAutoCreate : function()
24940 cls : 'form-group radio',
24945 html : this.boxLabel
24953 initEvents : function()
24955 this.parent().register(this);
24957 this.el.on('click', this.onClick, this);
24961 onClick : function(e)
24963 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24964 this.setChecked(true);
24968 setChecked : function(state, suppressEvent)
24970 this.parent().setValue(this.value, suppressEvent);
24974 setBoxLabel : function(v)
24979 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24994 * @class Roo.bootstrap.SecurePass
24995 * @extends Roo.bootstrap.Input
24996 * Bootstrap SecurePass class
25000 * Create a new SecurePass
25001 * @param {Object} config The config object
25004 Roo.bootstrap.SecurePass = function (config) {
25005 // these go here, so the translation tool can replace them..
25007 PwdEmpty: "Please type a password, and then retype it to confirm.",
25008 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25009 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25010 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25011 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25012 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25013 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25014 TooWeak: "Your password is Too Weak."
25016 this.meterLabel = "Password strength:";
25017 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25018 this.meterClass = [
25019 "roo-password-meter-tooweak",
25020 "roo-password-meter-weak",
25021 "roo-password-meter-medium",
25022 "roo-password-meter-strong",
25023 "roo-password-meter-grey"
25028 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25031 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25033 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25035 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25036 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25037 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25038 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25039 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25040 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25041 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25051 * @cfg {String/Object} Label for the strength meter (defaults to
25052 * 'Password strength:')
25057 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25058 * ['Weak', 'Medium', 'Strong'])
25061 pwdStrengths: false,
25074 initEvents: function ()
25076 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25078 if (this.el.is('input[type=password]') && Roo.isSafari) {
25079 this.el.on('keydown', this.SafariOnKeyDown, this);
25082 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25085 onRender: function (ct, position)
25087 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25088 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25089 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25091 this.trigger.createChild({
25096 cls: 'roo-password-meter-grey col-xs-12',
25099 //width: this.meterWidth + 'px'
25103 cls: 'roo-password-meter-text'
25109 if (this.hideTrigger) {
25110 this.trigger.setDisplayed(false);
25112 this.setSize(this.width || '', this.height || '');
25115 onDestroy: function ()
25117 if (this.trigger) {
25118 this.trigger.removeAllListeners();
25119 this.trigger.remove();
25122 this.wrap.remove();
25124 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25127 checkStrength: function ()
25129 var pwd = this.inputEl().getValue();
25130 if (pwd == this._lastPwd) {
25135 if (this.ClientSideStrongPassword(pwd)) {
25137 } else if (this.ClientSideMediumPassword(pwd)) {
25139 } else if (this.ClientSideWeakPassword(pwd)) {
25145 Roo.log('strength1: ' + strength);
25147 //var pm = this.trigger.child('div/div/div').dom;
25148 var pm = this.trigger.child('div/div');
25149 pm.removeClass(this.meterClass);
25150 pm.addClass(this.meterClass[strength]);
25153 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25155 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25157 this._lastPwd = pwd;
25161 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25163 this._lastPwd = '';
25165 var pm = this.trigger.child('div/div');
25166 pm.removeClass(this.meterClass);
25167 pm.addClass('roo-password-meter-grey');
25170 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25173 this.inputEl().dom.type='password';
25176 validateValue: function (value)
25178 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25181 if (value.length == 0) {
25182 if (this.allowBlank) {
25183 this.clearInvalid();
25187 this.markInvalid(this.errors.PwdEmpty);
25188 this.errorMsg = this.errors.PwdEmpty;
25196 if (!value.match(/[\x21-\x7e]+/)) {
25197 this.markInvalid(this.errors.PwdBadChar);
25198 this.errorMsg = this.errors.PwdBadChar;
25201 if (value.length < 6) {
25202 this.markInvalid(this.errors.PwdShort);
25203 this.errorMsg = this.errors.PwdShort;
25206 if (value.length > 16) {
25207 this.markInvalid(this.errors.PwdLong);
25208 this.errorMsg = this.errors.PwdLong;
25212 if (this.ClientSideStrongPassword(value)) {
25214 } else if (this.ClientSideMediumPassword(value)) {
25216 } else if (this.ClientSideWeakPassword(value)) {
25223 if (strength < 2) {
25224 //this.markInvalid(this.errors.TooWeak);
25225 this.errorMsg = this.errors.TooWeak;
25230 console.log('strength2: ' + strength);
25232 //var pm = this.trigger.child('div/div/div').dom;
25234 var pm = this.trigger.child('div/div');
25235 pm.removeClass(this.meterClass);
25236 pm.addClass(this.meterClass[strength]);
25238 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25240 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25242 this.errorMsg = '';
25246 CharacterSetChecks: function (type)
25249 this.fResult = false;
25252 isctype: function (character, type)
25255 case this.kCapitalLetter:
25256 if (character >= 'A' && character <= 'Z') {
25261 case this.kSmallLetter:
25262 if (character >= 'a' && character <= 'z') {
25268 if (character >= '0' && character <= '9') {
25273 case this.kPunctuation:
25274 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25285 IsLongEnough: function (pwd, size)
25287 return !(pwd == null || isNaN(size) || pwd.length < size);
25290 SpansEnoughCharacterSets: function (word, nb)
25292 if (!this.IsLongEnough(word, nb))
25297 var characterSetChecks = new Array(
25298 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25299 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25302 for (var index = 0; index < word.length; ++index) {
25303 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25304 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25305 characterSetChecks[nCharSet].fResult = true;
25312 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25313 if (characterSetChecks[nCharSet].fResult) {
25318 if (nCharSets < nb) {
25324 ClientSideStrongPassword: function (pwd)
25326 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25329 ClientSideMediumPassword: function (pwd)
25331 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25334 ClientSideWeakPassword: function (pwd)
25336 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25339 })//<script type="text/javascript">
25342 * Based Ext JS Library 1.1.1
25343 * Copyright(c) 2006-2007, Ext JS, LLC.
25349 * @class Roo.HtmlEditorCore
25350 * @extends Roo.Component
25351 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25353 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25356 Roo.HtmlEditorCore = function(config){
25359 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25364 * @event initialize
25365 * Fires when the editor is fully initialized (including the iframe)
25366 * @param {Roo.HtmlEditorCore} this
25371 * Fires when the editor is first receives the focus. Any insertion must wait
25372 * until after this event.
25373 * @param {Roo.HtmlEditorCore} this
25377 * @event beforesync
25378 * Fires before the textarea is updated with content from the editor iframe. Return false
25379 * to cancel the sync.
25380 * @param {Roo.HtmlEditorCore} this
25381 * @param {String} html
25385 * @event beforepush
25386 * Fires before the iframe editor is updated with content from the textarea. Return false
25387 * to cancel the push.
25388 * @param {Roo.HtmlEditorCore} this
25389 * @param {String} html
25394 * Fires when the textarea is updated with content from the editor iframe.
25395 * @param {Roo.HtmlEditorCore} this
25396 * @param {String} html
25401 * Fires when the iframe editor is updated with content from the textarea.
25402 * @param {Roo.HtmlEditorCore} this
25403 * @param {String} html
25408 * @event editorevent
25409 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25410 * @param {Roo.HtmlEditorCore} this
25416 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25418 // defaults : white / black...
25419 this.applyBlacklists();
25426 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25430 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25436 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25441 * @cfg {Number} height (in pixels)
25445 * @cfg {Number} width (in pixels)
25450 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25453 stylesheets: false,
25458 // private properties
25459 validationEvent : false,
25461 initialized : false,
25463 sourceEditMode : false,
25464 onFocus : Roo.emptyFn,
25466 hideMode:'offsets',
25470 // blacklist + whitelisted elements..
25477 * Protected method that will not generally be called directly. It
25478 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25479 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25481 getDocMarkup : function(){
25485 // inherit styels from page...??
25486 if (this.stylesheets === false) {
25488 Roo.get(document.head).select('style').each(function(node) {
25489 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25492 Roo.get(document.head).select('link').each(function(node) {
25493 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25496 } else if (!this.stylesheets.length) {
25498 st = '<style type="text/css">' +
25499 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25502 for (var i in this.stylesheets) {
25503 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25508 st += '<style type="text/css">' +
25509 'IMG { cursor: pointer } ' +
25512 var cls = 'roo-htmleditor-body';
25514 if(this.bodyCls.length){
25515 cls += ' ' + this.bodyCls;
25518 return '<html><head>' + st +
25519 //<style type="text/css">' +
25520 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25522 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25526 onRender : function(ct, position)
25529 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25530 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25533 this.el.dom.style.border = '0 none';
25534 this.el.dom.setAttribute('tabIndex', -1);
25535 this.el.addClass('x-hidden hide');
25539 if(Roo.isIE){ // fix IE 1px bogus margin
25540 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25544 this.frameId = Roo.id();
25548 var iframe = this.owner.wrap.createChild({
25550 cls: 'form-control', // bootstrap..
25552 name: this.frameId,
25553 frameBorder : 'no',
25554 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25559 this.iframe = iframe.dom;
25561 this.assignDocWin();
25563 this.doc.designMode = 'on';
25566 this.doc.write(this.getDocMarkup());
25570 var task = { // must defer to wait for browser to be ready
25572 //console.log("run task?" + this.doc.readyState);
25573 this.assignDocWin();
25574 if(this.doc.body || this.doc.readyState == 'complete'){
25576 this.doc.designMode="on";
25580 Roo.TaskMgr.stop(task);
25581 this.initEditor.defer(10, this);
25588 Roo.TaskMgr.start(task);
25593 onResize : function(w, h)
25595 Roo.log('resize: ' +w + ',' + h );
25596 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25600 if(typeof w == 'number'){
25602 this.iframe.style.width = w + 'px';
25604 if(typeof h == 'number'){
25606 this.iframe.style.height = h + 'px';
25608 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25615 * Toggles the editor between standard and source edit mode.
25616 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25618 toggleSourceEdit : function(sourceEditMode){
25620 this.sourceEditMode = sourceEditMode === true;
25622 if(this.sourceEditMode){
25624 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25627 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25628 //this.iframe.className = '';
25631 //this.setSize(this.owner.wrap.getSize());
25632 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25639 * Protected method that will not generally be called directly. If you need/want
25640 * custom HTML cleanup, this is the method you should override.
25641 * @param {String} html The HTML to be cleaned
25642 * return {String} The cleaned HTML
25644 cleanHtml : function(html){
25645 html = String(html);
25646 if(html.length > 5){
25647 if(Roo.isSafari){ // strip safari nonsense
25648 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25651 if(html == ' '){
25658 * HTML Editor -> Textarea
25659 * Protected method that will not generally be called directly. Syncs the contents
25660 * of the editor iframe with the textarea.
25662 syncValue : function(){
25663 if(this.initialized){
25664 var bd = (this.doc.body || this.doc.documentElement);
25665 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25666 var html = bd.innerHTML;
25668 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25669 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25671 html = '<div style="'+m[0]+'">' + html + '</div>';
25674 html = this.cleanHtml(html);
25675 // fix up the special chars.. normaly like back quotes in word...
25676 // however we do not want to do this with chinese..
25677 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25679 var cc = match.charCodeAt();
25681 // Get the character value, handling surrogate pairs
25682 if (match.length == 2) {
25683 // It's a surrogate pair, calculate the Unicode code point
25684 var high = match.charCodeAt(0) - 0xD800;
25685 var low = match.charCodeAt(1) - 0xDC00;
25686 cc = (high * 0x400) + low + 0x10000;
25688 (cc >= 0x4E00 && cc < 0xA000 ) ||
25689 (cc >= 0x3400 && cc < 0x4E00 ) ||
25690 (cc >= 0xf900 && cc < 0xfb00 )
25695 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25696 return "&#" + cc + ";";
25703 if(this.owner.fireEvent('beforesync', this, html) !== false){
25704 this.el.dom.value = html;
25705 this.owner.fireEvent('sync', this, html);
25711 * Protected method that will not generally be called directly. Pushes the value of the textarea
25712 * into the iframe editor.
25714 pushValue : function(){
25715 if(this.initialized){
25716 var v = this.el.dom.value.trim();
25718 // if(v.length < 1){
25722 if(this.owner.fireEvent('beforepush', this, v) !== false){
25723 var d = (this.doc.body || this.doc.documentElement);
25725 this.cleanUpPaste();
25726 this.el.dom.value = d.innerHTML;
25727 this.owner.fireEvent('push', this, v);
25733 deferFocus : function(){
25734 this.focus.defer(10, this);
25738 focus : function(){
25739 if(this.win && !this.sourceEditMode){
25746 assignDocWin: function()
25748 var iframe = this.iframe;
25751 this.doc = iframe.contentWindow.document;
25752 this.win = iframe.contentWindow;
25754 // if (!Roo.get(this.frameId)) {
25757 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25758 // this.win = Roo.get(this.frameId).dom.contentWindow;
25760 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25764 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25765 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25770 initEditor : function(){
25771 //console.log("INIT EDITOR");
25772 this.assignDocWin();
25776 this.doc.designMode="on";
25778 this.doc.write(this.getDocMarkup());
25781 var dbody = (this.doc.body || this.doc.documentElement);
25782 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25783 // this copies styles from the containing element into thsi one..
25784 // not sure why we need all of this..
25785 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25787 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25788 //ss['background-attachment'] = 'fixed'; // w3c
25789 dbody.bgProperties = 'fixed'; // ie
25790 //Roo.DomHelper.applyStyles(dbody, ss);
25791 Roo.EventManager.on(this.doc, {
25792 //'mousedown': this.onEditorEvent,
25793 'mouseup': this.onEditorEvent,
25794 'dblclick': this.onEditorEvent,
25795 'click': this.onEditorEvent,
25796 'keyup': this.onEditorEvent,
25801 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25803 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25804 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25806 this.initialized = true;
25808 this.owner.fireEvent('initialize', this);
25813 onDestroy : function(){
25819 //for (var i =0; i < this.toolbars.length;i++) {
25820 // // fixme - ask toolbars for heights?
25821 // this.toolbars[i].onDestroy();
25824 //this.wrap.dom.innerHTML = '';
25825 //this.wrap.remove();
25830 onFirstFocus : function(){
25832 this.assignDocWin();
25835 this.activated = true;
25838 if(Roo.isGecko){ // prevent silly gecko errors
25840 var s = this.win.getSelection();
25841 if(!s.focusNode || s.focusNode.nodeType != 3){
25842 var r = s.getRangeAt(0);
25843 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25848 this.execCmd('useCSS', true);
25849 this.execCmd('styleWithCSS', false);
25852 this.owner.fireEvent('activate', this);
25856 adjustFont: function(btn){
25857 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25858 //if(Roo.isSafari){ // safari
25861 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25862 if(Roo.isSafari){ // safari
25863 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25864 v = (v < 10) ? 10 : v;
25865 v = (v > 48) ? 48 : v;
25866 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25871 v = Math.max(1, v+adjust);
25873 this.execCmd('FontSize', v );
25876 onEditorEvent : function(e)
25878 this.owner.fireEvent('editorevent', this, e);
25879 // this.updateToolbar();
25880 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25883 insertTag : function(tg)
25885 // could be a bit smarter... -> wrap the current selected tRoo..
25886 if (tg.toLowerCase() == 'span' ||
25887 tg.toLowerCase() == 'code' ||
25888 tg.toLowerCase() == 'sup' ||
25889 tg.toLowerCase() == 'sub'
25892 range = this.createRange(this.getSelection());
25893 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25894 wrappingNode.appendChild(range.extractContents());
25895 range.insertNode(wrappingNode);
25902 this.execCmd("formatblock", tg);
25906 insertText : function(txt)
25910 var range = this.createRange();
25911 range.deleteContents();
25912 //alert(Sender.getAttribute('label'));
25914 range.insertNode(this.doc.createTextNode(txt));
25920 * Executes a Midas editor command on the editor document and performs necessary focus and
25921 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25922 * @param {String} cmd The Midas command
25923 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25925 relayCmd : function(cmd, value){
25927 this.execCmd(cmd, value);
25928 this.owner.fireEvent('editorevent', this);
25929 //this.updateToolbar();
25930 this.owner.deferFocus();
25934 * Executes a Midas editor command directly on the editor document.
25935 * For visual commands, you should use {@link #relayCmd} instead.
25936 * <b>This should only be called after the editor is initialized.</b>
25937 * @param {String} cmd The Midas command
25938 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25940 execCmd : function(cmd, value){
25941 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25948 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25950 * @param {String} text | dom node..
25952 insertAtCursor : function(text)
25955 if(!this.activated){
25961 var r = this.doc.selection.createRange();
25972 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25976 // from jquery ui (MIT licenced)
25978 var win = this.win;
25980 if (win.getSelection && win.getSelection().getRangeAt) {
25981 range = win.getSelection().getRangeAt(0);
25982 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25983 range.insertNode(node);
25984 } else if (win.document.selection && win.document.selection.createRange) {
25985 // no firefox support
25986 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25987 win.document.selection.createRange().pasteHTML(txt);
25989 // no firefox support
25990 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25991 this.execCmd('InsertHTML', txt);
26000 mozKeyPress : function(e){
26002 var c = e.getCharCode(), cmd;
26005 c = String.fromCharCode(c).toLowerCase();
26019 this.cleanUpPaste.defer(100, this);
26027 e.preventDefault();
26035 fixKeys : function(){ // load time branching for fastest keydown performance
26037 return function(e){
26038 var k = e.getKey(), r;
26041 r = this.doc.selection.createRange();
26044 r.pasteHTML('    ');
26051 r = this.doc.selection.createRange();
26053 var target = r.parentElement();
26054 if(!target || target.tagName.toLowerCase() != 'li'){
26056 r.pasteHTML('<br />');
26062 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26063 this.cleanUpPaste.defer(100, this);
26069 }else if(Roo.isOpera){
26070 return function(e){
26071 var k = e.getKey();
26075 this.execCmd('InsertHTML','    ');
26078 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26079 this.cleanUpPaste.defer(100, this);
26084 }else if(Roo.isSafari){
26085 return function(e){
26086 var k = e.getKey();
26090 this.execCmd('InsertText','\t');
26094 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26095 this.cleanUpPaste.defer(100, this);
26103 getAllAncestors: function()
26105 var p = this.getSelectedNode();
26108 a.push(p); // push blank onto stack..
26109 p = this.getParentElement();
26113 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26117 a.push(this.doc.body);
26121 lastSelNode : false,
26124 getSelection : function()
26126 this.assignDocWin();
26127 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26130 getSelectedNode: function()
26132 // this may only work on Gecko!!!
26134 // should we cache this!!!!
26139 var range = this.createRange(this.getSelection()).cloneRange();
26142 var parent = range.parentElement();
26144 var testRange = range.duplicate();
26145 testRange.moveToElementText(parent);
26146 if (testRange.inRange(range)) {
26149 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26152 parent = parent.parentElement;
26157 // is ancestor a text element.
26158 var ac = range.commonAncestorContainer;
26159 if (ac.nodeType == 3) {
26160 ac = ac.parentNode;
26163 var ar = ac.childNodes;
26166 var other_nodes = [];
26167 var has_other_nodes = false;
26168 for (var i=0;i<ar.length;i++) {
26169 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26172 // fullly contained node.
26174 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26179 // probably selected..
26180 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26181 other_nodes.push(ar[i]);
26185 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26190 has_other_nodes = true;
26192 if (!nodes.length && other_nodes.length) {
26193 nodes= other_nodes;
26195 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26201 createRange: function(sel)
26203 // this has strange effects when using with
26204 // top toolbar - not sure if it's a great idea.
26205 //this.editor.contentWindow.focus();
26206 if (typeof sel != "undefined") {
26208 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26210 return this.doc.createRange();
26213 return this.doc.createRange();
26216 getParentElement: function()
26219 this.assignDocWin();
26220 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26222 var range = this.createRange(sel);
26225 var p = range.commonAncestorContainer;
26226 while (p.nodeType == 3) { // text node
26237 * Range intersection.. the hard stuff...
26241 * [ -- selected range --- ]
26245 * if end is before start or hits it. fail.
26246 * if start is after end or hits it fail.
26248 * if either hits (but other is outside. - then it's not
26254 // @see http://www.thismuchiknow.co.uk/?p=64.
26255 rangeIntersectsNode : function(range, node)
26257 var nodeRange = node.ownerDocument.createRange();
26259 nodeRange.selectNode(node);
26261 nodeRange.selectNodeContents(node);
26264 var rangeStartRange = range.cloneRange();
26265 rangeStartRange.collapse(true);
26267 var rangeEndRange = range.cloneRange();
26268 rangeEndRange.collapse(false);
26270 var nodeStartRange = nodeRange.cloneRange();
26271 nodeStartRange.collapse(true);
26273 var nodeEndRange = nodeRange.cloneRange();
26274 nodeEndRange.collapse(false);
26276 return rangeStartRange.compareBoundaryPoints(
26277 Range.START_TO_START, nodeEndRange) == -1 &&
26278 rangeEndRange.compareBoundaryPoints(
26279 Range.START_TO_START, nodeStartRange) == 1;
26283 rangeCompareNode : function(range, node)
26285 var nodeRange = node.ownerDocument.createRange();
26287 nodeRange.selectNode(node);
26289 nodeRange.selectNodeContents(node);
26293 range.collapse(true);
26295 nodeRange.collapse(true);
26297 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26298 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26300 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26302 var nodeIsBefore = ss == 1;
26303 var nodeIsAfter = ee == -1;
26305 if (nodeIsBefore && nodeIsAfter) {
26308 if (!nodeIsBefore && nodeIsAfter) {
26309 return 1; //right trailed.
26312 if (nodeIsBefore && !nodeIsAfter) {
26313 return 2; // left trailed.
26319 // private? - in a new class?
26320 cleanUpPaste : function()
26322 // cleans up the whole document..
26323 Roo.log('cleanuppaste');
26325 this.cleanUpChildren(this.doc.body);
26326 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26327 if (clean != this.doc.body.innerHTML) {
26328 this.doc.body.innerHTML = clean;
26333 cleanWordChars : function(input) {// change the chars to hex code
26334 var he = Roo.HtmlEditorCore;
26336 var output = input;
26337 Roo.each(he.swapCodes, function(sw) {
26338 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26340 output = output.replace(swapper, sw[1]);
26347 cleanUpChildren : function (n)
26349 if (!n.childNodes.length) {
26352 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26353 this.cleanUpChild(n.childNodes[i]);
26360 cleanUpChild : function (node)
26363 //console.log(node);
26364 if (node.nodeName == "#text") {
26365 // clean up silly Windows -- stuff?
26368 if (node.nodeName == "#comment") {
26369 node.parentNode.removeChild(node);
26370 // clean up silly Windows -- stuff?
26373 var lcname = node.tagName.toLowerCase();
26374 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26375 // whitelist of tags..
26377 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26379 node.parentNode.removeChild(node);
26384 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26386 // spans with no attributes - just remove them..
26387 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26388 remove_keep_children = true;
26391 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26392 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26394 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26395 // remove_keep_children = true;
26398 if (remove_keep_children) {
26399 this.cleanUpChildren(node);
26400 // inserts everything just before this node...
26401 while (node.childNodes.length) {
26402 var cn = node.childNodes[0];
26403 node.removeChild(cn);
26404 node.parentNode.insertBefore(cn, node);
26406 node.parentNode.removeChild(node);
26410 if (!node.attributes || !node.attributes.length) {
26415 this.cleanUpChildren(node);
26419 function cleanAttr(n,v)
26422 if (v.match(/^\./) || v.match(/^\//)) {
26425 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26428 if (v.match(/^#/)) {
26431 if (v.match(/^\{/)) { // allow template editing.
26434 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26435 node.removeAttribute(n);
26439 var cwhite = this.cwhite;
26440 var cblack = this.cblack;
26442 function cleanStyle(n,v)
26444 if (v.match(/expression/)) { //XSS?? should we even bother..
26445 node.removeAttribute(n);
26449 var parts = v.split(/;/);
26452 Roo.each(parts, function(p) {
26453 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26457 var l = p.split(':').shift().replace(/\s+/g,'');
26458 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26460 if ( cwhite.length && cblack.indexOf(l) > -1) {
26461 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26462 //node.removeAttribute(n);
26466 // only allow 'c whitelisted system attributes'
26467 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26468 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26469 //node.removeAttribute(n);
26479 if (clean.length) {
26480 node.setAttribute(n, clean.join(';'));
26482 node.removeAttribute(n);
26488 for (var i = node.attributes.length-1; i > -1 ; i--) {
26489 var a = node.attributes[i];
26492 if (a.name.toLowerCase().substr(0,2)=='on') {
26493 node.removeAttribute(a.name);
26496 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26497 node.removeAttribute(a.name);
26500 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26501 cleanAttr(a.name,a.value); // fixme..
26504 if (a.name == 'style') {
26505 cleanStyle(a.name,a.value);
26508 /// clean up MS crap..
26509 // tecnically this should be a list of valid class'es..
26512 if (a.name == 'class') {
26513 if (a.value.match(/^Mso/)) {
26514 node.removeAttribute('class');
26517 if (a.value.match(/^body$/)) {
26518 node.removeAttribute('class');
26529 this.cleanUpChildren(node);
26535 * Clean up MS wordisms...
26537 cleanWord : function(node)
26540 this.cleanWord(this.doc.body);
26545 node.nodeName == 'SPAN' &&
26546 !node.hasAttributes() &&
26547 node.childNodes.length == 1 &&
26548 node.firstChild.nodeName == "#text"
26550 var textNode = node.firstChild;
26551 node.removeChild(textNode);
26552 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26553 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26555 node.parentNode.insertBefore(textNode, node);
26556 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26557 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26559 node.parentNode.removeChild(node);
26562 if (node.nodeName == "#text") {
26563 // clean up silly Windows -- stuff?
26566 if (node.nodeName == "#comment") {
26567 node.parentNode.removeChild(node);
26568 // clean up silly Windows -- stuff?
26572 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26573 node.parentNode.removeChild(node);
26576 //Roo.log(node.tagName);
26577 // remove - but keep children..
26578 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26579 //Roo.log('-- removed');
26580 while (node.childNodes.length) {
26581 var cn = node.childNodes[0];
26582 node.removeChild(cn);
26583 node.parentNode.insertBefore(cn, node);
26584 // move node to parent - and clean it..
26585 this.cleanWord(cn);
26587 node.parentNode.removeChild(node);
26588 /// no need to iterate chidlren = it's got none..
26589 //this.iterateChildren(node, this.cleanWord);
26593 if (node.className.length) {
26595 var cn = node.className.split(/\W+/);
26597 Roo.each(cn, function(cls) {
26598 if (cls.match(/Mso[a-zA-Z]+/)) {
26603 node.className = cna.length ? cna.join(' ') : '';
26605 node.removeAttribute("class");
26609 if (node.hasAttribute("lang")) {
26610 node.removeAttribute("lang");
26613 if (node.hasAttribute("style")) {
26615 var styles = node.getAttribute("style").split(";");
26617 Roo.each(styles, function(s) {
26618 if (!s.match(/:/)) {
26621 var kv = s.split(":");
26622 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26625 // what ever is left... we allow.
26628 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26629 if (!nstyle.length) {
26630 node.removeAttribute('style');
26633 this.iterateChildren(node, this.cleanWord);
26639 * iterateChildren of a Node, calling fn each time, using this as the scole..
26640 * @param {DomNode} node node to iterate children of.
26641 * @param {Function} fn method of this class to call on each item.
26643 iterateChildren : function(node, fn)
26645 if (!node.childNodes.length) {
26648 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26649 fn.call(this, node.childNodes[i])
26655 * cleanTableWidths.
26657 * Quite often pasting from word etc.. results in tables with column and widths.
26658 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26661 cleanTableWidths : function(node)
26666 this.cleanTableWidths(this.doc.body);
26671 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26674 Roo.log(node.tagName);
26675 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26676 this.iterateChildren(node, this.cleanTableWidths);
26679 if (node.hasAttribute('width')) {
26680 node.removeAttribute('width');
26684 if (node.hasAttribute("style")) {
26687 var styles = node.getAttribute("style").split(";");
26689 Roo.each(styles, function(s) {
26690 if (!s.match(/:/)) {
26693 var kv = s.split(":");
26694 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26697 // what ever is left... we allow.
26700 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26701 if (!nstyle.length) {
26702 node.removeAttribute('style');
26706 this.iterateChildren(node, this.cleanTableWidths);
26714 domToHTML : function(currentElement, depth, nopadtext) {
26716 depth = depth || 0;
26717 nopadtext = nopadtext || false;
26719 if (!currentElement) {
26720 return this.domToHTML(this.doc.body);
26723 //Roo.log(currentElement);
26725 var allText = false;
26726 var nodeName = currentElement.nodeName;
26727 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26729 if (nodeName == '#text') {
26731 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26736 if (nodeName != 'BODY') {
26739 // Prints the node tagName, such as <A>, <IMG>, etc
26742 for(i = 0; i < currentElement.attributes.length;i++) {
26744 var aname = currentElement.attributes.item(i).name;
26745 if (!currentElement.attributes.item(i).value.length) {
26748 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26751 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26760 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26763 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26768 // Traverse the tree
26770 var currentElementChild = currentElement.childNodes.item(i);
26771 var allText = true;
26772 var innerHTML = '';
26774 while (currentElementChild) {
26775 // Formatting code (indent the tree so it looks nice on the screen)
26776 var nopad = nopadtext;
26777 if (lastnode == 'SPAN') {
26781 if (currentElementChild.nodeName == '#text') {
26782 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26783 toadd = nopadtext ? toadd : toadd.trim();
26784 if (!nopad && toadd.length > 80) {
26785 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26787 innerHTML += toadd;
26790 currentElementChild = currentElement.childNodes.item(i);
26796 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26798 // Recursively traverse the tree structure of the child node
26799 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26800 lastnode = currentElementChild.nodeName;
26802 currentElementChild=currentElement.childNodes.item(i);
26808 // The remaining code is mostly for formatting the tree
26809 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26814 ret+= "</"+tagName+">";
26820 applyBlacklists : function()
26822 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26823 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26827 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26828 if (b.indexOf(tag) > -1) {
26831 this.white.push(tag);
26835 Roo.each(w, function(tag) {
26836 if (b.indexOf(tag) > -1) {
26839 if (this.white.indexOf(tag) > -1) {
26842 this.white.push(tag);
26847 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26848 if (w.indexOf(tag) > -1) {
26851 this.black.push(tag);
26855 Roo.each(b, function(tag) {
26856 if (w.indexOf(tag) > -1) {
26859 if (this.black.indexOf(tag) > -1) {
26862 this.black.push(tag);
26867 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26868 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26872 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26873 if (b.indexOf(tag) > -1) {
26876 this.cwhite.push(tag);
26880 Roo.each(w, function(tag) {
26881 if (b.indexOf(tag) > -1) {
26884 if (this.cwhite.indexOf(tag) > -1) {
26887 this.cwhite.push(tag);
26892 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26893 if (w.indexOf(tag) > -1) {
26896 this.cblack.push(tag);
26900 Roo.each(b, function(tag) {
26901 if (w.indexOf(tag) > -1) {
26904 if (this.cblack.indexOf(tag) > -1) {
26907 this.cblack.push(tag);
26912 setStylesheets : function(stylesheets)
26914 if(typeof(stylesheets) == 'string'){
26915 Roo.get(this.iframe.contentDocument.head).createChild({
26917 rel : 'stylesheet',
26926 Roo.each(stylesheets, function(s) {
26931 Roo.get(_this.iframe.contentDocument.head).createChild({
26933 rel : 'stylesheet',
26942 removeStylesheets : function()
26946 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26951 setStyle : function(style)
26953 Roo.get(this.iframe.contentDocument.head).createChild({
26962 // hide stuff that is not compatible
26976 * @event specialkey
26980 * @cfg {String} fieldClass @hide
26983 * @cfg {String} focusClass @hide
26986 * @cfg {String} autoCreate @hide
26989 * @cfg {String} inputType @hide
26992 * @cfg {String} invalidClass @hide
26995 * @cfg {String} invalidText @hide
26998 * @cfg {String} msgFx @hide
27001 * @cfg {String} validateOnBlur @hide
27005 Roo.HtmlEditorCore.white = [
27006 'area', 'br', 'img', 'input', 'hr', 'wbr',
27008 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27009 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27010 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27011 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27012 'table', 'ul', 'xmp',
27014 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27017 'dir', 'menu', 'ol', 'ul', 'dl',
27023 Roo.HtmlEditorCore.black = [
27024 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27026 'base', 'basefont', 'bgsound', 'blink', 'body',
27027 'frame', 'frameset', 'head', 'html', 'ilayer',
27028 'iframe', 'layer', 'link', 'meta', 'object',
27029 'script', 'style' ,'title', 'xml' // clean later..
27031 Roo.HtmlEditorCore.clean = [
27032 'script', 'style', 'title', 'xml'
27034 Roo.HtmlEditorCore.remove = [
27039 Roo.HtmlEditorCore.ablack = [
27043 Roo.HtmlEditorCore.aclean = [
27044 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27048 Roo.HtmlEditorCore.pwhite= [
27049 'http', 'https', 'mailto'
27052 // white listed style attributes.
27053 Roo.HtmlEditorCore.cwhite= [
27054 // 'text-align', /// default is to allow most things..
27060 // black listed style attributes.
27061 Roo.HtmlEditorCore.cblack= [
27062 // 'font-size' -- this can be set by the project
27066 Roo.HtmlEditorCore.swapCodes =[
27067 [ 8211, "–" ],
27068 [ 8212, "—" ],
27085 * @class Roo.bootstrap.HtmlEditor
27086 * @extends Roo.bootstrap.TextArea
27087 * Bootstrap HtmlEditor class
27090 * Create a new HtmlEditor
27091 * @param {Object} config The config object
27094 Roo.bootstrap.HtmlEditor = function(config){
27095 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27096 if (!this.toolbars) {
27097 this.toolbars = [];
27100 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27103 * @event initialize
27104 * Fires when the editor is fully initialized (including the iframe)
27105 * @param {HtmlEditor} this
27110 * Fires when the editor is first receives the focus. Any insertion must wait
27111 * until after this event.
27112 * @param {HtmlEditor} this
27116 * @event beforesync
27117 * Fires before the textarea is updated with content from the editor iframe. Return false
27118 * to cancel the sync.
27119 * @param {HtmlEditor} this
27120 * @param {String} html
27124 * @event beforepush
27125 * Fires before the iframe editor is updated with content from the textarea. Return false
27126 * to cancel the push.
27127 * @param {HtmlEditor} this
27128 * @param {String} html
27133 * Fires when the textarea is updated with content from the editor iframe.
27134 * @param {HtmlEditor} this
27135 * @param {String} html
27140 * Fires when the iframe editor is updated with content from the textarea.
27141 * @param {HtmlEditor} this
27142 * @param {String} html
27146 * @event editmodechange
27147 * Fires when the editor switches edit modes
27148 * @param {HtmlEditor} this
27149 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27151 editmodechange: true,
27153 * @event editorevent
27154 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27155 * @param {HtmlEditor} this
27159 * @event firstfocus
27160 * Fires when on first focus - needed by toolbars..
27161 * @param {HtmlEditor} this
27166 * Auto save the htmlEditor value as a file into Events
27167 * @param {HtmlEditor} this
27171 * @event savedpreview
27172 * preview the saved version of htmlEditor
27173 * @param {HtmlEditor} this
27180 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27184 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27189 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27194 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27199 * @cfg {Number} height (in pixels)
27203 * @cfg {Number} width (in pixels)
27208 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27211 stylesheets: false,
27216 // private properties
27217 validationEvent : false,
27219 initialized : false,
27222 onFocus : Roo.emptyFn,
27224 hideMode:'offsets',
27226 tbContainer : false,
27230 toolbarContainer :function() {
27231 return this.wrap.select('.x-html-editor-tb',true).first();
27235 * Protected method that will not generally be called directly. It
27236 * is called when the editor creates its toolbar. Override this method if you need to
27237 * add custom toolbar buttons.
27238 * @param {HtmlEditor} editor
27240 createToolbar : function(){
27241 Roo.log('renewing');
27242 Roo.log("create toolbars");
27244 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27245 this.toolbars[0].render(this.toolbarContainer());
27249 // if (!editor.toolbars || !editor.toolbars.length) {
27250 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27253 // for (var i =0 ; i < editor.toolbars.length;i++) {
27254 // editor.toolbars[i] = Roo.factory(
27255 // typeof(editor.toolbars[i]) == 'string' ?
27256 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27257 // Roo.bootstrap.HtmlEditor);
27258 // editor.toolbars[i].init(editor);
27264 onRender : function(ct, position)
27266 // Roo.log("Call onRender: " + this.xtype);
27268 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27270 this.wrap = this.inputEl().wrap({
27271 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27274 this.editorcore.onRender(ct, position);
27276 if (this.resizable) {
27277 this.resizeEl = new Roo.Resizable(this.wrap, {
27281 minHeight : this.height,
27282 height: this.height,
27283 handles : this.resizable,
27286 resize : function(r, w, h) {
27287 _t.onResize(w,h); // -something
27293 this.createToolbar(this);
27296 if(!this.width && this.resizable){
27297 this.setSize(this.wrap.getSize());
27299 if (this.resizeEl) {
27300 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27301 // should trigger onReize..
27307 onResize : function(w, h)
27309 Roo.log('resize: ' +w + ',' + h );
27310 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27314 if(this.inputEl() ){
27315 if(typeof w == 'number'){
27316 var aw = w - this.wrap.getFrameWidth('lr');
27317 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27320 if(typeof h == 'number'){
27321 var tbh = -11; // fixme it needs to tool bar size!
27322 for (var i =0; i < this.toolbars.length;i++) {
27323 // fixme - ask toolbars for heights?
27324 tbh += this.toolbars[i].el.getHeight();
27325 //if (this.toolbars[i].footer) {
27326 // tbh += this.toolbars[i].footer.el.getHeight();
27334 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27335 ah -= 5; // knock a few pixes off for look..
27336 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27340 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27341 this.editorcore.onResize(ew,eh);
27346 * Toggles the editor between standard and source edit mode.
27347 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27349 toggleSourceEdit : function(sourceEditMode)
27351 this.editorcore.toggleSourceEdit(sourceEditMode);
27353 if(this.editorcore.sourceEditMode){
27354 Roo.log('editor - showing textarea');
27357 // Roo.log(this.syncValue());
27359 this.inputEl().removeClass(['hide', 'x-hidden']);
27360 this.inputEl().dom.removeAttribute('tabIndex');
27361 this.inputEl().focus();
27363 Roo.log('editor - hiding textarea');
27365 // Roo.log(this.pushValue());
27368 this.inputEl().addClass(['hide', 'x-hidden']);
27369 this.inputEl().dom.setAttribute('tabIndex', -1);
27370 //this.deferFocus();
27373 if(this.resizable){
27374 this.setSize(this.wrap.getSize());
27377 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27380 // private (for BoxComponent)
27381 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27383 // private (for BoxComponent)
27384 getResizeEl : function(){
27388 // private (for BoxComponent)
27389 getPositionEl : function(){
27394 initEvents : function(){
27395 this.originalValue = this.getValue();
27399 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27402 // markInvalid : Roo.emptyFn,
27404 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27407 // clearInvalid : Roo.emptyFn,
27409 setValue : function(v){
27410 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27411 this.editorcore.pushValue();
27416 deferFocus : function(){
27417 this.focus.defer(10, this);
27421 focus : function(){
27422 this.editorcore.focus();
27428 onDestroy : function(){
27434 for (var i =0; i < this.toolbars.length;i++) {
27435 // fixme - ask toolbars for heights?
27436 this.toolbars[i].onDestroy();
27439 this.wrap.dom.innerHTML = '';
27440 this.wrap.remove();
27445 onFirstFocus : function(){
27446 //Roo.log("onFirstFocus");
27447 this.editorcore.onFirstFocus();
27448 for (var i =0; i < this.toolbars.length;i++) {
27449 this.toolbars[i].onFirstFocus();
27455 syncValue : function()
27457 this.editorcore.syncValue();
27460 pushValue : function()
27462 this.editorcore.pushValue();
27466 // hide stuff that is not compatible
27480 * @event specialkey
27484 * @cfg {String} fieldClass @hide
27487 * @cfg {String} focusClass @hide
27490 * @cfg {String} autoCreate @hide
27493 * @cfg {String} inputType @hide
27497 * @cfg {String} invalidText @hide
27500 * @cfg {String} msgFx @hide
27503 * @cfg {String} validateOnBlur @hide
27512 Roo.namespace('Roo.bootstrap.htmleditor');
27514 * @class Roo.bootstrap.HtmlEditorToolbar1
27520 new Roo.bootstrap.HtmlEditor({
27523 new Roo.bootstrap.HtmlEditorToolbar1({
27524 disable : { fonts: 1 , format: 1, ..., ... , ...],
27530 * @cfg {Object} disable List of elements to disable..
27531 * @cfg {Array} btns List of additional buttons.
27535 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27538 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27541 Roo.apply(this, config);
27543 // default disabled, based on 'good practice'..
27544 this.disable = this.disable || {};
27545 Roo.applyIf(this.disable, {
27548 specialElements : true
27550 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27552 this.editor = config.editor;
27553 this.editorcore = config.editor.editorcore;
27555 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27557 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27558 // dont call parent... till later.
27560 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27565 editorcore : false,
27570 "h1","h2","h3","h4","h5","h6",
27572 "abbr", "acronym", "address", "cite", "samp", "var",
27576 onRender : function(ct, position)
27578 // Roo.log("Call onRender: " + this.xtype);
27580 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27582 this.el.dom.style.marginBottom = '0';
27584 var editorcore = this.editorcore;
27585 var editor= this.editor;
27588 var btn = function(id,cmd , toggle, handler, html){
27590 var event = toggle ? 'toggle' : 'click';
27595 xns: Roo.bootstrap,
27599 enableToggle:toggle !== false,
27601 pressed : toggle ? false : null,
27604 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27605 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27611 // var cb_box = function...
27616 xns: Roo.bootstrap,
27621 xns: Roo.bootstrap,
27625 Roo.each(this.formats, function(f) {
27626 style.menu.items.push({
27628 xns: Roo.bootstrap,
27629 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27634 editorcore.insertTag(this.tagname);
27641 children.push(style);
27643 btn('bold',false,true);
27644 btn('italic',false,true);
27645 btn('align-left', 'justifyleft',true);
27646 btn('align-center', 'justifycenter',true);
27647 btn('align-right' , 'justifyright',true);
27648 btn('link', false, false, function(btn) {
27649 //Roo.log("create link?");
27650 var url = prompt(this.createLinkText, this.defaultLinkValue);
27651 if(url && url != 'http:/'+'/'){
27652 this.editorcore.relayCmd('createlink', url);
27655 btn('list','insertunorderedlist',true);
27656 btn('pencil', false,true, function(btn){
27658 this.toggleSourceEdit(btn.pressed);
27661 if (this.editor.btns.length > 0) {
27662 for (var i = 0; i<this.editor.btns.length; i++) {
27663 children.push(this.editor.btns[i]);
27671 xns: Roo.bootstrap,
27676 xns: Roo.bootstrap,
27681 cog.menu.items.push({
27683 xns: Roo.bootstrap,
27684 html : Clean styles,
27689 editorcore.insertTag(this.tagname);
27698 this.xtype = 'NavSimplebar';
27700 for(var i=0;i< children.length;i++) {
27702 this.buttons.add(this.addxtypeChild(children[i]));
27706 editor.on('editorevent', this.updateToolbar, this);
27708 onBtnClick : function(id)
27710 this.editorcore.relayCmd(id);
27711 this.editorcore.focus();
27715 * Protected method that will not generally be called directly. It triggers
27716 * a toolbar update by reading the markup state of the current selection in the editor.
27718 updateToolbar: function(){
27720 if(!this.editorcore.activated){
27721 this.editor.onFirstFocus(); // is this neeed?
27725 var btns = this.buttons;
27726 var doc = this.editorcore.doc;
27727 btns.get('bold').setActive(doc.queryCommandState('bold'));
27728 btns.get('italic').setActive(doc.queryCommandState('italic'));
27729 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27731 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27732 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27733 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27735 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27736 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27739 var ans = this.editorcore.getAllAncestors();
27740 if (this.formatCombo) {
27743 var store = this.formatCombo.store;
27744 this.formatCombo.setValue("");
27745 for (var i =0; i < ans.length;i++) {
27746 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27748 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27756 // hides menus... - so this cant be on a menu...
27757 Roo.bootstrap.MenuMgr.hideAll();
27759 Roo.bootstrap.MenuMgr.hideAll();
27760 //this.editorsyncValue();
27762 onFirstFocus: function() {
27763 this.buttons.each(function(item){
27767 toggleSourceEdit : function(sourceEditMode){
27770 if(sourceEditMode){
27771 Roo.log("disabling buttons");
27772 this.buttons.each( function(item){
27773 if(item.cmd != 'pencil'){
27779 Roo.log("enabling buttons");
27780 if(this.editorcore.initialized){
27781 this.buttons.each( function(item){
27787 Roo.log("calling toggole on editor");
27788 // tell the editor that it's been pressed..
27789 this.editor.toggleSourceEdit(sourceEditMode);
27803 * @class Roo.bootstrap.Markdown
27804 * @extends Roo.bootstrap.TextArea
27805 * Bootstrap Showdown editable area
27806 * @cfg {string} content
27809 * Create a new Showdown
27812 Roo.bootstrap.Markdown = function(config){
27813 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27817 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27821 initEvents : function()
27824 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27825 this.markdownEl = this.el.createChild({
27826 cls : 'roo-markdown-area'
27828 this.inputEl().addClass('d-none');
27829 if (this.getValue() == '') {
27830 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27833 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27835 this.markdownEl.on('click', this.toggleTextEdit, this);
27836 this.on('blur', this.toggleTextEdit, this);
27837 this.on('specialkey', this.resizeTextArea, this);
27840 toggleTextEdit : function()
27842 var sh = this.markdownEl.getHeight();
27843 this.inputEl().addClass('d-none');
27844 this.markdownEl.addClass('d-none');
27845 if (!this.editing) {
27847 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27848 this.inputEl().removeClass('d-none');
27849 this.inputEl().focus();
27850 this.editing = true;
27853 // show showdown...
27854 this.updateMarkdown();
27855 this.markdownEl.removeClass('d-none');
27856 this.editing = false;
27859 updateMarkdown : function()
27861 if (this.getValue() == '') {
27862 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27866 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27869 resizeTextArea: function () {
27872 Roo.log([sh, this.getValue().split("\n").length * 30]);
27873 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27875 setValue : function(val)
27877 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27878 if (!this.editing) {
27879 this.updateMarkdown();
27885 if (!this.editing) {
27886 this.toggleTextEdit();
27894 * Ext JS Library 1.1.1
27895 * Copyright(c) 2006-2007, Ext JS, LLC.
27897 * Originally Released Under LGPL - original licence link has changed is not relivant.
27900 * <script type="text/javascript">
27904 * @class Roo.bootstrap.PagingToolbar
27905 * @extends Roo.bootstrap.NavSimplebar
27906 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27908 * Create a new PagingToolbar
27909 * @param {Object} config The config object
27910 * @param {Roo.data.Store} store
27912 Roo.bootstrap.PagingToolbar = function(config)
27914 // old args format still supported... - xtype is prefered..
27915 // created from xtype...
27917 this.ds = config.dataSource;
27919 if (config.store && !this.ds) {
27920 this.store= Roo.factory(config.store, Roo.data);
27921 this.ds = this.store;
27922 this.ds.xmodule = this.xmodule || false;
27925 this.toolbarItems = [];
27926 if (config.items) {
27927 this.toolbarItems = config.items;
27930 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27935 this.bind(this.ds);
27938 if (Roo.bootstrap.version == 4) {
27939 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27941 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27946 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27948 * @cfg {Roo.data.Store} dataSource
27949 * The underlying data store providing the paged data
27952 * @cfg {String/HTMLElement/Element} container
27953 * container The id or element that will contain the toolbar
27956 * @cfg {Boolean} displayInfo
27957 * True to display the displayMsg (defaults to false)
27960 * @cfg {Number} pageSize
27961 * The number of records to display per page (defaults to 20)
27965 * @cfg {String} displayMsg
27966 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27968 displayMsg : 'Displaying {0} - {1} of {2}',
27970 * @cfg {String} emptyMsg
27971 * The message to display when no records are found (defaults to "No data to display")
27973 emptyMsg : 'No data to display',
27975 * Customizable piece of the default paging text (defaults to "Page")
27978 beforePageText : "Page",
27980 * Customizable piece of the default paging text (defaults to "of %0")
27983 afterPageText : "of {0}",
27985 * Customizable piece of the default paging text (defaults to "First Page")
27988 firstText : "First Page",
27990 * Customizable piece of the default paging text (defaults to "Previous Page")
27993 prevText : "Previous Page",
27995 * Customizable piece of the default paging text (defaults to "Next Page")
27998 nextText : "Next Page",
28000 * Customizable piece of the default paging text (defaults to "Last Page")
28003 lastText : "Last Page",
28005 * Customizable piece of the default paging text (defaults to "Refresh")
28008 refreshText : "Refresh",
28012 onRender : function(ct, position)
28014 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28015 this.navgroup.parentId = this.id;
28016 this.navgroup.onRender(this.el, null);
28017 // add the buttons to the navgroup
28019 if(this.displayInfo){
28020 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28021 this.displayEl = this.el.select('.x-paging-info', true).first();
28022 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28023 // this.displayEl = navel.el.select('span',true).first();
28029 Roo.each(_this.buttons, function(e){ // this might need to use render????
28030 Roo.factory(e).render(_this.el);
28034 Roo.each(_this.toolbarItems, function(e) {
28035 _this.navgroup.addItem(e);
28039 this.first = this.navgroup.addItem({
28040 tooltip: this.firstText,
28041 cls: "prev btn-outline-secondary",
28042 html : ' <i class="fa fa-step-backward"></i>',
28044 preventDefault: true,
28045 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28048 this.prev = this.navgroup.addItem({
28049 tooltip: this.prevText,
28050 cls: "prev btn-outline-secondary",
28051 html : ' <i class="fa fa-backward"></i>',
28053 preventDefault: true,
28054 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28056 //this.addSeparator();
28059 var field = this.navgroup.addItem( {
28061 cls : 'x-paging-position btn-outline-secondary',
28063 html : this.beforePageText +
28064 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28065 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28068 this.field = field.el.select('input', true).first();
28069 this.field.on("keydown", this.onPagingKeydown, this);
28070 this.field.on("focus", function(){this.dom.select();});
28073 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28074 //this.field.setHeight(18);
28075 //this.addSeparator();
28076 this.next = this.navgroup.addItem({
28077 tooltip: this.nextText,
28078 cls: "next btn-outline-secondary",
28079 html : ' <i class="fa fa-forward"></i>',
28081 preventDefault: true,
28082 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28084 this.last = this.navgroup.addItem({
28085 tooltip: this.lastText,
28086 html : ' <i class="fa fa-step-forward"></i>',
28087 cls: "next btn-outline-secondary",
28089 preventDefault: true,
28090 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28092 //this.addSeparator();
28093 this.loading = this.navgroup.addItem({
28094 tooltip: this.refreshText,
28095 cls: "btn-outline-secondary",
28096 html : ' <i class="fa fa-refresh"></i>',
28097 preventDefault: true,
28098 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28104 updateInfo : function(){
28105 if(this.displayEl){
28106 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28107 var msg = count == 0 ?
28111 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28113 this.displayEl.update(msg);
28118 onLoad : function(ds, r, o)
28120 this.cursor = o.params && o.params.start ? o.params.start : 0;
28122 var d = this.getPageData(),
28127 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28128 this.field.dom.value = ap;
28129 this.first.setDisabled(ap == 1);
28130 this.prev.setDisabled(ap == 1);
28131 this.next.setDisabled(ap == ps);
28132 this.last.setDisabled(ap == ps);
28133 this.loading.enable();
28138 getPageData : function(){
28139 var total = this.ds.getTotalCount();
28142 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28143 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28148 onLoadError : function(){
28149 this.loading.enable();
28153 onPagingKeydown : function(e){
28154 var k = e.getKey();
28155 var d = this.getPageData();
28157 var v = this.field.dom.value, pageNum;
28158 if(!v || isNaN(pageNum = parseInt(v, 10))){
28159 this.field.dom.value = d.activePage;
28162 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28163 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28166 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))
28168 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28169 this.field.dom.value = pageNum;
28170 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28173 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28175 var v = this.field.dom.value, pageNum;
28176 var increment = (e.shiftKey) ? 10 : 1;
28177 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28180 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28181 this.field.dom.value = d.activePage;
28184 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28186 this.field.dom.value = parseInt(v, 10) + increment;
28187 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28188 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28195 beforeLoad : function(){
28197 this.loading.disable();
28202 onClick : function(which){
28211 ds.load({params:{start: 0, limit: this.pageSize}});
28214 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28217 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28220 var total = ds.getTotalCount();
28221 var extra = total % this.pageSize;
28222 var lastStart = extra ? (total - extra) : total-this.pageSize;
28223 ds.load({params:{start: lastStart, limit: this.pageSize}});
28226 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28232 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28233 * @param {Roo.data.Store} store The data store to unbind
28235 unbind : function(ds){
28236 ds.un("beforeload", this.beforeLoad, this);
28237 ds.un("load", this.onLoad, this);
28238 ds.un("loadexception", this.onLoadError, this);
28239 ds.un("remove", this.updateInfo, this);
28240 ds.un("add", this.updateInfo, this);
28241 this.ds = undefined;
28245 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28246 * @param {Roo.data.Store} store The data store to bind
28248 bind : function(ds){
28249 ds.on("beforeload", this.beforeLoad, this);
28250 ds.on("load", this.onLoad, this);
28251 ds.on("loadexception", this.onLoadError, this);
28252 ds.on("remove", this.updateInfo, this);
28253 ds.on("add", this.updateInfo, this);
28264 * @class Roo.bootstrap.MessageBar
28265 * @extends Roo.bootstrap.Component
28266 * Bootstrap MessageBar class
28267 * @cfg {String} html contents of the MessageBar
28268 * @cfg {String} weight (info | success | warning | danger) default info
28269 * @cfg {String} beforeClass insert the bar before the given class
28270 * @cfg {Boolean} closable (true | false) default false
28271 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28274 * Create a new Element
28275 * @param {Object} config The config object
28278 Roo.bootstrap.MessageBar = function(config){
28279 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28282 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28288 beforeClass: 'bootstrap-sticky-wrap',
28290 getAutoCreate : function(){
28294 cls: 'alert alert-dismissable alert-' + this.weight,
28299 html: this.html || ''
28305 cfg.cls += ' alert-messages-fixed';
28319 onRender : function(ct, position)
28321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28324 var cfg = Roo.apply({}, this.getAutoCreate());
28328 cfg.cls += ' ' + this.cls;
28331 cfg.style = this.style;
28333 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28335 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28338 this.el.select('>button.close').on('click', this.hide, this);
28344 if (!this.rendered) {
28350 this.fireEvent('show', this);
28356 if (!this.rendered) {
28362 this.fireEvent('hide', this);
28365 update : function()
28367 // var e = this.el.dom.firstChild;
28369 // if(this.closable){
28370 // e = e.nextSibling;
28373 // e.data = this.html || '';
28375 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28391 * @class Roo.bootstrap.Graph
28392 * @extends Roo.bootstrap.Component
28393 * Bootstrap Graph class
28397 @cfg {String} graphtype bar | vbar | pie
28398 @cfg {number} g_x coodinator | centre x (pie)
28399 @cfg {number} g_y coodinator | centre y (pie)
28400 @cfg {number} g_r radius (pie)
28401 @cfg {number} g_height height of the chart (respected by all elements in the set)
28402 @cfg {number} g_width width of the chart (respected by all elements in the set)
28403 @cfg {Object} title The title of the chart
28406 -opts (object) options for the chart
28408 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28409 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28411 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.
28412 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28414 o stretch (boolean)
28416 -opts (object) options for the pie
28419 o startAngle (number)
28420 o endAngle (number)
28424 * Create a new Input
28425 * @param {Object} config The config object
28428 Roo.bootstrap.Graph = function(config){
28429 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28435 * The img click event for the img.
28436 * @param {Roo.EventObject} e
28442 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28453 //g_colors: this.colors,
28460 getAutoCreate : function(){
28471 onRender : function(ct,position){
28474 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28476 if (typeof(Raphael) == 'undefined') {
28477 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28481 this.raphael = Raphael(this.el.dom);
28483 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28484 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28485 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28486 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28488 r.text(160, 10, "Single Series Chart").attr(txtattr);
28489 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28490 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28491 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28493 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28494 r.barchart(330, 10, 300, 220, data1);
28495 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28496 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28499 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28500 // r.barchart(30, 30, 560, 250, xdata, {
28501 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28502 // axis : "0 0 1 1",
28503 // axisxlabels : xdata
28504 // //yvalues : cols,
28507 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28509 // this.load(null,xdata,{
28510 // axis : "0 0 1 1",
28511 // axisxlabels : xdata
28516 load : function(graphtype,xdata,opts)
28518 this.raphael.clear();
28520 graphtype = this.graphtype;
28525 var r = this.raphael,
28526 fin = function () {
28527 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28529 fout = function () {
28530 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28532 pfin = function() {
28533 this.sector.stop();
28534 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28537 this.label[0].stop();
28538 this.label[0].attr({ r: 7.5 });
28539 this.label[1].attr({ "font-weight": 800 });
28542 pfout = function() {
28543 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28546 this.label[0].animate({ r: 5 }, 500, "bounce");
28547 this.label[1].attr({ "font-weight": 400 });
28553 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28556 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28559 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28560 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28562 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28569 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28574 setTitle: function(o)
28579 initEvents: function() {
28582 this.el.on('click', this.onClick, this);
28586 onClick : function(e)
28588 Roo.log('img onclick');
28589 this.fireEvent('click', this, e);
28601 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28604 * @class Roo.bootstrap.dash.NumberBox
28605 * @extends Roo.bootstrap.Component
28606 * Bootstrap NumberBox class
28607 * @cfg {String} headline Box headline
28608 * @cfg {String} content Box content
28609 * @cfg {String} icon Box icon
28610 * @cfg {String} footer Footer text
28611 * @cfg {String} fhref Footer href
28614 * Create a new NumberBox
28615 * @param {Object} config The config object
28619 Roo.bootstrap.dash.NumberBox = function(config){
28620 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28624 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28633 getAutoCreate : function(){
28637 cls : 'small-box ',
28645 cls : 'roo-headline',
28646 html : this.headline
28650 cls : 'roo-content',
28651 html : this.content
28665 cls : 'ion ' + this.icon
28674 cls : 'small-box-footer',
28675 href : this.fhref || '#',
28679 cfg.cn.push(footer);
28686 onRender : function(ct,position){
28687 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28694 setHeadline: function (value)
28696 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28699 setFooter: function (value, href)
28701 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28704 this.el.select('a.small-box-footer',true).first().attr('href', href);
28709 setContent: function (value)
28711 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28714 initEvents: function()
28728 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28731 * @class Roo.bootstrap.dash.TabBox
28732 * @extends Roo.bootstrap.Component
28733 * Bootstrap TabBox class
28734 * @cfg {String} title Title of the TabBox
28735 * @cfg {String} icon Icon of the TabBox
28736 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28737 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28740 * Create a new TabBox
28741 * @param {Object} config The config object
28745 Roo.bootstrap.dash.TabBox = function(config){
28746 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28751 * When a pane is added
28752 * @param {Roo.bootstrap.dash.TabPane} pane
28756 * @event activatepane
28757 * When a pane is activated
28758 * @param {Roo.bootstrap.dash.TabPane} pane
28760 "activatepane" : true
28768 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28773 tabScrollable : false,
28775 getChildContainer : function()
28777 return this.el.select('.tab-content', true).first();
28780 getAutoCreate : function(){
28784 cls: 'pull-left header',
28792 cls: 'fa ' + this.icon
28798 cls: 'nav nav-tabs pull-right',
28804 if(this.tabScrollable){
28811 cls: 'nav nav-tabs pull-right',
28822 cls: 'nav-tabs-custom',
28827 cls: 'tab-content no-padding',
28835 initEvents : function()
28837 //Roo.log('add add pane handler');
28838 this.on('addpane', this.onAddPane, this);
28841 * Updates the box title
28842 * @param {String} html to set the title to.
28844 setTitle : function(value)
28846 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28848 onAddPane : function(pane)
28850 this.panes.push(pane);
28851 //Roo.log('addpane');
28853 // tabs are rendere left to right..
28854 if(!this.showtabs){
28858 var ctr = this.el.select('.nav-tabs', true).first();
28861 var existing = ctr.select('.nav-tab',true);
28862 var qty = existing.getCount();;
28865 var tab = ctr.createChild({
28867 cls : 'nav-tab' + (qty ? '' : ' active'),
28875 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28878 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28880 pane.el.addClass('active');
28885 onTabClick : function(ev,un,ob,pane)
28887 //Roo.log('tab - prev default');
28888 ev.preventDefault();
28891 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28892 pane.tab.addClass('active');
28893 //Roo.log(pane.title);
28894 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28895 // technically we should have a deactivate event.. but maybe add later.
28896 // and it should not de-activate the selected tab...
28897 this.fireEvent('activatepane', pane);
28898 pane.el.addClass('active');
28899 pane.fireEvent('activate');
28904 getActivePane : function()
28907 Roo.each(this.panes, function(p) {
28908 if(p.el.hasClass('active')){
28929 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28931 * @class Roo.bootstrap.TabPane
28932 * @extends Roo.bootstrap.Component
28933 * Bootstrap TabPane class
28934 * @cfg {Boolean} active (false | true) Default false
28935 * @cfg {String} title title of panel
28939 * Create a new TabPane
28940 * @param {Object} config The config object
28943 Roo.bootstrap.dash.TabPane = function(config){
28944 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28950 * When a pane is activated
28951 * @param {Roo.bootstrap.dash.TabPane} pane
28958 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28963 // the tabBox that this is attached to.
28966 getAutoCreate : function()
28974 cfg.cls += ' active';
28979 initEvents : function()
28981 //Roo.log('trigger add pane handler');
28982 this.parent().fireEvent('addpane', this)
28986 * Updates the tab title
28987 * @param {String} html to set the title to.
28989 setTitle: function(str)
28995 this.tab.select('a', true).first().dom.innerHTML = str;
29012 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29015 * @class Roo.bootstrap.menu.Menu
29016 * @extends Roo.bootstrap.Component
29017 * Bootstrap Menu class - container for Menu
29018 * @cfg {String} html Text of the menu
29019 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29020 * @cfg {String} icon Font awesome icon
29021 * @cfg {String} pos Menu align to (top | bottom) default bottom
29025 * Create a new Menu
29026 * @param {Object} config The config object
29030 Roo.bootstrap.menu.Menu = function(config){
29031 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29035 * @event beforeshow
29036 * Fires before this menu is displayed
29037 * @param {Roo.bootstrap.menu.Menu} this
29041 * @event beforehide
29042 * Fires before this menu is hidden
29043 * @param {Roo.bootstrap.menu.Menu} this
29048 * Fires after this menu is displayed
29049 * @param {Roo.bootstrap.menu.Menu} this
29054 * Fires after this menu is hidden
29055 * @param {Roo.bootstrap.menu.Menu} this
29060 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29061 * @param {Roo.bootstrap.menu.Menu} this
29062 * @param {Roo.EventObject} e
29069 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29073 weight : 'default',
29078 getChildContainer : function() {
29079 if(this.isSubMenu){
29083 return this.el.select('ul.dropdown-menu', true).first();
29086 getAutoCreate : function()
29091 cls : 'roo-menu-text',
29099 cls : 'fa ' + this.icon
29110 cls : 'dropdown-button btn btn-' + this.weight,
29115 cls : 'dropdown-toggle btn btn-' + this.weight,
29125 cls : 'dropdown-menu'
29131 if(this.pos == 'top'){
29132 cfg.cls += ' dropup';
29135 if(this.isSubMenu){
29138 cls : 'dropdown-menu'
29145 onRender : function(ct, position)
29147 this.isSubMenu = ct.hasClass('dropdown-submenu');
29149 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29152 initEvents : function()
29154 if(this.isSubMenu){
29158 this.hidden = true;
29160 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29161 this.triggerEl.on('click', this.onTriggerPress, this);
29163 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29164 this.buttonEl.on('click', this.onClick, this);
29170 if(this.isSubMenu){
29174 return this.el.select('ul.dropdown-menu', true).first();
29177 onClick : function(e)
29179 this.fireEvent("click", this, e);
29182 onTriggerPress : function(e)
29184 if (this.isVisible()) {
29191 isVisible : function(){
29192 return !this.hidden;
29197 this.fireEvent("beforeshow", this);
29199 this.hidden = false;
29200 this.el.addClass('open');
29202 Roo.get(document).on("mouseup", this.onMouseUp, this);
29204 this.fireEvent("show", this);
29211 this.fireEvent("beforehide", this);
29213 this.hidden = true;
29214 this.el.removeClass('open');
29216 Roo.get(document).un("mouseup", this.onMouseUp);
29218 this.fireEvent("hide", this);
29221 onMouseUp : function()
29235 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29238 * @class Roo.bootstrap.menu.Item
29239 * @extends Roo.bootstrap.Component
29240 * Bootstrap MenuItem class
29241 * @cfg {Boolean} submenu (true | false) default false
29242 * @cfg {String} html text of the item
29243 * @cfg {String} href the link
29244 * @cfg {Boolean} disable (true | false) default false
29245 * @cfg {Boolean} preventDefault (true | false) default true
29246 * @cfg {String} icon Font awesome icon
29247 * @cfg {String} pos Submenu align to (left | right) default right
29251 * Create a new Item
29252 * @param {Object} config The config object
29256 Roo.bootstrap.menu.Item = function(config){
29257 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29261 * Fires when the mouse is hovering over this menu
29262 * @param {Roo.bootstrap.menu.Item} this
29263 * @param {Roo.EventObject} e
29268 * Fires when the mouse exits this menu
29269 * @param {Roo.bootstrap.menu.Item} this
29270 * @param {Roo.EventObject} e
29276 * The raw click event for the entire grid.
29277 * @param {Roo.EventObject} e
29283 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29288 preventDefault: true,
29293 getAutoCreate : function()
29298 cls : 'roo-menu-item-text',
29306 cls : 'fa ' + this.icon
29315 href : this.href || '#',
29322 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29326 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29328 if(this.pos == 'left'){
29329 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29336 initEvents : function()
29338 this.el.on('mouseover', this.onMouseOver, this);
29339 this.el.on('mouseout', this.onMouseOut, this);
29341 this.el.select('a', true).first().on('click', this.onClick, this);
29345 onClick : function(e)
29347 if(this.preventDefault){
29348 e.preventDefault();
29351 this.fireEvent("click", this, e);
29354 onMouseOver : function(e)
29356 if(this.submenu && this.pos == 'left'){
29357 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29360 this.fireEvent("mouseover", this, e);
29363 onMouseOut : function(e)
29365 this.fireEvent("mouseout", this, e);
29377 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29380 * @class Roo.bootstrap.menu.Separator
29381 * @extends Roo.bootstrap.Component
29382 * Bootstrap Separator class
29385 * Create a new Separator
29386 * @param {Object} config The config object
29390 Roo.bootstrap.menu.Separator = function(config){
29391 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29394 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29396 getAutoCreate : function(){
29399 cls: 'dropdown-divider divider'
29417 * @class Roo.bootstrap.Tooltip
29418 * Bootstrap Tooltip class
29419 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29420 * to determine which dom element triggers the tooltip.
29422 * It needs to add support for additional attributes like tooltip-position
29425 * Create a new Toolti
29426 * @param {Object} config The config object
29429 Roo.bootstrap.Tooltip = function(config){
29430 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29432 this.alignment = Roo.bootstrap.Tooltip.alignment;
29434 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29435 this.alignment = config.alignment;
29440 Roo.apply(Roo.bootstrap.Tooltip, {
29442 * @function init initialize tooltip monitoring.
29446 currentTip : false,
29447 currentRegion : false,
29453 Roo.get(document).on('mouseover', this.enter ,this);
29454 Roo.get(document).on('mouseout', this.leave, this);
29457 this.currentTip = new Roo.bootstrap.Tooltip();
29460 enter : function(ev)
29462 var dom = ev.getTarget();
29464 //Roo.log(['enter',dom]);
29465 var el = Roo.fly(dom);
29466 if (this.currentEl) {
29468 //Roo.log(this.currentEl);
29469 //Roo.log(this.currentEl.contains(dom));
29470 if (this.currentEl == el) {
29473 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29479 if (this.currentTip.el) {
29480 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29484 if(!el || el.dom == document){
29490 if (!el.attr('tooltip')) {
29491 pel = el.findParent("[tooltip]");
29493 bindEl = Roo.get(pel);
29499 // you can not look for children, as if el is the body.. then everythign is the child..
29500 if (!pel && !el.attr('tooltip')) { //
29501 if (!el.select("[tooltip]").elements.length) {
29504 // is the mouse over this child...?
29505 bindEl = el.select("[tooltip]").first();
29506 var xy = ev.getXY();
29507 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29508 //Roo.log("not in region.");
29511 //Roo.log("child element over..");
29514 this.currentEl = el;
29515 this.currentTip.bind(bindEl);
29516 this.currentRegion = Roo.lib.Region.getRegion(dom);
29517 this.currentTip.enter();
29520 leave : function(ev)
29522 var dom = ev.getTarget();
29523 //Roo.log(['leave',dom]);
29524 if (!this.currentEl) {
29529 if (dom != this.currentEl.dom) {
29532 var xy = ev.getXY();
29533 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29536 // only activate leave if mouse cursor is outside... bounding box..
29541 if (this.currentTip) {
29542 this.currentTip.leave();
29544 //Roo.log('clear currentEl');
29545 this.currentEl = false;
29550 'left' : ['r-l', [-2,0], 'right'],
29551 'right' : ['l-r', [2,0], 'left'],
29552 'bottom' : ['t-b', [0,2], 'top'],
29553 'top' : [ 'b-t', [0,-2], 'bottom']
29559 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29564 delay : null, // can be { show : 300 , hide: 500}
29568 hoverState : null, //???
29570 placement : 'bottom',
29574 getAutoCreate : function(){
29581 cls : 'tooltip-arrow arrow'
29584 cls : 'tooltip-inner'
29591 bind : function(el)
29596 initEvents : function()
29598 this.arrowEl = this.el.select('.arrow', true).first();
29599 this.innerEl = this.el.select('.tooltip-inner', true).first();
29602 enter : function () {
29604 if (this.timeout != null) {
29605 clearTimeout(this.timeout);
29608 this.hoverState = 'in';
29609 //Roo.log("enter - show");
29610 if (!this.delay || !this.delay.show) {
29615 this.timeout = setTimeout(function () {
29616 if (_t.hoverState == 'in') {
29619 }, this.delay.show);
29623 clearTimeout(this.timeout);
29625 this.hoverState = 'out';
29626 if (!this.delay || !this.delay.hide) {
29632 this.timeout = setTimeout(function () {
29633 //Roo.log("leave - timeout");
29635 if (_t.hoverState == 'out') {
29637 Roo.bootstrap.Tooltip.currentEl = false;
29642 show : function (msg)
29645 this.render(document.body);
29648 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29650 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29652 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29654 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29655 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29657 var placement = typeof this.placement == 'function' ?
29658 this.placement.call(this, this.el, on_el) :
29661 var autoToken = /\s?auto?\s?/i;
29662 var autoPlace = autoToken.test(placement);
29664 placement = placement.replace(autoToken, '') || 'top';
29668 //this.el.setXY([0,0]);
29670 //this.el.dom.style.display='block';
29672 //this.el.appendTo(on_el);
29674 var p = this.getPosition();
29675 var box = this.el.getBox();
29681 var align = this.alignment[placement];
29683 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29685 if(placement == 'top' || placement == 'bottom'){
29687 placement = 'right';
29690 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29691 placement = 'left';
29694 var scroll = Roo.select('body', true).first().getScroll();
29696 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29700 align = this.alignment[placement];
29702 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29706 var elems = document.getElementsByTagName('div');
29707 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29708 for (var i = 0; i < elems.length; i++) {
29709 var zindex = Number.parseInt(
29710 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29713 if (zindex > highest) {
29720 this.el.dom.style.zIndex = highest;
29722 this.el.alignTo(this.bindEl, align[0],align[1]);
29723 //var arrow = this.el.select('.arrow',true).first();
29724 //arrow.set(align[2],
29726 this.el.addClass(placement);
29727 this.el.addClass("bs-tooltip-"+ placement);
29729 this.el.addClass('in fade show');
29731 this.hoverState = null;
29733 if (this.el.hasClass('fade')) {
29748 //this.el.setXY([0,0]);
29749 this.el.removeClass(['show', 'in']);
29765 * @class Roo.bootstrap.LocationPicker
29766 * @extends Roo.bootstrap.Component
29767 * Bootstrap LocationPicker class
29768 * @cfg {Number} latitude Position when init default 0
29769 * @cfg {Number} longitude Position when init default 0
29770 * @cfg {Number} zoom default 15
29771 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29772 * @cfg {Boolean} mapTypeControl default false
29773 * @cfg {Boolean} disableDoubleClickZoom default false
29774 * @cfg {Boolean} scrollwheel default true
29775 * @cfg {Boolean} streetViewControl default false
29776 * @cfg {Number} radius default 0
29777 * @cfg {String} locationName
29778 * @cfg {Boolean} draggable default true
29779 * @cfg {Boolean} enableAutocomplete default false
29780 * @cfg {Boolean} enableReverseGeocode default true
29781 * @cfg {String} markerTitle
29784 * Create a new LocationPicker
29785 * @param {Object} config The config object
29789 Roo.bootstrap.LocationPicker = function(config){
29791 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29796 * Fires when the picker initialized.
29797 * @param {Roo.bootstrap.LocationPicker} this
29798 * @param {Google Location} location
29802 * @event positionchanged
29803 * Fires when the picker position changed.
29804 * @param {Roo.bootstrap.LocationPicker} this
29805 * @param {Google Location} location
29807 positionchanged : true,
29810 * Fires when the map resize.
29811 * @param {Roo.bootstrap.LocationPicker} this
29816 * Fires when the map show.
29817 * @param {Roo.bootstrap.LocationPicker} this
29822 * Fires when the map hide.
29823 * @param {Roo.bootstrap.LocationPicker} this
29828 * Fires when click the map.
29829 * @param {Roo.bootstrap.LocationPicker} this
29830 * @param {Map event} e
29834 * @event mapRightClick
29835 * Fires when right click the map.
29836 * @param {Roo.bootstrap.LocationPicker} this
29837 * @param {Map event} e
29839 mapRightClick : true,
29841 * @event markerClick
29842 * Fires when click the marker.
29843 * @param {Roo.bootstrap.LocationPicker} this
29844 * @param {Map event} e
29846 markerClick : true,
29848 * @event markerRightClick
29849 * Fires when right click the marker.
29850 * @param {Roo.bootstrap.LocationPicker} this
29851 * @param {Map event} e
29853 markerRightClick : true,
29855 * @event OverlayViewDraw
29856 * Fires when OverlayView Draw
29857 * @param {Roo.bootstrap.LocationPicker} this
29859 OverlayViewDraw : true,
29861 * @event OverlayViewOnAdd
29862 * Fires when OverlayView Draw
29863 * @param {Roo.bootstrap.LocationPicker} this
29865 OverlayViewOnAdd : true,
29867 * @event OverlayViewOnRemove
29868 * Fires when OverlayView Draw
29869 * @param {Roo.bootstrap.LocationPicker} this
29871 OverlayViewOnRemove : true,
29873 * @event OverlayViewShow
29874 * Fires when OverlayView Draw
29875 * @param {Roo.bootstrap.LocationPicker} this
29876 * @param {Pixel} cpx
29878 OverlayViewShow : true,
29880 * @event OverlayViewHide
29881 * Fires when OverlayView Draw
29882 * @param {Roo.bootstrap.LocationPicker} this
29884 OverlayViewHide : true,
29886 * @event loadexception
29887 * Fires when load google lib failed.
29888 * @param {Roo.bootstrap.LocationPicker} this
29890 loadexception : true
29895 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29897 gMapContext: false,
29903 mapTypeControl: false,
29904 disableDoubleClickZoom: false,
29906 streetViewControl: false,
29910 enableAutocomplete: false,
29911 enableReverseGeocode: true,
29914 getAutoCreate: function()
29919 cls: 'roo-location-picker'
29925 initEvents: function(ct, position)
29927 if(!this.el.getWidth() || this.isApplied()){
29931 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29936 initial: function()
29938 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29939 this.fireEvent('loadexception', this);
29943 if(!this.mapTypeId){
29944 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29947 this.gMapContext = this.GMapContext();
29949 this.initOverlayView();
29951 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29955 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29956 _this.setPosition(_this.gMapContext.marker.position);
29959 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29960 _this.fireEvent('mapClick', this, event);
29964 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29965 _this.fireEvent('mapRightClick', this, event);
29969 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29970 _this.fireEvent('markerClick', this, event);
29974 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29975 _this.fireEvent('markerRightClick', this, event);
29979 this.setPosition(this.gMapContext.location);
29981 this.fireEvent('initial', this, this.gMapContext.location);
29984 initOverlayView: function()
29988 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29992 _this.fireEvent('OverlayViewDraw', _this);
29997 _this.fireEvent('OverlayViewOnAdd', _this);
30000 onRemove: function()
30002 _this.fireEvent('OverlayViewOnRemove', _this);
30005 show: function(cpx)
30007 _this.fireEvent('OverlayViewShow', _this, cpx);
30012 _this.fireEvent('OverlayViewHide', _this);
30018 fromLatLngToContainerPixel: function(event)
30020 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30023 isApplied: function()
30025 return this.getGmapContext() == false ? false : true;
30028 getGmapContext: function()
30030 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30033 GMapContext: function()
30035 var position = new google.maps.LatLng(this.latitude, this.longitude);
30037 var _map = new google.maps.Map(this.el.dom, {
30040 mapTypeId: this.mapTypeId,
30041 mapTypeControl: this.mapTypeControl,
30042 disableDoubleClickZoom: this.disableDoubleClickZoom,
30043 scrollwheel: this.scrollwheel,
30044 streetViewControl: this.streetViewControl,
30045 locationName: this.locationName,
30046 draggable: this.draggable,
30047 enableAutocomplete: this.enableAutocomplete,
30048 enableReverseGeocode: this.enableReverseGeocode
30051 var _marker = new google.maps.Marker({
30052 position: position,
30054 title: this.markerTitle,
30055 draggable: this.draggable
30062 location: position,
30063 radius: this.radius,
30064 locationName: this.locationName,
30065 addressComponents: {
30066 formatted_address: null,
30067 addressLine1: null,
30068 addressLine2: null,
30070 streetNumber: null,
30074 stateOrProvince: null
30077 domContainer: this.el.dom,
30078 geodecoder: new google.maps.Geocoder()
30082 drawCircle: function(center, radius, options)
30084 if (this.gMapContext.circle != null) {
30085 this.gMapContext.circle.setMap(null);
30089 options = Roo.apply({}, options, {
30090 strokeColor: "#0000FF",
30091 strokeOpacity: .35,
30093 fillColor: "#0000FF",
30097 options.map = this.gMapContext.map;
30098 options.radius = radius;
30099 options.center = center;
30100 this.gMapContext.circle = new google.maps.Circle(options);
30101 return this.gMapContext.circle;
30107 setPosition: function(location)
30109 this.gMapContext.location = location;
30110 this.gMapContext.marker.setPosition(location);
30111 this.gMapContext.map.panTo(location);
30112 this.drawCircle(location, this.gMapContext.radius, {});
30116 if (this.gMapContext.settings.enableReverseGeocode) {
30117 this.gMapContext.geodecoder.geocode({
30118 latLng: this.gMapContext.location
30119 }, function(results, status) {
30121 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30122 _this.gMapContext.locationName = results[0].formatted_address;
30123 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30125 _this.fireEvent('positionchanged', this, location);
30132 this.fireEvent('positionchanged', this, location);
30137 google.maps.event.trigger(this.gMapContext.map, "resize");
30139 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30141 this.fireEvent('resize', this);
30144 setPositionByLatLng: function(latitude, longitude)
30146 this.setPosition(new google.maps.LatLng(latitude, longitude));
30149 getCurrentPosition: function()
30152 latitude: this.gMapContext.location.lat(),
30153 longitude: this.gMapContext.location.lng()
30157 getAddressName: function()
30159 return this.gMapContext.locationName;
30162 getAddressComponents: function()
30164 return this.gMapContext.addressComponents;
30167 address_component_from_google_geocode: function(address_components)
30171 for (var i = 0; i < address_components.length; i++) {
30172 var component = address_components[i];
30173 if (component.types.indexOf("postal_code") >= 0) {
30174 result.postalCode = component.short_name;
30175 } else if (component.types.indexOf("street_number") >= 0) {
30176 result.streetNumber = component.short_name;
30177 } else if (component.types.indexOf("route") >= 0) {
30178 result.streetName = component.short_name;
30179 } else if (component.types.indexOf("neighborhood") >= 0) {
30180 result.city = component.short_name;
30181 } else if (component.types.indexOf("locality") >= 0) {
30182 result.city = component.short_name;
30183 } else if (component.types.indexOf("sublocality") >= 0) {
30184 result.district = component.short_name;
30185 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30186 result.stateOrProvince = component.short_name;
30187 } else if (component.types.indexOf("country") >= 0) {
30188 result.country = component.short_name;
30192 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30193 result.addressLine2 = "";
30197 setZoomLevel: function(zoom)
30199 this.gMapContext.map.setZoom(zoom);
30212 this.fireEvent('show', this);
30223 this.fireEvent('hide', this);
30228 Roo.apply(Roo.bootstrap.LocationPicker, {
30230 OverlayView : function(map, options)
30232 options = options || {};
30239 * @class Roo.bootstrap.Alert
30240 * @extends Roo.bootstrap.Component
30241 * Bootstrap Alert class - shows an alert area box
30243 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30244 Enter a valid email address
30247 * @cfg {String} title The title of alert
30248 * @cfg {String} html The content of alert
30249 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30250 * @cfg {String} fa font-awesomeicon
30251 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30252 * @cfg {Boolean} close true to show a x closer
30256 * Create a new alert
30257 * @param {Object} config The config object
30261 Roo.bootstrap.Alert = function(config){
30262 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30266 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30272 faicon: false, // BC
30276 getAutoCreate : function()
30288 style : this.close ? '' : 'display:none'
30292 cls : 'roo-alert-icon'
30297 cls : 'roo-alert-title',
30302 cls : 'roo-alert-text',
30309 cfg.cn[0].cls += ' fa ' + this.faicon;
30312 cfg.cn[0].cls += ' fa ' + this.fa;
30316 cfg.cls += ' alert-' + this.weight;
30322 initEvents: function()
30324 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30325 this.titleEl = this.el.select('.roo-alert-title',true).first();
30326 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30327 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30328 if (this.seconds > 0) {
30329 this.hide.defer(this.seconds, this);
30333 * Set the Title Message HTML
30334 * @param {String} html
30336 setTitle : function(str)
30338 this.titleEl.dom.innerHTML = str;
30342 * Set the Body Message HTML
30343 * @param {String} html
30345 setHtml : function(str)
30347 this.htmlEl.dom.innerHTML = str;
30350 * Set the Weight of the alert
30351 * @param {String} (success|info|warning|danger) weight
30354 setWeight : function(weight)
30357 this.el.removeClass('alert-' + this.weight);
30360 this.weight = weight;
30362 this.el.addClass('alert-' + this.weight);
30365 * Set the Icon of the alert
30366 * @param {String} see fontawsome names (name without the 'fa-' bit)
30368 setIcon : function(icon)
30371 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30374 this.faicon = icon;
30376 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30401 * @class Roo.bootstrap.UploadCropbox
30402 * @extends Roo.bootstrap.Component
30403 * Bootstrap UploadCropbox class
30404 * @cfg {String} emptyText show when image has been loaded
30405 * @cfg {String} rotateNotify show when image too small to rotate
30406 * @cfg {Number} errorTimeout default 3000
30407 * @cfg {Number} minWidth default 300
30408 * @cfg {Number} minHeight default 300
30409 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30410 * @cfg {Boolean} isDocument (true|false) default false
30411 * @cfg {String} url action url
30412 * @cfg {String} paramName default 'imageUpload'
30413 * @cfg {String} method default POST
30414 * @cfg {Boolean} loadMask (true|false) default true
30415 * @cfg {Boolean} loadingText default 'Loading...'
30418 * Create a new UploadCropbox
30419 * @param {Object} config The config object
30422 Roo.bootstrap.UploadCropbox = function(config){
30423 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30427 * @event beforeselectfile
30428 * Fire before select file
30429 * @param {Roo.bootstrap.UploadCropbox} this
30431 "beforeselectfile" : true,
30434 * Fire after initEvent
30435 * @param {Roo.bootstrap.UploadCropbox} this
30440 * Fire after initEvent
30441 * @param {Roo.bootstrap.UploadCropbox} this
30442 * @param {String} data
30447 * Fire when preparing the file data
30448 * @param {Roo.bootstrap.UploadCropbox} this
30449 * @param {Object} file
30454 * Fire when get exception
30455 * @param {Roo.bootstrap.UploadCropbox} this
30456 * @param {XMLHttpRequest} xhr
30458 "exception" : true,
30460 * @event beforeloadcanvas
30461 * Fire before load the canvas
30462 * @param {Roo.bootstrap.UploadCropbox} this
30463 * @param {String} src
30465 "beforeloadcanvas" : true,
30468 * Fire when trash image
30469 * @param {Roo.bootstrap.UploadCropbox} this
30474 * Fire when download the image
30475 * @param {Roo.bootstrap.UploadCropbox} this
30479 * @event footerbuttonclick
30480 * Fire when footerbuttonclick
30481 * @param {Roo.bootstrap.UploadCropbox} this
30482 * @param {String} type
30484 "footerbuttonclick" : true,
30488 * @param {Roo.bootstrap.UploadCropbox} this
30493 * Fire when rotate the image
30494 * @param {Roo.bootstrap.UploadCropbox} this
30495 * @param {String} pos
30500 * Fire when inspect the file
30501 * @param {Roo.bootstrap.UploadCropbox} this
30502 * @param {Object} file
30507 * Fire when xhr upload the file
30508 * @param {Roo.bootstrap.UploadCropbox} this
30509 * @param {Object} data
30514 * Fire when arrange the file data
30515 * @param {Roo.bootstrap.UploadCropbox} this
30516 * @param {Object} formData
30521 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30524 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30526 emptyText : 'Click to upload image',
30527 rotateNotify : 'Image is too small to rotate',
30528 errorTimeout : 3000,
30542 cropType : 'image/jpeg',
30544 canvasLoaded : false,
30545 isDocument : false,
30547 paramName : 'imageUpload',
30549 loadingText : 'Loading...',
30552 getAutoCreate : function()
30556 cls : 'roo-upload-cropbox',
30560 cls : 'roo-upload-cropbox-selector',
30565 cls : 'roo-upload-cropbox-body',
30566 style : 'cursor:pointer',
30570 cls : 'roo-upload-cropbox-preview'
30574 cls : 'roo-upload-cropbox-thumb'
30578 cls : 'roo-upload-cropbox-empty-notify',
30579 html : this.emptyText
30583 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30584 html : this.rotateNotify
30590 cls : 'roo-upload-cropbox-footer',
30593 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30603 onRender : function(ct, position)
30605 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30607 if (this.buttons.length) {
30609 Roo.each(this.buttons, function(bb) {
30611 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30613 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30619 this.maskEl = this.el;
30623 initEvents : function()
30625 this.urlAPI = (window.createObjectURL && window) ||
30626 (window.URL && URL.revokeObjectURL && URL) ||
30627 (window.webkitURL && webkitURL);
30629 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30630 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30632 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30633 this.selectorEl.hide();
30635 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30636 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30638 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30639 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30640 this.thumbEl.hide();
30642 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30643 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30645 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30646 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30647 this.errorEl.hide();
30649 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30650 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30651 this.footerEl.hide();
30653 this.setThumbBoxSize();
30659 this.fireEvent('initial', this);
30666 window.addEventListener("resize", function() { _this.resize(); } );
30668 this.bodyEl.on('click', this.beforeSelectFile, this);
30671 this.bodyEl.on('touchstart', this.onTouchStart, this);
30672 this.bodyEl.on('touchmove', this.onTouchMove, this);
30673 this.bodyEl.on('touchend', this.onTouchEnd, this);
30677 this.bodyEl.on('mousedown', this.onMouseDown, this);
30678 this.bodyEl.on('mousemove', this.onMouseMove, this);
30679 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30680 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30681 Roo.get(document).on('mouseup', this.onMouseUp, this);
30684 this.selectorEl.on('change', this.onFileSelected, this);
30690 this.baseScale = 1;
30692 this.baseRotate = 1;
30693 this.dragable = false;
30694 this.pinching = false;
30697 this.cropData = false;
30698 this.notifyEl.dom.innerHTML = this.emptyText;
30700 this.selectorEl.dom.value = '';
30704 resize : function()
30706 if(this.fireEvent('resize', this) != false){
30707 this.setThumbBoxPosition();
30708 this.setCanvasPosition();
30712 onFooterButtonClick : function(e, el, o, type)
30715 case 'rotate-left' :
30716 this.onRotateLeft(e);
30718 case 'rotate-right' :
30719 this.onRotateRight(e);
30722 this.beforeSelectFile(e);
30737 this.fireEvent('footerbuttonclick', this, type);
30740 beforeSelectFile : function(e)
30742 e.preventDefault();
30744 if(this.fireEvent('beforeselectfile', this) != false){
30745 this.selectorEl.dom.click();
30749 onFileSelected : function(e)
30751 e.preventDefault();
30753 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30757 var file = this.selectorEl.dom.files[0];
30759 if(this.fireEvent('inspect', this, file) != false){
30760 this.prepare(file);
30765 trash : function(e)
30767 this.fireEvent('trash', this);
30770 download : function(e)
30772 this.fireEvent('download', this);
30775 loadCanvas : function(src)
30777 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30781 this.imageEl = document.createElement('img');
30785 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30787 this.imageEl.src = src;
30791 onLoadCanvas : function()
30793 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30794 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30796 this.bodyEl.un('click', this.beforeSelectFile, this);
30798 this.notifyEl.hide();
30799 this.thumbEl.show();
30800 this.footerEl.show();
30802 this.baseRotateLevel();
30804 if(this.isDocument){
30805 this.setThumbBoxSize();
30808 this.setThumbBoxPosition();
30810 this.baseScaleLevel();
30816 this.canvasLoaded = true;
30819 this.maskEl.unmask();
30824 setCanvasPosition : function()
30826 if(!this.canvasEl){
30830 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30831 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30833 this.previewEl.setLeft(pw);
30834 this.previewEl.setTop(ph);
30838 onMouseDown : function(e)
30842 this.dragable = true;
30843 this.pinching = false;
30845 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30846 this.dragable = false;
30850 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30851 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30855 onMouseMove : function(e)
30859 if(!this.canvasLoaded){
30863 if (!this.dragable){
30867 var minX = Math.ceil(this.thumbEl.getLeft(true));
30868 var minY = Math.ceil(this.thumbEl.getTop(true));
30870 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30871 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30873 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30874 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30876 x = x - this.mouseX;
30877 y = y - this.mouseY;
30879 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30880 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30882 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30883 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30885 this.previewEl.setLeft(bgX);
30886 this.previewEl.setTop(bgY);
30888 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30889 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30892 onMouseUp : function(e)
30896 this.dragable = false;
30899 onMouseWheel : function(e)
30903 this.startScale = this.scale;
30905 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30907 if(!this.zoomable()){
30908 this.scale = this.startScale;
30917 zoomable : function()
30919 var minScale = this.thumbEl.getWidth() / this.minWidth;
30921 if(this.minWidth < this.minHeight){
30922 minScale = this.thumbEl.getHeight() / this.minHeight;
30925 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30926 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30930 (this.rotate == 0 || this.rotate == 180) &&
30932 width > this.imageEl.OriginWidth ||
30933 height > this.imageEl.OriginHeight ||
30934 (width < this.minWidth && height < this.minHeight)
30942 (this.rotate == 90 || this.rotate == 270) &&
30944 width > this.imageEl.OriginWidth ||
30945 height > this.imageEl.OriginHeight ||
30946 (width < this.minHeight && height < this.minWidth)
30953 !this.isDocument &&
30954 (this.rotate == 0 || this.rotate == 180) &&
30956 width < this.minWidth ||
30957 width > this.imageEl.OriginWidth ||
30958 height < this.minHeight ||
30959 height > this.imageEl.OriginHeight
30966 !this.isDocument &&
30967 (this.rotate == 90 || this.rotate == 270) &&
30969 width < this.minHeight ||
30970 width > this.imageEl.OriginWidth ||
30971 height < this.minWidth ||
30972 height > this.imageEl.OriginHeight
30982 onRotateLeft : function(e)
30984 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30986 var minScale = this.thumbEl.getWidth() / this.minWidth;
30988 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30989 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30991 this.startScale = this.scale;
30993 while (this.getScaleLevel() < minScale){
30995 this.scale = this.scale + 1;
30997 if(!this.zoomable()){
31002 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31003 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31008 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31015 this.scale = this.startScale;
31017 this.onRotateFail();
31022 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31024 if(this.isDocument){
31025 this.setThumbBoxSize();
31026 this.setThumbBoxPosition();
31027 this.setCanvasPosition();
31032 this.fireEvent('rotate', this, 'left');
31036 onRotateRight : function(e)
31038 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31040 var minScale = this.thumbEl.getWidth() / this.minWidth;
31042 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31043 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31045 this.startScale = this.scale;
31047 while (this.getScaleLevel() < minScale){
31049 this.scale = this.scale + 1;
31051 if(!this.zoomable()){
31056 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31057 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31062 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31069 this.scale = this.startScale;
31071 this.onRotateFail();
31076 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31078 if(this.isDocument){
31079 this.setThumbBoxSize();
31080 this.setThumbBoxPosition();
31081 this.setCanvasPosition();
31086 this.fireEvent('rotate', this, 'right');
31089 onRotateFail : function()
31091 this.errorEl.show(true);
31095 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31100 this.previewEl.dom.innerHTML = '';
31102 var canvasEl = document.createElement("canvas");
31104 var contextEl = canvasEl.getContext("2d");
31106 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31107 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31108 var center = this.imageEl.OriginWidth / 2;
31110 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31111 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31112 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31113 center = this.imageEl.OriginHeight / 2;
31116 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31118 contextEl.translate(center, center);
31119 contextEl.rotate(this.rotate * Math.PI / 180);
31121 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31123 this.canvasEl = document.createElement("canvas");
31125 this.contextEl = this.canvasEl.getContext("2d");
31127 switch (this.rotate) {
31130 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31131 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31133 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31138 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31139 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31141 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31142 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);
31146 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31151 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31152 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31154 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31155 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);
31159 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);
31164 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31165 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31167 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31168 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31172 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);
31179 this.previewEl.appendChild(this.canvasEl);
31181 this.setCanvasPosition();
31186 if(!this.canvasLoaded){
31190 var imageCanvas = document.createElement("canvas");
31192 var imageContext = imageCanvas.getContext("2d");
31194 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31195 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31197 var center = imageCanvas.width / 2;
31199 imageContext.translate(center, center);
31201 imageContext.rotate(this.rotate * Math.PI / 180);
31203 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31205 var canvas = document.createElement("canvas");
31207 var context = canvas.getContext("2d");
31209 canvas.width = this.minWidth;
31210 canvas.height = this.minHeight;
31212 switch (this.rotate) {
31215 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31216 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31218 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31219 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31221 var targetWidth = this.minWidth - 2 * x;
31222 var targetHeight = this.minHeight - 2 * y;
31226 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31227 scale = targetWidth / width;
31230 if(x > 0 && y == 0){
31231 scale = targetHeight / height;
31234 if(x > 0 && y > 0){
31235 scale = targetWidth / width;
31237 if(width < height){
31238 scale = targetHeight / height;
31242 context.scale(scale, scale);
31244 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31245 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31247 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31248 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31250 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31255 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31256 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31258 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31259 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31261 var targetWidth = this.minWidth - 2 * x;
31262 var targetHeight = this.minHeight - 2 * y;
31266 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31267 scale = targetWidth / width;
31270 if(x > 0 && y == 0){
31271 scale = targetHeight / height;
31274 if(x > 0 && y > 0){
31275 scale = targetWidth / width;
31277 if(width < height){
31278 scale = targetHeight / height;
31282 context.scale(scale, scale);
31284 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31285 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31287 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31288 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31290 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31292 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31297 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31298 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31300 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31301 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31303 var targetWidth = this.minWidth - 2 * x;
31304 var targetHeight = this.minHeight - 2 * y;
31308 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31309 scale = targetWidth / width;
31312 if(x > 0 && y == 0){
31313 scale = targetHeight / height;
31316 if(x > 0 && y > 0){
31317 scale = targetWidth / width;
31319 if(width < height){
31320 scale = targetHeight / height;
31324 context.scale(scale, scale);
31326 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31327 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31329 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31330 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31332 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31333 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31335 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31340 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31341 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31343 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31344 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31346 var targetWidth = this.minWidth - 2 * x;
31347 var targetHeight = this.minHeight - 2 * y;
31351 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31352 scale = targetWidth / width;
31355 if(x > 0 && y == 0){
31356 scale = targetHeight / height;
31359 if(x > 0 && y > 0){
31360 scale = targetWidth / width;
31362 if(width < height){
31363 scale = targetHeight / height;
31367 context.scale(scale, scale);
31369 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31370 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31372 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31373 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31375 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31377 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31384 this.cropData = canvas.toDataURL(this.cropType);
31386 if(this.fireEvent('crop', this, this.cropData) !== false){
31387 this.process(this.file, this.cropData);
31394 setThumbBoxSize : function()
31398 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31399 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31400 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31402 this.minWidth = width;
31403 this.minHeight = height;
31405 if(this.rotate == 90 || this.rotate == 270){
31406 this.minWidth = height;
31407 this.minHeight = width;
31412 width = Math.ceil(this.minWidth * height / this.minHeight);
31414 if(this.minWidth > this.minHeight){
31416 height = Math.ceil(this.minHeight * width / this.minWidth);
31419 this.thumbEl.setStyle({
31420 width : width + 'px',
31421 height : height + 'px'
31428 setThumbBoxPosition : function()
31430 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31431 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31433 this.thumbEl.setLeft(x);
31434 this.thumbEl.setTop(y);
31438 baseRotateLevel : function()
31440 this.baseRotate = 1;
31443 typeof(this.exif) != 'undefined' &&
31444 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31445 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31447 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31450 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31454 baseScaleLevel : function()
31458 if(this.isDocument){
31460 if(this.baseRotate == 6 || this.baseRotate == 8){
31462 height = this.thumbEl.getHeight();
31463 this.baseScale = height / this.imageEl.OriginWidth;
31465 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31466 width = this.thumbEl.getWidth();
31467 this.baseScale = width / this.imageEl.OriginHeight;
31473 height = this.thumbEl.getHeight();
31474 this.baseScale = height / this.imageEl.OriginHeight;
31476 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31477 width = this.thumbEl.getWidth();
31478 this.baseScale = width / this.imageEl.OriginWidth;
31484 if(this.baseRotate == 6 || this.baseRotate == 8){
31486 width = this.thumbEl.getHeight();
31487 this.baseScale = width / this.imageEl.OriginHeight;
31489 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31490 height = this.thumbEl.getWidth();
31491 this.baseScale = height / this.imageEl.OriginHeight;
31494 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31495 height = this.thumbEl.getWidth();
31496 this.baseScale = height / this.imageEl.OriginHeight;
31498 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31499 width = this.thumbEl.getHeight();
31500 this.baseScale = width / this.imageEl.OriginWidth;
31507 width = this.thumbEl.getWidth();
31508 this.baseScale = width / this.imageEl.OriginWidth;
31510 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31511 height = this.thumbEl.getHeight();
31512 this.baseScale = height / this.imageEl.OriginHeight;
31515 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31517 height = this.thumbEl.getHeight();
31518 this.baseScale = height / this.imageEl.OriginHeight;
31520 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31521 width = this.thumbEl.getWidth();
31522 this.baseScale = width / this.imageEl.OriginWidth;
31530 getScaleLevel : function()
31532 return this.baseScale * Math.pow(1.1, this.scale);
31535 onTouchStart : function(e)
31537 if(!this.canvasLoaded){
31538 this.beforeSelectFile(e);
31542 var touches = e.browserEvent.touches;
31548 if(touches.length == 1){
31549 this.onMouseDown(e);
31553 if(touches.length != 2){
31559 for(var i = 0, finger; finger = touches[i]; i++){
31560 coords.push(finger.pageX, finger.pageY);
31563 var x = Math.pow(coords[0] - coords[2], 2);
31564 var y = Math.pow(coords[1] - coords[3], 2);
31566 this.startDistance = Math.sqrt(x + y);
31568 this.startScale = this.scale;
31570 this.pinching = true;
31571 this.dragable = false;
31575 onTouchMove : function(e)
31577 if(!this.pinching && !this.dragable){
31581 var touches = e.browserEvent.touches;
31588 this.onMouseMove(e);
31594 for(var i = 0, finger; finger = touches[i]; i++){
31595 coords.push(finger.pageX, finger.pageY);
31598 var x = Math.pow(coords[0] - coords[2], 2);
31599 var y = Math.pow(coords[1] - coords[3], 2);
31601 this.endDistance = Math.sqrt(x + y);
31603 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31605 if(!this.zoomable()){
31606 this.scale = this.startScale;
31614 onTouchEnd : function(e)
31616 this.pinching = false;
31617 this.dragable = false;
31621 process : function(file, crop)
31624 this.maskEl.mask(this.loadingText);
31627 this.xhr = new XMLHttpRequest();
31629 file.xhr = this.xhr;
31631 this.xhr.open(this.method, this.url, true);
31634 "Accept": "application/json",
31635 "Cache-Control": "no-cache",
31636 "X-Requested-With": "XMLHttpRequest"
31639 for (var headerName in headers) {
31640 var headerValue = headers[headerName];
31642 this.xhr.setRequestHeader(headerName, headerValue);
31648 this.xhr.onload = function()
31650 _this.xhrOnLoad(_this.xhr);
31653 this.xhr.onerror = function()
31655 _this.xhrOnError(_this.xhr);
31658 var formData = new FormData();
31660 formData.append('returnHTML', 'NO');
31663 formData.append('crop', crop);
31666 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31667 formData.append(this.paramName, file, file.name);
31670 if(typeof(file.filename) != 'undefined'){
31671 formData.append('filename', file.filename);
31674 if(typeof(file.mimetype) != 'undefined'){
31675 formData.append('mimetype', file.mimetype);
31678 if(this.fireEvent('arrange', this, formData) != false){
31679 this.xhr.send(formData);
31683 xhrOnLoad : function(xhr)
31686 this.maskEl.unmask();
31689 if (xhr.readyState !== 4) {
31690 this.fireEvent('exception', this, xhr);
31694 var response = Roo.decode(xhr.responseText);
31696 if(!response.success){
31697 this.fireEvent('exception', this, xhr);
31701 var response = Roo.decode(xhr.responseText);
31703 this.fireEvent('upload', this, response);
31707 xhrOnError : function()
31710 this.maskEl.unmask();
31713 Roo.log('xhr on error');
31715 var response = Roo.decode(xhr.responseText);
31721 prepare : function(file)
31724 this.maskEl.mask(this.loadingText);
31730 if(typeof(file) === 'string'){
31731 this.loadCanvas(file);
31735 if(!file || !this.urlAPI){
31740 this.cropType = file.type;
31744 if(this.fireEvent('prepare', this, this.file) != false){
31746 var reader = new FileReader();
31748 reader.onload = function (e) {
31749 if (e.target.error) {
31750 Roo.log(e.target.error);
31754 var buffer = e.target.result,
31755 dataView = new DataView(buffer),
31757 maxOffset = dataView.byteLength - 4,
31761 if (dataView.getUint16(0) === 0xffd8) {
31762 while (offset < maxOffset) {
31763 markerBytes = dataView.getUint16(offset);
31765 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31766 markerLength = dataView.getUint16(offset + 2) + 2;
31767 if (offset + markerLength > dataView.byteLength) {
31768 Roo.log('Invalid meta data: Invalid segment size.');
31772 if(markerBytes == 0xffe1){
31773 _this.parseExifData(
31780 offset += markerLength;
31790 var url = _this.urlAPI.createObjectURL(_this.file);
31792 _this.loadCanvas(url);
31797 reader.readAsArrayBuffer(this.file);
31803 parseExifData : function(dataView, offset, length)
31805 var tiffOffset = offset + 10,
31809 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31810 // No Exif data, might be XMP data instead
31814 // Check for the ASCII code for "Exif" (0x45786966):
31815 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31816 // No Exif data, might be XMP data instead
31819 if (tiffOffset + 8 > dataView.byteLength) {
31820 Roo.log('Invalid Exif data: Invalid segment size.');
31823 // Check for the two null bytes:
31824 if (dataView.getUint16(offset + 8) !== 0x0000) {
31825 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31828 // Check the byte alignment:
31829 switch (dataView.getUint16(tiffOffset)) {
31831 littleEndian = true;
31834 littleEndian = false;
31837 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31840 // Check for the TIFF tag marker (0x002A):
31841 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31842 Roo.log('Invalid Exif data: Missing TIFF marker.');
31845 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31846 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31848 this.parseExifTags(
31851 tiffOffset + dirOffset,
31856 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31861 if (dirOffset + 6 > dataView.byteLength) {
31862 Roo.log('Invalid Exif data: Invalid directory offset.');
31865 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31866 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31867 if (dirEndOffset + 4 > dataView.byteLength) {
31868 Roo.log('Invalid Exif data: Invalid directory size.');
31871 for (i = 0; i < tagsNumber; i += 1) {
31875 dirOffset + 2 + 12 * i, // tag offset
31879 // Return the offset to the next directory:
31880 return dataView.getUint32(dirEndOffset, littleEndian);
31883 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31885 var tag = dataView.getUint16(offset, littleEndian);
31887 this.exif[tag] = this.getExifValue(
31891 dataView.getUint16(offset + 2, littleEndian), // tag type
31892 dataView.getUint32(offset + 4, littleEndian), // tag length
31897 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31899 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31908 Roo.log('Invalid Exif data: Invalid tag type.');
31912 tagSize = tagType.size * length;
31913 // Determine if the value is contained in the dataOffset bytes,
31914 // or if the value at the dataOffset is a pointer to the actual data:
31915 dataOffset = tagSize > 4 ?
31916 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31917 if (dataOffset + tagSize > dataView.byteLength) {
31918 Roo.log('Invalid Exif data: Invalid data offset.');
31921 if (length === 1) {
31922 return tagType.getValue(dataView, dataOffset, littleEndian);
31925 for (i = 0; i < length; i += 1) {
31926 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31929 if (tagType.ascii) {
31931 // Concatenate the chars:
31932 for (i = 0; i < values.length; i += 1) {
31934 // Ignore the terminating NULL byte(s):
31935 if (c === '\u0000') {
31947 Roo.apply(Roo.bootstrap.UploadCropbox, {
31949 'Orientation': 0x0112
31953 1: 0, //'top-left',
31955 3: 180, //'bottom-right',
31956 // 4: 'bottom-left',
31958 6: 90, //'right-top',
31959 // 7: 'right-bottom',
31960 8: 270 //'left-bottom'
31964 // byte, 8-bit unsigned int:
31966 getValue: function (dataView, dataOffset) {
31967 return dataView.getUint8(dataOffset);
31971 // ascii, 8-bit byte:
31973 getValue: function (dataView, dataOffset) {
31974 return String.fromCharCode(dataView.getUint8(dataOffset));
31979 // short, 16 bit int:
31981 getValue: function (dataView, dataOffset, littleEndian) {
31982 return dataView.getUint16(dataOffset, littleEndian);
31986 // long, 32 bit int:
31988 getValue: function (dataView, dataOffset, littleEndian) {
31989 return dataView.getUint32(dataOffset, littleEndian);
31993 // rational = two long values, first is numerator, second is denominator:
31995 getValue: function (dataView, dataOffset, littleEndian) {
31996 return dataView.getUint32(dataOffset, littleEndian) /
31997 dataView.getUint32(dataOffset + 4, littleEndian);
32001 // slong, 32 bit signed int:
32003 getValue: function (dataView, dataOffset, littleEndian) {
32004 return dataView.getInt32(dataOffset, littleEndian);
32008 // srational, two slongs, first is numerator, second is denominator:
32010 getValue: function (dataView, dataOffset, littleEndian) {
32011 return dataView.getInt32(dataOffset, littleEndian) /
32012 dataView.getInt32(dataOffset + 4, littleEndian);
32022 cls : 'btn-group roo-upload-cropbox-rotate-left',
32023 action : 'rotate-left',
32027 cls : 'btn btn-default',
32028 html : '<i class="fa fa-undo"></i>'
32034 cls : 'btn-group roo-upload-cropbox-picture',
32035 action : 'picture',
32039 cls : 'btn btn-default',
32040 html : '<i class="fa fa-picture-o"></i>'
32046 cls : 'btn-group roo-upload-cropbox-rotate-right',
32047 action : 'rotate-right',
32051 cls : 'btn btn-default',
32052 html : '<i class="fa fa-repeat"></i>'
32060 cls : 'btn-group roo-upload-cropbox-rotate-left',
32061 action : 'rotate-left',
32065 cls : 'btn btn-default',
32066 html : '<i class="fa fa-undo"></i>'
32072 cls : 'btn-group roo-upload-cropbox-download',
32073 action : 'download',
32077 cls : 'btn btn-default',
32078 html : '<i class="fa fa-download"></i>'
32084 cls : 'btn-group roo-upload-cropbox-crop',
32089 cls : 'btn btn-default',
32090 html : '<i class="fa fa-crop"></i>'
32096 cls : 'btn-group roo-upload-cropbox-trash',
32101 cls : 'btn btn-default',
32102 html : '<i class="fa fa-trash"></i>'
32108 cls : 'btn-group roo-upload-cropbox-rotate-right',
32109 action : 'rotate-right',
32113 cls : 'btn btn-default',
32114 html : '<i class="fa fa-repeat"></i>'
32122 cls : 'btn-group roo-upload-cropbox-rotate-left',
32123 action : 'rotate-left',
32127 cls : 'btn btn-default',
32128 html : '<i class="fa fa-undo"></i>'
32134 cls : 'btn-group roo-upload-cropbox-rotate-right',
32135 action : 'rotate-right',
32139 cls : 'btn btn-default',
32140 html : '<i class="fa fa-repeat"></i>'
32153 * @class Roo.bootstrap.DocumentManager
32154 * @extends Roo.bootstrap.Component
32155 * Bootstrap DocumentManager class
32156 * @cfg {String} paramName default 'imageUpload'
32157 * @cfg {String} toolTipName default 'filename'
32158 * @cfg {String} method default POST
32159 * @cfg {String} url action url
32160 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32161 * @cfg {Boolean} multiple multiple upload default true
32162 * @cfg {Number} thumbSize default 300
32163 * @cfg {String} fieldLabel
32164 * @cfg {Number} labelWidth default 4
32165 * @cfg {String} labelAlign (left|top) default left
32166 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32167 * @cfg {Number} labellg set the width of label (1-12)
32168 * @cfg {Number} labelmd set the width of label (1-12)
32169 * @cfg {Number} labelsm set the width of label (1-12)
32170 * @cfg {Number} labelxs set the width of label (1-12)
32173 * Create a new DocumentManager
32174 * @param {Object} config The config object
32177 Roo.bootstrap.DocumentManager = function(config){
32178 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32181 this.delegates = [];
32186 * Fire when initial the DocumentManager
32187 * @param {Roo.bootstrap.DocumentManager} this
32192 * inspect selected file
32193 * @param {Roo.bootstrap.DocumentManager} this
32194 * @param {File} file
32199 * Fire when xhr load exception
32200 * @param {Roo.bootstrap.DocumentManager} this
32201 * @param {XMLHttpRequest} xhr
32203 "exception" : true,
32205 * @event afterupload
32206 * Fire when xhr load exception
32207 * @param {Roo.bootstrap.DocumentManager} this
32208 * @param {XMLHttpRequest} xhr
32210 "afterupload" : true,
32213 * prepare the form data
32214 * @param {Roo.bootstrap.DocumentManager} this
32215 * @param {Object} formData
32220 * Fire when remove the file
32221 * @param {Roo.bootstrap.DocumentManager} this
32222 * @param {Object} file
32227 * Fire after refresh the file
32228 * @param {Roo.bootstrap.DocumentManager} this
32233 * Fire after click the image
32234 * @param {Roo.bootstrap.DocumentManager} this
32235 * @param {Object} file
32240 * Fire when upload a image and editable set to true
32241 * @param {Roo.bootstrap.DocumentManager} this
32242 * @param {Object} file
32246 * @event beforeselectfile
32247 * Fire before select file
32248 * @param {Roo.bootstrap.DocumentManager} this
32250 "beforeselectfile" : true,
32253 * Fire before process file
32254 * @param {Roo.bootstrap.DocumentManager} this
32255 * @param {Object} file
32259 * @event previewrendered
32260 * Fire when preview rendered
32261 * @param {Roo.bootstrap.DocumentManager} this
32262 * @param {Object} file
32264 "previewrendered" : true,
32267 "previewResize" : true
32272 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32281 paramName : 'imageUpload',
32282 toolTipName : 'filename',
32285 labelAlign : 'left',
32295 getAutoCreate : function()
32297 var managerWidget = {
32299 cls : 'roo-document-manager',
32303 cls : 'roo-document-manager-selector',
32308 cls : 'roo-document-manager-uploader',
32312 cls : 'roo-document-manager-upload-btn',
32313 html : '<i class="fa fa-plus"></i>'
32324 cls : 'column col-md-12',
32329 if(this.fieldLabel.length){
32334 cls : 'column col-md-12',
32335 html : this.fieldLabel
32339 cls : 'column col-md-12',
32344 if(this.labelAlign == 'left'){
32349 html : this.fieldLabel
32358 if(this.labelWidth > 12){
32359 content[0].style = "width: " + this.labelWidth + 'px';
32362 if(this.labelWidth < 13 && this.labelmd == 0){
32363 this.labelmd = this.labelWidth;
32366 if(this.labellg > 0){
32367 content[0].cls += ' col-lg-' + this.labellg;
32368 content[1].cls += ' col-lg-' + (12 - this.labellg);
32371 if(this.labelmd > 0){
32372 content[0].cls += ' col-md-' + this.labelmd;
32373 content[1].cls += ' col-md-' + (12 - this.labelmd);
32376 if(this.labelsm > 0){
32377 content[0].cls += ' col-sm-' + this.labelsm;
32378 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32381 if(this.labelxs > 0){
32382 content[0].cls += ' col-xs-' + this.labelxs;
32383 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32391 cls : 'row clearfix',
32399 initEvents : function()
32401 this.managerEl = this.el.select('.roo-document-manager', true).first();
32402 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32404 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32405 this.selectorEl.hide();
32408 this.selectorEl.attr('multiple', 'multiple');
32411 this.selectorEl.on('change', this.onFileSelected, this);
32413 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32414 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32416 this.uploader.on('click', this.onUploaderClick, this);
32418 this.renderProgressDialog();
32422 window.addEventListener("resize", function() { _this.refresh(); } );
32424 this.fireEvent('initial', this);
32427 renderProgressDialog : function()
32431 this.progressDialog = new Roo.bootstrap.Modal({
32432 cls : 'roo-document-manager-progress-dialog',
32433 allow_close : false,
32444 btnclick : function() {
32445 _this.uploadCancel();
32451 this.progressDialog.render(Roo.get(document.body));
32453 this.progress = new Roo.bootstrap.Progress({
32454 cls : 'roo-document-manager-progress',
32459 this.progress.render(this.progressDialog.getChildContainer());
32461 this.progressBar = new Roo.bootstrap.ProgressBar({
32462 cls : 'roo-document-manager-progress-bar',
32465 aria_valuemax : 12,
32469 this.progressBar.render(this.progress.getChildContainer());
32472 onUploaderClick : function(e)
32474 e.preventDefault();
32476 if(this.fireEvent('beforeselectfile', this) != false){
32477 this.selectorEl.dom.click();
32482 onFileSelected : function(e)
32484 e.preventDefault();
32486 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32490 Roo.each(this.selectorEl.dom.files, function(file){
32491 if(this.fireEvent('inspect', this, file) != false){
32492 this.files.push(file);
32502 this.selectorEl.dom.value = '';
32504 if(!this.files || !this.files.length){
32508 if(this.boxes > 0 && this.files.length > this.boxes){
32509 this.files = this.files.slice(0, this.boxes);
32512 this.uploader.show();
32514 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32515 this.uploader.hide();
32524 Roo.each(this.files, function(file){
32526 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32527 var f = this.renderPreview(file);
32532 if(file.type.indexOf('image') != -1){
32533 this.delegates.push(
32535 _this.process(file);
32536 }).createDelegate(this)
32544 _this.process(file);
32545 }).createDelegate(this)
32550 this.files = files;
32552 this.delegates = this.delegates.concat(docs);
32554 if(!this.delegates.length){
32559 this.progressBar.aria_valuemax = this.delegates.length;
32566 arrange : function()
32568 if(!this.delegates.length){
32569 this.progressDialog.hide();
32574 var delegate = this.delegates.shift();
32576 this.progressDialog.show();
32578 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32580 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32585 refresh : function()
32587 this.uploader.show();
32589 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32590 this.uploader.hide();
32593 Roo.isTouch ? this.closable(false) : this.closable(true);
32595 this.fireEvent('refresh', this);
32598 onRemove : function(e, el, o)
32600 e.preventDefault();
32602 this.fireEvent('remove', this, o);
32606 remove : function(o)
32610 Roo.each(this.files, function(file){
32611 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32620 this.files = files;
32627 Roo.each(this.files, function(file){
32632 file.target.remove();
32641 onClick : function(e, el, o)
32643 e.preventDefault();
32645 this.fireEvent('click', this, o);
32649 closable : function(closable)
32651 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32653 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32665 xhrOnLoad : function(xhr)
32667 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32671 if (xhr.readyState !== 4) {
32673 this.fireEvent('exception', this, xhr);
32677 var response = Roo.decode(xhr.responseText);
32679 if(!response.success){
32681 this.fireEvent('exception', this, xhr);
32685 var file = this.renderPreview(response.data);
32687 this.files.push(file);
32691 this.fireEvent('afterupload', this, xhr);
32695 xhrOnError : function(xhr)
32697 Roo.log('xhr on error');
32699 var response = Roo.decode(xhr.responseText);
32706 process : function(file)
32708 if(this.fireEvent('process', this, file) !== false){
32709 if(this.editable && file.type.indexOf('image') != -1){
32710 this.fireEvent('edit', this, file);
32714 this.uploadStart(file, false);
32721 uploadStart : function(file, crop)
32723 this.xhr = new XMLHttpRequest();
32725 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32730 file.xhr = this.xhr;
32732 this.managerEl.createChild({
32734 cls : 'roo-document-manager-loading',
32738 tooltip : file.name,
32739 cls : 'roo-document-manager-thumb',
32740 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32746 this.xhr.open(this.method, this.url, true);
32749 "Accept": "application/json",
32750 "Cache-Control": "no-cache",
32751 "X-Requested-With": "XMLHttpRequest"
32754 for (var headerName in headers) {
32755 var headerValue = headers[headerName];
32757 this.xhr.setRequestHeader(headerName, headerValue);
32763 this.xhr.onload = function()
32765 _this.xhrOnLoad(_this.xhr);
32768 this.xhr.onerror = function()
32770 _this.xhrOnError(_this.xhr);
32773 var formData = new FormData();
32775 formData.append('returnHTML', 'NO');
32778 formData.append('crop', crop);
32781 formData.append(this.paramName, file, file.name);
32788 if(this.fireEvent('prepare', this, formData, options) != false){
32790 if(options.manually){
32794 this.xhr.send(formData);
32798 this.uploadCancel();
32801 uploadCancel : function()
32807 this.delegates = [];
32809 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32816 renderPreview : function(file)
32818 if(typeof(file.target) != 'undefined' && file.target){
32822 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32824 var previewEl = this.managerEl.createChild({
32826 cls : 'roo-document-manager-preview',
32830 tooltip : file[this.toolTipName],
32831 cls : 'roo-document-manager-thumb',
32832 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32837 html : '<i class="fa fa-times-circle"></i>'
32842 var close = previewEl.select('button.close', true).first();
32844 close.on('click', this.onRemove, this, file);
32846 file.target = previewEl;
32848 var image = previewEl.select('img', true).first();
32852 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32854 image.on('click', this.onClick, this, file);
32856 this.fireEvent('previewrendered', this, file);
32862 onPreviewLoad : function(file, image)
32864 if(typeof(file.target) == 'undefined' || !file.target){
32868 var width = image.dom.naturalWidth || image.dom.width;
32869 var height = image.dom.naturalHeight || image.dom.height;
32871 if(!this.previewResize) {
32875 if(width > height){
32876 file.target.addClass('wide');
32880 file.target.addClass('tall');
32885 uploadFromSource : function(file, crop)
32887 this.xhr = new XMLHttpRequest();
32889 this.managerEl.createChild({
32891 cls : 'roo-document-manager-loading',
32895 tooltip : file.name,
32896 cls : 'roo-document-manager-thumb',
32897 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32903 this.xhr.open(this.method, this.url, true);
32906 "Accept": "application/json",
32907 "Cache-Control": "no-cache",
32908 "X-Requested-With": "XMLHttpRequest"
32911 for (var headerName in headers) {
32912 var headerValue = headers[headerName];
32914 this.xhr.setRequestHeader(headerName, headerValue);
32920 this.xhr.onload = function()
32922 _this.xhrOnLoad(_this.xhr);
32925 this.xhr.onerror = function()
32927 _this.xhrOnError(_this.xhr);
32930 var formData = new FormData();
32932 formData.append('returnHTML', 'NO');
32934 formData.append('crop', crop);
32936 if(typeof(file.filename) != 'undefined'){
32937 formData.append('filename', file.filename);
32940 if(typeof(file.mimetype) != 'undefined'){
32941 formData.append('mimetype', file.mimetype);
32946 if(this.fireEvent('prepare', this, formData) != false){
32947 this.xhr.send(formData);
32957 * @class Roo.bootstrap.DocumentViewer
32958 * @extends Roo.bootstrap.Component
32959 * Bootstrap DocumentViewer class
32960 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32961 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32964 * Create a new DocumentViewer
32965 * @param {Object} config The config object
32968 Roo.bootstrap.DocumentViewer = function(config){
32969 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32974 * Fire after initEvent
32975 * @param {Roo.bootstrap.DocumentViewer} this
32981 * @param {Roo.bootstrap.DocumentViewer} this
32986 * Fire after download button
32987 * @param {Roo.bootstrap.DocumentViewer} this
32992 * Fire after trash button
32993 * @param {Roo.bootstrap.DocumentViewer} this
33000 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33002 showDownload : true,
33006 getAutoCreate : function()
33010 cls : 'roo-document-viewer',
33014 cls : 'roo-document-viewer-body',
33018 cls : 'roo-document-viewer-thumb',
33022 cls : 'roo-document-viewer-image'
33030 cls : 'roo-document-viewer-footer',
33033 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33037 cls : 'btn-group roo-document-viewer-download',
33041 cls : 'btn btn-default',
33042 html : '<i class="fa fa-download"></i>'
33048 cls : 'btn-group roo-document-viewer-trash',
33052 cls : 'btn btn-default',
33053 html : '<i class="fa fa-trash"></i>'
33066 initEvents : function()
33068 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33069 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33071 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33072 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33074 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33075 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33077 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33078 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33080 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33081 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33083 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33084 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33086 this.bodyEl.on('click', this.onClick, this);
33087 this.downloadBtn.on('click', this.onDownload, this);
33088 this.trashBtn.on('click', this.onTrash, this);
33090 this.downloadBtn.hide();
33091 this.trashBtn.hide();
33093 if(this.showDownload){
33094 this.downloadBtn.show();
33097 if(this.showTrash){
33098 this.trashBtn.show();
33101 if(!this.showDownload && !this.showTrash) {
33102 this.footerEl.hide();
33107 initial : function()
33109 this.fireEvent('initial', this);
33113 onClick : function(e)
33115 e.preventDefault();
33117 this.fireEvent('click', this);
33120 onDownload : function(e)
33122 e.preventDefault();
33124 this.fireEvent('download', this);
33127 onTrash : function(e)
33129 e.preventDefault();
33131 this.fireEvent('trash', this);
33143 * @class Roo.bootstrap.NavProgressBar
33144 * @extends Roo.bootstrap.Component
33145 * Bootstrap NavProgressBar class
33148 * Create a new nav progress bar
33149 * @param {Object} config The config object
33152 Roo.bootstrap.NavProgressBar = function(config){
33153 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33155 this.bullets = this.bullets || [];
33157 // Roo.bootstrap.NavProgressBar.register(this);
33161 * Fires when the active item changes
33162 * @param {Roo.bootstrap.NavProgressBar} this
33163 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33164 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33171 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33176 getAutoCreate : function()
33178 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33182 cls : 'roo-navigation-bar-group',
33186 cls : 'roo-navigation-top-bar'
33190 cls : 'roo-navigation-bullets-bar',
33194 cls : 'roo-navigation-bar'
33201 cls : 'roo-navigation-bottom-bar'
33211 initEvents: function()
33216 onRender : function(ct, position)
33218 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33220 if(this.bullets.length){
33221 Roo.each(this.bullets, function(b){
33230 addItem : function(cfg)
33232 var item = new Roo.bootstrap.NavProgressItem(cfg);
33234 item.parentId = this.id;
33235 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33238 var top = new Roo.bootstrap.Element({
33240 cls : 'roo-navigation-bar-text'
33243 var bottom = new Roo.bootstrap.Element({
33245 cls : 'roo-navigation-bar-text'
33248 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33249 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33251 var topText = new Roo.bootstrap.Element({
33253 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33256 var bottomText = new Roo.bootstrap.Element({
33258 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33261 topText.onRender(top.el, null);
33262 bottomText.onRender(bottom.el, null);
33265 item.bottomEl = bottom;
33268 this.barItems.push(item);
33273 getActive : function()
33275 var active = false;
33277 Roo.each(this.barItems, function(v){
33279 if (!v.isActive()) {
33291 setActiveItem : function(item)
33295 Roo.each(this.barItems, function(v){
33296 if (v.rid == item.rid) {
33300 if (v.isActive()) {
33301 v.setActive(false);
33306 item.setActive(true);
33308 this.fireEvent('changed', this, item, prev);
33311 getBarItem: function(rid)
33315 Roo.each(this.barItems, function(e) {
33316 if (e.rid != rid) {
33327 indexOfItem : function(item)
33331 Roo.each(this.barItems, function(v, i){
33333 if (v.rid != item.rid) {
33344 setActiveNext : function()
33346 var i = this.indexOfItem(this.getActive());
33348 if (i > this.barItems.length) {
33352 this.setActiveItem(this.barItems[i+1]);
33355 setActivePrev : function()
33357 var i = this.indexOfItem(this.getActive());
33363 this.setActiveItem(this.barItems[i-1]);
33366 format : function()
33368 if(!this.barItems.length){
33372 var width = 100 / this.barItems.length;
33374 Roo.each(this.barItems, function(i){
33375 i.el.setStyle('width', width + '%');
33376 i.topEl.el.setStyle('width', width + '%');
33377 i.bottomEl.el.setStyle('width', width + '%');
33386 * Nav Progress Item
33391 * @class Roo.bootstrap.NavProgressItem
33392 * @extends Roo.bootstrap.Component
33393 * Bootstrap NavProgressItem class
33394 * @cfg {String} rid the reference id
33395 * @cfg {Boolean} active (true|false) Is item active default false
33396 * @cfg {Boolean} disabled (true|false) Is item active default false
33397 * @cfg {String} html
33398 * @cfg {String} position (top|bottom) text position default bottom
33399 * @cfg {String} icon show icon instead of number
33402 * Create a new NavProgressItem
33403 * @param {Object} config The config object
33405 Roo.bootstrap.NavProgressItem = function(config){
33406 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33411 * The raw click event for the entire grid.
33412 * @param {Roo.bootstrap.NavProgressItem} this
33413 * @param {Roo.EventObject} e
33420 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33426 position : 'bottom',
33429 getAutoCreate : function()
33431 var iconCls = 'roo-navigation-bar-item-icon';
33433 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33437 cls: 'roo-navigation-bar-item',
33447 cfg.cls += ' active';
33450 cfg.cls += ' disabled';
33456 disable : function()
33458 this.setDisabled(true);
33461 enable : function()
33463 this.setDisabled(false);
33466 initEvents: function()
33468 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33470 this.iconEl.on('click', this.onClick, this);
33473 onClick : function(e)
33475 e.preventDefault();
33481 if(this.fireEvent('click', this, e) === false){
33485 this.parent().setActiveItem(this);
33488 isActive: function ()
33490 return this.active;
33493 setActive : function(state)
33495 if(this.active == state){
33499 this.active = state;
33502 this.el.addClass('active');
33506 this.el.removeClass('active');
33511 setDisabled : function(state)
33513 if(this.disabled == state){
33517 this.disabled = state;
33520 this.el.addClass('disabled');
33524 this.el.removeClass('disabled');
33527 tooltipEl : function()
33529 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33542 * @class Roo.bootstrap.FieldLabel
33543 * @extends Roo.bootstrap.Component
33544 * Bootstrap FieldLabel class
33545 * @cfg {String} html contents of the element
33546 * @cfg {String} tag tag of the element default label
33547 * @cfg {String} cls class of the element
33548 * @cfg {String} target label target
33549 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33550 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33551 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33552 * @cfg {String} iconTooltip default "This field is required"
33553 * @cfg {String} indicatorpos (left|right) default left
33556 * Create a new FieldLabel
33557 * @param {Object} config The config object
33560 Roo.bootstrap.FieldLabel = function(config){
33561 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33566 * Fires after the field has been marked as invalid.
33567 * @param {Roo.form.FieldLabel} this
33568 * @param {String} msg The validation message
33573 * Fires after the field has been validated with no errors.
33574 * @param {Roo.form.FieldLabel} this
33580 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33587 invalidClass : 'has-warning',
33588 validClass : 'has-success',
33589 iconTooltip : 'This field is required',
33590 indicatorpos : 'left',
33592 getAutoCreate : function(){
33595 if (!this.allowBlank) {
33601 cls : 'roo-bootstrap-field-label ' + this.cls,
33606 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33607 tooltip : this.iconTooltip
33616 if(this.indicatorpos == 'right'){
33619 cls : 'roo-bootstrap-field-label ' + this.cls,
33628 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33629 tooltip : this.iconTooltip
33638 initEvents: function()
33640 Roo.bootstrap.Element.superclass.initEvents.call(this);
33642 this.indicator = this.indicatorEl();
33644 if(this.indicator){
33645 this.indicator.removeClass('visible');
33646 this.indicator.addClass('invisible');
33649 Roo.bootstrap.FieldLabel.register(this);
33652 indicatorEl : function()
33654 var indicator = this.el.select('i.roo-required-indicator',true).first();
33665 * Mark this field as valid
33667 markValid : function()
33669 if(this.indicator){
33670 this.indicator.removeClass('visible');
33671 this.indicator.addClass('invisible');
33673 if (Roo.bootstrap.version == 3) {
33674 this.el.removeClass(this.invalidClass);
33675 this.el.addClass(this.validClass);
33677 this.el.removeClass('is-invalid');
33678 this.el.addClass('is-valid');
33682 this.fireEvent('valid', this);
33686 * Mark this field as invalid
33687 * @param {String} msg The validation message
33689 markInvalid : function(msg)
33691 if(this.indicator){
33692 this.indicator.removeClass('invisible');
33693 this.indicator.addClass('visible');
33695 if (Roo.bootstrap.version == 3) {
33696 this.el.removeClass(this.validClass);
33697 this.el.addClass(this.invalidClass);
33699 this.el.removeClass('is-valid');
33700 this.el.addClass('is-invalid');
33704 this.fireEvent('invalid', this, msg);
33710 Roo.apply(Roo.bootstrap.FieldLabel, {
33715 * register a FieldLabel Group
33716 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33718 register : function(label)
33720 if(this.groups.hasOwnProperty(label.target)){
33724 this.groups[label.target] = label;
33728 * fetch a FieldLabel Group based on the target
33729 * @param {string} target
33730 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33732 get: function(target) {
33733 if (typeof(this.groups[target]) == 'undefined') {
33737 return this.groups[target] ;
33746 * page DateSplitField.
33752 * @class Roo.bootstrap.DateSplitField
33753 * @extends Roo.bootstrap.Component
33754 * Bootstrap DateSplitField class
33755 * @cfg {string} fieldLabel - the label associated
33756 * @cfg {Number} labelWidth set the width of label (0-12)
33757 * @cfg {String} labelAlign (top|left)
33758 * @cfg {Boolean} dayAllowBlank (true|false) default false
33759 * @cfg {Boolean} monthAllowBlank (true|false) default false
33760 * @cfg {Boolean} yearAllowBlank (true|false) default false
33761 * @cfg {string} dayPlaceholder
33762 * @cfg {string} monthPlaceholder
33763 * @cfg {string} yearPlaceholder
33764 * @cfg {string} dayFormat default 'd'
33765 * @cfg {string} monthFormat default 'm'
33766 * @cfg {string} yearFormat default 'Y'
33767 * @cfg {Number} labellg set the width of label (1-12)
33768 * @cfg {Number} labelmd set the width of label (1-12)
33769 * @cfg {Number} labelsm set the width of label (1-12)
33770 * @cfg {Number} labelxs set the width of label (1-12)
33774 * Create a new DateSplitField
33775 * @param {Object} config The config object
33778 Roo.bootstrap.DateSplitField = function(config){
33779 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33785 * getting the data of years
33786 * @param {Roo.bootstrap.DateSplitField} this
33787 * @param {Object} years
33792 * getting the data of days
33793 * @param {Roo.bootstrap.DateSplitField} this
33794 * @param {Object} days
33799 * Fires after the field has been marked as invalid.
33800 * @param {Roo.form.Field} this
33801 * @param {String} msg The validation message
33806 * Fires after the field has been validated with no errors.
33807 * @param {Roo.form.Field} this
33813 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33816 labelAlign : 'top',
33818 dayAllowBlank : false,
33819 monthAllowBlank : false,
33820 yearAllowBlank : false,
33821 dayPlaceholder : '',
33822 monthPlaceholder : '',
33823 yearPlaceholder : '',
33827 isFormField : true,
33833 getAutoCreate : function()
33837 cls : 'row roo-date-split-field-group',
33842 cls : 'form-hidden-field roo-date-split-field-group-value',
33848 var labelCls = 'col-md-12';
33849 var contentCls = 'col-md-4';
33851 if(this.fieldLabel){
33855 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33859 html : this.fieldLabel
33864 if(this.labelAlign == 'left'){
33866 if(this.labelWidth > 12){
33867 label.style = "width: " + this.labelWidth + 'px';
33870 if(this.labelWidth < 13 && this.labelmd == 0){
33871 this.labelmd = this.labelWidth;
33874 if(this.labellg > 0){
33875 labelCls = ' col-lg-' + this.labellg;
33876 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33879 if(this.labelmd > 0){
33880 labelCls = ' col-md-' + this.labelmd;
33881 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33884 if(this.labelsm > 0){
33885 labelCls = ' col-sm-' + this.labelsm;
33886 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33889 if(this.labelxs > 0){
33890 labelCls = ' col-xs-' + this.labelxs;
33891 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33895 label.cls += ' ' + labelCls;
33897 cfg.cn.push(label);
33900 Roo.each(['day', 'month', 'year'], function(t){
33903 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33910 inputEl: function ()
33912 return this.el.select('.roo-date-split-field-group-value', true).first();
33915 onRender : function(ct, position)
33919 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33921 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33923 this.dayField = new Roo.bootstrap.ComboBox({
33924 allowBlank : this.dayAllowBlank,
33925 alwaysQuery : true,
33926 displayField : 'value',
33929 forceSelection : true,
33931 placeholder : this.dayPlaceholder,
33932 selectOnFocus : true,
33933 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33934 triggerAction : 'all',
33936 valueField : 'value',
33937 store : new Roo.data.SimpleStore({
33938 data : (function() {
33940 _this.fireEvent('days', _this, days);
33943 fields : [ 'value' ]
33946 select : function (_self, record, index)
33948 _this.setValue(_this.getValue());
33953 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33955 this.monthField = new Roo.bootstrap.MonthField({
33956 after : '<i class=\"fa fa-calendar\"></i>',
33957 allowBlank : this.monthAllowBlank,
33958 placeholder : this.monthPlaceholder,
33961 render : function (_self)
33963 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33964 e.preventDefault();
33968 select : function (_self, oldvalue, newvalue)
33970 _this.setValue(_this.getValue());
33975 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33977 this.yearField = new Roo.bootstrap.ComboBox({
33978 allowBlank : this.yearAllowBlank,
33979 alwaysQuery : true,
33980 displayField : 'value',
33983 forceSelection : true,
33985 placeholder : this.yearPlaceholder,
33986 selectOnFocus : true,
33987 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33988 triggerAction : 'all',
33990 valueField : 'value',
33991 store : new Roo.data.SimpleStore({
33992 data : (function() {
33994 _this.fireEvent('years', _this, years);
33997 fields : [ 'value' ]
34000 select : function (_self, record, index)
34002 _this.setValue(_this.getValue());
34007 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34010 setValue : function(v, format)
34012 this.inputEl.dom.value = v;
34014 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34016 var d = Date.parseDate(v, f);
34023 this.setDay(d.format(this.dayFormat));
34024 this.setMonth(d.format(this.monthFormat));
34025 this.setYear(d.format(this.yearFormat));
34032 setDay : function(v)
34034 this.dayField.setValue(v);
34035 this.inputEl.dom.value = this.getValue();
34040 setMonth : function(v)
34042 this.monthField.setValue(v, true);
34043 this.inputEl.dom.value = this.getValue();
34048 setYear : function(v)
34050 this.yearField.setValue(v);
34051 this.inputEl.dom.value = this.getValue();
34056 getDay : function()
34058 return this.dayField.getValue();
34061 getMonth : function()
34063 return this.monthField.getValue();
34066 getYear : function()
34068 return this.yearField.getValue();
34071 getValue : function()
34073 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34075 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34085 this.inputEl.dom.value = '';
34090 validate : function()
34092 var d = this.dayField.validate();
34093 var m = this.monthField.validate();
34094 var y = this.yearField.validate();
34099 (!this.dayAllowBlank && !d) ||
34100 (!this.monthAllowBlank && !m) ||
34101 (!this.yearAllowBlank && !y)
34106 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34115 this.markInvalid();
34120 markValid : function()
34123 var label = this.el.select('label', true).first();
34124 var icon = this.el.select('i.fa-star', true).first();
34130 this.fireEvent('valid', this);
34134 * Mark this field as invalid
34135 * @param {String} msg The validation message
34137 markInvalid : function(msg)
34140 var label = this.el.select('label', true).first();
34141 var icon = this.el.select('i.fa-star', true).first();
34143 if(label && !icon){
34144 this.el.select('.roo-date-split-field-label', true).createChild({
34146 cls : 'text-danger fa fa-lg fa-star',
34147 tooltip : 'This field is required',
34148 style : 'margin-right:5px;'
34152 this.fireEvent('invalid', this, msg);
34155 clearInvalid : function()
34157 var label = this.el.select('label', true).first();
34158 var icon = this.el.select('i.fa-star', true).first();
34164 this.fireEvent('valid', this);
34167 getName: function()
34177 * http://masonry.desandro.com
34179 * The idea is to render all the bricks based on vertical width...
34181 * The original code extends 'outlayer' - we might need to use that....
34187 * @class Roo.bootstrap.LayoutMasonry
34188 * @extends Roo.bootstrap.Component
34189 * Bootstrap Layout Masonry class
34192 * Create a new Element
34193 * @param {Object} config The config object
34196 Roo.bootstrap.LayoutMasonry = function(config){
34198 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34202 Roo.bootstrap.LayoutMasonry.register(this);
34208 * Fire after layout the items
34209 * @param {Roo.bootstrap.LayoutMasonry} this
34210 * @param {Roo.EventObject} e
34217 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34220 * @cfg {Boolean} isLayoutInstant = no animation?
34222 isLayoutInstant : false, // needed?
34225 * @cfg {Number} boxWidth width of the columns
34230 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34235 * @cfg {Number} padWidth padding below box..
34240 * @cfg {Number} gutter gutter width..
34245 * @cfg {Number} maxCols maximum number of columns
34251 * @cfg {Boolean} isAutoInitial defalut true
34253 isAutoInitial : true,
34258 * @cfg {Boolean} isHorizontal defalut false
34260 isHorizontal : false,
34262 currentSize : null,
34268 bricks: null, //CompositeElement
34272 _isLayoutInited : false,
34274 // isAlternative : false, // only use for vertical layout...
34277 * @cfg {Number} alternativePadWidth padding below box..
34279 alternativePadWidth : 50,
34281 selectedBrick : [],
34283 getAutoCreate : function(){
34285 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34289 cls: 'blog-masonary-wrapper ' + this.cls,
34291 cls : 'mas-boxes masonary'
34298 getChildContainer: function( )
34300 if (this.boxesEl) {
34301 return this.boxesEl;
34304 this.boxesEl = this.el.select('.mas-boxes').first();
34306 return this.boxesEl;
34310 initEvents : function()
34314 if(this.isAutoInitial){
34315 Roo.log('hook children rendered');
34316 this.on('childrenrendered', function() {
34317 Roo.log('children rendered');
34323 initial : function()
34325 this.selectedBrick = [];
34327 this.currentSize = this.el.getBox(true);
34329 Roo.EventManager.onWindowResize(this.resize, this);
34331 if(!this.isAutoInitial){
34339 //this.layout.defer(500,this);
34343 resize : function()
34345 var cs = this.el.getBox(true);
34348 this.currentSize.width == cs.width &&
34349 this.currentSize.x == cs.x &&
34350 this.currentSize.height == cs.height &&
34351 this.currentSize.y == cs.y
34353 Roo.log("no change in with or X or Y");
34357 this.currentSize = cs;
34363 layout : function()
34365 this._resetLayout();
34367 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34369 this.layoutItems( isInstant );
34371 this._isLayoutInited = true;
34373 this.fireEvent('layout', this);
34377 _resetLayout : function()
34379 if(this.isHorizontal){
34380 this.horizontalMeasureColumns();
34384 this.verticalMeasureColumns();
34388 verticalMeasureColumns : function()
34390 this.getContainerWidth();
34392 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34393 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34397 var boxWidth = this.boxWidth + this.padWidth;
34399 if(this.containerWidth < this.boxWidth){
34400 boxWidth = this.containerWidth
34403 var containerWidth = this.containerWidth;
34405 var cols = Math.floor(containerWidth / boxWidth);
34407 this.cols = Math.max( cols, 1 );
34409 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34411 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34413 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34415 this.colWidth = boxWidth + avail - this.padWidth;
34417 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34418 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34421 horizontalMeasureColumns : function()
34423 this.getContainerWidth();
34425 var boxWidth = this.boxWidth;
34427 if(this.containerWidth < boxWidth){
34428 boxWidth = this.containerWidth;
34431 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34433 this.el.setHeight(boxWidth);
34437 getContainerWidth : function()
34439 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34442 layoutItems : function( isInstant )
34444 Roo.log(this.bricks);
34446 var items = Roo.apply([], this.bricks);
34448 if(this.isHorizontal){
34449 this._horizontalLayoutItems( items , isInstant );
34453 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34454 // this._verticalAlternativeLayoutItems( items , isInstant );
34458 this._verticalLayoutItems( items , isInstant );
34462 _verticalLayoutItems : function ( items , isInstant)
34464 if ( !items || !items.length ) {
34469 ['xs', 'xs', 'xs', 'tall'],
34470 ['xs', 'xs', 'tall'],
34471 ['xs', 'xs', 'sm'],
34472 ['xs', 'xs', 'xs'],
34478 ['sm', 'xs', 'xs'],
34482 ['tall', 'xs', 'xs', 'xs'],
34483 ['tall', 'xs', 'xs'],
34495 Roo.each(items, function(item, k){
34497 switch (item.size) {
34498 // these layouts take up a full box,
34509 boxes.push([item]);
34532 var filterPattern = function(box, length)
34540 var pattern = box.slice(0, length);
34544 Roo.each(pattern, function(i){
34545 format.push(i.size);
34548 Roo.each(standard, function(s){
34550 if(String(s) != String(format)){
34559 if(!match && length == 1){
34564 filterPattern(box, length - 1);
34568 queue.push(pattern);
34570 box = box.slice(length, box.length);
34572 filterPattern(box, 4);
34578 Roo.each(boxes, function(box, k){
34584 if(box.length == 1){
34589 filterPattern(box, 4);
34593 this._processVerticalLayoutQueue( queue, isInstant );
34597 // _verticalAlternativeLayoutItems : function( items , isInstant )
34599 // if ( !items || !items.length ) {
34603 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34607 _horizontalLayoutItems : function ( items , isInstant)
34609 if ( !items || !items.length || items.length < 3) {
34615 var eItems = items.slice(0, 3);
34617 items = items.slice(3, items.length);
34620 ['xs', 'xs', 'xs', 'wide'],
34621 ['xs', 'xs', 'wide'],
34622 ['xs', 'xs', 'sm'],
34623 ['xs', 'xs', 'xs'],
34629 ['sm', 'xs', 'xs'],
34633 ['wide', 'xs', 'xs', 'xs'],
34634 ['wide', 'xs', 'xs'],
34647 Roo.each(items, function(item, k){
34649 switch (item.size) {
34660 boxes.push([item]);
34684 var filterPattern = function(box, length)
34692 var pattern = box.slice(0, length);
34696 Roo.each(pattern, function(i){
34697 format.push(i.size);
34700 Roo.each(standard, function(s){
34702 if(String(s) != String(format)){
34711 if(!match && length == 1){
34716 filterPattern(box, length - 1);
34720 queue.push(pattern);
34722 box = box.slice(length, box.length);
34724 filterPattern(box, 4);
34730 Roo.each(boxes, function(box, k){
34736 if(box.length == 1){
34741 filterPattern(box, 4);
34748 var pos = this.el.getBox(true);
34752 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34754 var hit_end = false;
34756 Roo.each(queue, function(box){
34760 Roo.each(box, function(b){
34762 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34772 Roo.each(box, function(b){
34774 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34777 mx = Math.max(mx, b.x);
34781 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34785 Roo.each(box, function(b){
34787 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34801 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34804 /** Sets position of item in DOM
34805 * @param {Element} item
34806 * @param {Number} x - horizontal position
34807 * @param {Number} y - vertical position
34808 * @param {Boolean} isInstant - disables transitions
34810 _processVerticalLayoutQueue : function( queue, isInstant )
34812 var pos = this.el.getBox(true);
34817 for (var i = 0; i < this.cols; i++){
34821 Roo.each(queue, function(box, k){
34823 var col = k % this.cols;
34825 Roo.each(box, function(b,kk){
34827 b.el.position('absolute');
34829 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34830 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34832 if(b.size == 'md-left' || b.size == 'md-right'){
34833 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34834 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34837 b.el.setWidth(width);
34838 b.el.setHeight(height);
34840 b.el.select('iframe',true).setSize(width,height);
34844 for (var i = 0; i < this.cols; i++){
34846 if(maxY[i] < maxY[col]){
34851 col = Math.min(col, i);
34855 x = pos.x + col * (this.colWidth + this.padWidth);
34859 var positions = [];
34861 switch (box.length){
34863 positions = this.getVerticalOneBoxColPositions(x, y, box);
34866 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34869 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34872 positions = this.getVerticalFourBoxColPositions(x, y, box);
34878 Roo.each(box, function(b,kk){
34880 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34882 var sz = b.el.getSize();
34884 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34892 for (var i = 0; i < this.cols; i++){
34893 mY = Math.max(mY, maxY[i]);
34896 this.el.setHeight(mY - pos.y);
34900 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34902 // var pos = this.el.getBox(true);
34905 // var maxX = pos.right;
34907 // var maxHeight = 0;
34909 // Roo.each(items, function(item, k){
34913 // item.el.position('absolute');
34915 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34917 // item.el.setWidth(width);
34919 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34921 // item.el.setHeight(height);
34924 // item.el.setXY([x, y], isInstant ? false : true);
34926 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34929 // y = y + height + this.alternativePadWidth;
34931 // maxHeight = maxHeight + height + this.alternativePadWidth;
34935 // this.el.setHeight(maxHeight);
34939 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34941 var pos = this.el.getBox(true);
34946 var maxX = pos.right;
34948 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34950 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34952 Roo.each(queue, function(box, k){
34954 Roo.each(box, function(b, kk){
34956 b.el.position('absolute');
34958 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34959 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34961 if(b.size == 'md-left' || b.size == 'md-right'){
34962 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34963 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34966 b.el.setWidth(width);
34967 b.el.setHeight(height);
34975 var positions = [];
34977 switch (box.length){
34979 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34982 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34985 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34988 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34994 Roo.each(box, function(b,kk){
34996 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34998 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35006 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35008 Roo.each(eItems, function(b,k){
35010 b.size = (k == 0) ? 'sm' : 'xs';
35011 b.x = (k == 0) ? 2 : 1;
35012 b.y = (k == 0) ? 2 : 1;
35014 b.el.position('absolute');
35016 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35018 b.el.setWidth(width);
35020 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35022 b.el.setHeight(height);
35026 var positions = [];
35029 x : maxX - this.unitWidth * 2 - this.gutter,
35034 x : maxX - this.unitWidth,
35035 y : minY + (this.unitWidth + this.gutter) * 2
35039 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35043 Roo.each(eItems, function(b,k){
35045 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35051 getVerticalOneBoxColPositions : function(x, y, box)
35055 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35057 if(box[0].size == 'md-left'){
35061 if(box[0].size == 'md-right'){
35066 x : x + (this.unitWidth + this.gutter) * rand,
35073 getVerticalTwoBoxColPositions : function(x, y, box)
35077 if(box[0].size == 'xs'){
35081 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35085 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35099 x : x + (this.unitWidth + this.gutter) * 2,
35100 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35107 getVerticalThreeBoxColPositions : function(x, y, box)
35111 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35119 x : x + (this.unitWidth + this.gutter) * 1,
35124 x : x + (this.unitWidth + this.gutter) * 2,
35132 if(box[0].size == 'xs' && box[1].size == 'xs'){
35141 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35145 x : x + (this.unitWidth + this.gutter) * 1,
35159 x : x + (this.unitWidth + this.gutter) * 2,
35164 x : x + (this.unitWidth + this.gutter) * 2,
35165 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35172 getVerticalFourBoxColPositions : function(x, y, box)
35176 if(box[0].size == 'xs'){
35185 y : y + (this.unitHeight + this.gutter) * 1
35190 y : y + (this.unitHeight + this.gutter) * 2
35194 x : x + (this.unitWidth + this.gutter) * 1,
35208 x : x + (this.unitWidth + this.gutter) * 2,
35213 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35214 y : y + (this.unitHeight + this.gutter) * 1
35218 x : x + (this.unitWidth + this.gutter) * 2,
35219 y : y + (this.unitWidth + this.gutter) * 2
35226 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35230 if(box[0].size == 'md-left'){
35232 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35239 if(box[0].size == 'md-right'){
35241 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35242 y : minY + (this.unitWidth + this.gutter) * 1
35248 var rand = Math.floor(Math.random() * (4 - box[0].y));
35251 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35252 y : minY + (this.unitWidth + this.gutter) * rand
35259 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35263 if(box[0].size == 'xs'){
35266 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35271 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35272 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35280 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35285 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35286 y : minY + (this.unitWidth + this.gutter) * 2
35293 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35297 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35300 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35305 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35306 y : minY + (this.unitWidth + this.gutter) * 1
35310 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35311 y : minY + (this.unitWidth + this.gutter) * 2
35318 if(box[0].size == 'xs' && box[1].size == 'xs'){
35321 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35326 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35331 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35332 y : minY + (this.unitWidth + this.gutter) * 1
35340 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35345 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35346 y : minY + (this.unitWidth + this.gutter) * 2
35350 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35351 y : minY + (this.unitWidth + this.gutter) * 2
35358 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35362 if(box[0].size == 'xs'){
35365 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35375 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),
35380 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35381 y : minY + (this.unitWidth + this.gutter) * 1
35389 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35394 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35395 y : minY + (this.unitWidth + this.gutter) * 2
35399 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35400 y : minY + (this.unitWidth + this.gutter) * 2
35404 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),
35405 y : minY + (this.unitWidth + this.gutter) * 2
35413 * remove a Masonry Brick
35414 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35416 removeBrick : function(brick_id)
35422 for (var i = 0; i<this.bricks.length; i++) {
35423 if (this.bricks[i].id == brick_id) {
35424 this.bricks.splice(i,1);
35425 this.el.dom.removeChild(Roo.get(brick_id).dom);
35432 * adds a Masonry Brick
35433 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35435 addBrick : function(cfg)
35437 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35438 //this.register(cn);
35439 cn.parentId = this.id;
35440 cn.render(this.el);
35445 * register a Masonry Brick
35446 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35449 register : function(brick)
35451 this.bricks.push(brick);
35452 brick.masonryId = this.id;
35456 * clear all the Masonry Brick
35458 clearAll : function()
35461 //this.getChildContainer().dom.innerHTML = "";
35462 this.el.dom.innerHTML = '';
35465 getSelected : function()
35467 if (!this.selectedBrick) {
35471 return this.selectedBrick;
35475 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35479 * register a Masonry Layout
35480 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35483 register : function(layout)
35485 this.groups[layout.id] = layout;
35488 * fetch a Masonry Layout based on the masonry layout ID
35489 * @param {string} the masonry layout to add
35490 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35493 get: function(layout_id) {
35494 if (typeof(this.groups[layout_id]) == 'undefined') {
35497 return this.groups[layout_id] ;
35509 * http://masonry.desandro.com
35511 * The idea is to render all the bricks based on vertical width...
35513 * The original code extends 'outlayer' - we might need to use that....
35519 * @class Roo.bootstrap.LayoutMasonryAuto
35520 * @extends Roo.bootstrap.Component
35521 * Bootstrap Layout Masonry class
35524 * Create a new Element
35525 * @param {Object} config The config object
35528 Roo.bootstrap.LayoutMasonryAuto = function(config){
35529 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35532 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35535 * @cfg {Boolean} isFitWidth - resize the width..
35537 isFitWidth : false, // options..
35539 * @cfg {Boolean} isOriginLeft = left align?
35541 isOriginLeft : true,
35543 * @cfg {Boolean} isOriginTop = top align?
35545 isOriginTop : false,
35547 * @cfg {Boolean} isLayoutInstant = no animation?
35549 isLayoutInstant : false, // needed?
35551 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35553 isResizingContainer : true,
35555 * @cfg {Number} columnWidth width of the columns
35561 * @cfg {Number} maxCols maximum number of columns
35566 * @cfg {Number} padHeight padding below box..
35572 * @cfg {Boolean} isAutoInitial defalut true
35575 isAutoInitial : true,
35581 initialColumnWidth : 0,
35582 currentSize : null,
35584 colYs : null, // array.
35591 bricks: null, //CompositeElement
35592 cols : 0, // array?
35593 // element : null, // wrapped now this.el
35594 _isLayoutInited : null,
35597 getAutoCreate : function(){
35601 cls: 'blog-masonary-wrapper ' + this.cls,
35603 cls : 'mas-boxes masonary'
35610 getChildContainer: function( )
35612 if (this.boxesEl) {
35613 return this.boxesEl;
35616 this.boxesEl = this.el.select('.mas-boxes').first();
35618 return this.boxesEl;
35622 initEvents : function()
35626 if(this.isAutoInitial){
35627 Roo.log('hook children rendered');
35628 this.on('childrenrendered', function() {
35629 Roo.log('children rendered');
35636 initial : function()
35638 this.reloadItems();
35640 this.currentSize = this.el.getBox(true);
35642 /// was window resize... - let's see if this works..
35643 Roo.EventManager.onWindowResize(this.resize, this);
35645 if(!this.isAutoInitial){
35650 this.layout.defer(500,this);
35653 reloadItems: function()
35655 this.bricks = this.el.select('.masonry-brick', true);
35657 this.bricks.each(function(b) {
35658 //Roo.log(b.getSize());
35659 if (!b.attr('originalwidth')) {
35660 b.attr('originalwidth', b.getSize().width);
35665 Roo.log(this.bricks.elements.length);
35668 resize : function()
35671 var cs = this.el.getBox(true);
35673 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35674 Roo.log("no change in with or X");
35677 this.currentSize = cs;
35681 layout : function()
35684 this._resetLayout();
35685 //this._manageStamps();
35687 // don't animate first layout
35688 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35689 this.layoutItems( isInstant );
35691 // flag for initalized
35692 this._isLayoutInited = true;
35695 layoutItems : function( isInstant )
35697 //var items = this._getItemsForLayout( this.items );
35698 // original code supports filtering layout items.. we just ignore it..
35700 this._layoutItems( this.bricks , isInstant );
35702 this._postLayout();
35704 _layoutItems : function ( items , isInstant)
35706 //this.fireEvent( 'layout', this, items );
35709 if ( !items || !items.elements.length ) {
35710 // no items, emit event with empty array
35715 items.each(function(item) {
35716 Roo.log("layout item");
35718 // get x/y object from method
35719 var position = this._getItemLayoutPosition( item );
35721 position.item = item;
35722 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35723 queue.push( position );
35726 this._processLayoutQueue( queue );
35728 /** Sets position of item in DOM
35729 * @param {Element} item
35730 * @param {Number} x - horizontal position
35731 * @param {Number} y - vertical position
35732 * @param {Boolean} isInstant - disables transitions
35734 _processLayoutQueue : function( queue )
35736 for ( var i=0, len = queue.length; i < len; i++ ) {
35737 var obj = queue[i];
35738 obj.item.position('absolute');
35739 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35745 * Any logic you want to do after each layout,
35746 * i.e. size the container
35748 _postLayout : function()
35750 this.resizeContainer();
35753 resizeContainer : function()
35755 if ( !this.isResizingContainer ) {
35758 var size = this._getContainerSize();
35760 this.el.setSize(size.width,size.height);
35761 this.boxesEl.setSize(size.width,size.height);
35767 _resetLayout : function()
35769 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35770 this.colWidth = this.el.getWidth();
35771 //this.gutter = this.el.getWidth();
35773 this.measureColumns();
35779 this.colYs.push( 0 );
35785 measureColumns : function()
35787 this.getContainerWidth();
35788 // if columnWidth is 0, default to outerWidth of first item
35789 if ( !this.columnWidth ) {
35790 var firstItem = this.bricks.first();
35791 Roo.log(firstItem);
35792 this.columnWidth = this.containerWidth;
35793 if (firstItem && firstItem.attr('originalwidth') ) {
35794 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35796 // columnWidth fall back to item of first element
35797 Roo.log("set column width?");
35798 this.initialColumnWidth = this.columnWidth ;
35800 // if first elem has no width, default to size of container
35805 if (this.initialColumnWidth) {
35806 this.columnWidth = this.initialColumnWidth;
35811 // column width is fixed at the top - however if container width get's smaller we should
35814 // this bit calcs how man columns..
35816 var columnWidth = this.columnWidth += this.gutter;
35818 // calculate columns
35819 var containerWidth = this.containerWidth + this.gutter;
35821 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35822 // fix rounding errors, typically with gutters
35823 var excess = columnWidth - containerWidth % columnWidth;
35826 // if overshoot is less than a pixel, round up, otherwise floor it
35827 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35828 cols = Math[ mathMethod ]( cols );
35829 this.cols = Math.max( cols, 1 );
35830 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35832 // padding positioning..
35833 var totalColWidth = this.cols * this.columnWidth;
35834 var padavail = this.containerWidth - totalColWidth;
35835 // so for 2 columns - we need 3 'pads'
35837 var padNeeded = (1+this.cols) * this.padWidth;
35839 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35841 this.columnWidth += padExtra
35842 //this.padWidth = Math.floor(padavail / ( this.cols));
35844 // adjust colum width so that padding is fixed??
35846 // we have 3 columns ... total = width * 3
35847 // we have X left over... that should be used by
35849 //if (this.expandC) {
35857 getContainerWidth : function()
35859 /* // container is parent if fit width
35860 var container = this.isFitWidth ? this.element.parentNode : this.element;
35861 // check that this.size and size are there
35862 // IE8 triggers resize on body size change, so they might not be
35864 var size = getSize( container ); //FIXME
35865 this.containerWidth = size && size.innerWidth; //FIXME
35868 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35872 _getItemLayoutPosition : function( item ) // what is item?
35874 // we resize the item to our columnWidth..
35876 item.setWidth(this.columnWidth);
35877 item.autoBoxAdjust = false;
35879 var sz = item.getSize();
35881 // how many columns does this brick span
35882 var remainder = this.containerWidth % this.columnWidth;
35884 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35885 // round if off by 1 pixel, otherwise use ceil
35886 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35887 colSpan = Math.min( colSpan, this.cols );
35889 // normally this should be '1' as we dont' currently allow multi width columns..
35891 var colGroup = this._getColGroup( colSpan );
35892 // get the minimum Y value from the columns
35893 var minimumY = Math.min.apply( Math, colGroup );
35894 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35896 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35898 // position the brick
35900 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35901 y: this.currentSize.y + minimumY + this.padHeight
35905 // apply setHeight to necessary columns
35906 var setHeight = minimumY + sz.height + this.padHeight;
35907 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35909 var setSpan = this.cols + 1 - colGroup.length;
35910 for ( var i = 0; i < setSpan; i++ ) {
35911 this.colYs[ shortColIndex + i ] = setHeight ;
35918 * @param {Number} colSpan - number of columns the element spans
35919 * @returns {Array} colGroup
35921 _getColGroup : function( colSpan )
35923 if ( colSpan < 2 ) {
35924 // if brick spans only one column, use all the column Ys
35929 // how many different places could this brick fit horizontally
35930 var groupCount = this.cols + 1 - colSpan;
35931 // for each group potential horizontal position
35932 for ( var i = 0; i < groupCount; i++ ) {
35933 // make an array of colY values for that one group
35934 var groupColYs = this.colYs.slice( i, i + colSpan );
35935 // and get the max value of the array
35936 colGroup[i] = Math.max.apply( Math, groupColYs );
35941 _manageStamp : function( stamp )
35943 var stampSize = stamp.getSize();
35944 var offset = stamp.getBox();
35945 // get the columns that this stamp affects
35946 var firstX = this.isOriginLeft ? offset.x : offset.right;
35947 var lastX = firstX + stampSize.width;
35948 var firstCol = Math.floor( firstX / this.columnWidth );
35949 firstCol = Math.max( 0, firstCol );
35951 var lastCol = Math.floor( lastX / this.columnWidth );
35952 // lastCol should not go over if multiple of columnWidth #425
35953 lastCol -= lastX % this.columnWidth ? 0 : 1;
35954 lastCol = Math.min( this.cols - 1, lastCol );
35956 // set colYs to bottom of the stamp
35957 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35960 for ( var i = firstCol; i <= lastCol; i++ ) {
35961 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35966 _getContainerSize : function()
35968 this.maxY = Math.max.apply( Math, this.colYs );
35973 if ( this.isFitWidth ) {
35974 size.width = this._getContainerFitWidth();
35980 _getContainerFitWidth : function()
35982 var unusedCols = 0;
35983 // count unused columns
35986 if ( this.colYs[i] !== 0 ) {
35991 // fit container to columns that have been used
35992 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35995 needsResizeLayout : function()
35997 var previousWidth = this.containerWidth;
35998 this.getContainerWidth();
35999 return previousWidth !== this.containerWidth;
36014 * @class Roo.bootstrap.MasonryBrick
36015 * @extends Roo.bootstrap.Component
36016 * Bootstrap MasonryBrick class
36019 * Create a new MasonryBrick
36020 * @param {Object} config The config object
36023 Roo.bootstrap.MasonryBrick = function(config){
36025 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36027 Roo.bootstrap.MasonryBrick.register(this);
36033 * When a MasonryBrick is clcik
36034 * @param {Roo.bootstrap.MasonryBrick} this
36035 * @param {Roo.EventObject} e
36041 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36044 * @cfg {String} title
36048 * @cfg {String} html
36052 * @cfg {String} bgimage
36056 * @cfg {String} videourl
36060 * @cfg {String} cls
36064 * @cfg {String} href
36068 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36073 * @cfg {String} placetitle (center|bottom)
36078 * @cfg {Boolean} isFitContainer defalut true
36080 isFitContainer : true,
36083 * @cfg {Boolean} preventDefault defalut false
36085 preventDefault : false,
36088 * @cfg {Boolean} inverse defalut false
36090 maskInverse : false,
36092 getAutoCreate : function()
36094 if(!this.isFitContainer){
36095 return this.getSplitAutoCreate();
36098 var cls = 'masonry-brick masonry-brick-full';
36100 if(this.href.length){
36101 cls += ' masonry-brick-link';
36104 if(this.bgimage.length){
36105 cls += ' masonry-brick-image';
36108 if(this.maskInverse){
36109 cls += ' mask-inverse';
36112 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36113 cls += ' enable-mask';
36117 cls += ' masonry-' + this.size + '-brick';
36120 if(this.placetitle.length){
36122 switch (this.placetitle) {
36124 cls += ' masonry-center-title';
36127 cls += ' masonry-bottom-title';
36134 if(!this.html.length && !this.bgimage.length){
36135 cls += ' masonry-center-title';
36138 if(!this.html.length && this.bgimage.length){
36139 cls += ' masonry-bottom-title';
36144 cls += ' ' + this.cls;
36148 tag: (this.href.length) ? 'a' : 'div',
36153 cls: 'masonry-brick-mask'
36157 cls: 'masonry-brick-paragraph',
36163 if(this.href.length){
36164 cfg.href = this.href;
36167 var cn = cfg.cn[1].cn;
36169 if(this.title.length){
36172 cls: 'masonry-brick-title',
36177 if(this.html.length){
36180 cls: 'masonry-brick-text',
36185 if (!this.title.length && !this.html.length) {
36186 cfg.cn[1].cls += ' hide';
36189 if(this.bgimage.length){
36192 cls: 'masonry-brick-image-view',
36197 if(this.videourl.length){
36198 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36199 // youtube support only?
36202 cls: 'masonry-brick-image-view',
36205 allowfullscreen : true
36213 getSplitAutoCreate : function()
36215 var cls = 'masonry-brick masonry-brick-split';
36217 if(this.href.length){
36218 cls += ' masonry-brick-link';
36221 if(this.bgimage.length){
36222 cls += ' masonry-brick-image';
36226 cls += ' masonry-' + this.size + '-brick';
36229 switch (this.placetitle) {
36231 cls += ' masonry-center-title';
36234 cls += ' masonry-bottom-title';
36237 if(!this.bgimage.length){
36238 cls += ' masonry-center-title';
36241 if(this.bgimage.length){
36242 cls += ' masonry-bottom-title';
36248 cls += ' ' + this.cls;
36252 tag: (this.href.length) ? 'a' : 'div',
36257 cls: 'masonry-brick-split-head',
36261 cls: 'masonry-brick-paragraph',
36268 cls: 'masonry-brick-split-body',
36274 if(this.href.length){
36275 cfg.href = this.href;
36278 if(this.title.length){
36279 cfg.cn[0].cn[0].cn.push({
36281 cls: 'masonry-brick-title',
36286 if(this.html.length){
36287 cfg.cn[1].cn.push({
36289 cls: 'masonry-brick-text',
36294 if(this.bgimage.length){
36295 cfg.cn[0].cn.push({
36297 cls: 'masonry-brick-image-view',
36302 if(this.videourl.length){
36303 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36304 // youtube support only?
36305 cfg.cn[0].cn.cn.push({
36307 cls: 'masonry-brick-image-view',
36310 allowfullscreen : true
36317 initEvents: function()
36319 switch (this.size) {
36352 this.el.on('touchstart', this.onTouchStart, this);
36353 this.el.on('touchmove', this.onTouchMove, this);
36354 this.el.on('touchend', this.onTouchEnd, this);
36355 this.el.on('contextmenu', this.onContextMenu, this);
36357 this.el.on('mouseenter' ,this.enter, this);
36358 this.el.on('mouseleave', this.leave, this);
36359 this.el.on('click', this.onClick, this);
36362 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36363 this.parent().bricks.push(this);
36368 onClick: function(e, el)
36370 var time = this.endTimer - this.startTimer;
36371 // Roo.log(e.preventDefault());
36374 e.preventDefault();
36379 if(!this.preventDefault){
36383 e.preventDefault();
36385 if (this.activeClass != '') {
36386 this.selectBrick();
36389 this.fireEvent('click', this, e);
36392 enter: function(e, el)
36394 e.preventDefault();
36396 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36400 if(this.bgimage.length && this.html.length){
36401 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36405 leave: function(e, el)
36407 e.preventDefault();
36409 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36413 if(this.bgimage.length && this.html.length){
36414 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36418 onTouchStart: function(e, el)
36420 // e.preventDefault();
36422 this.touchmoved = false;
36424 if(!this.isFitContainer){
36428 if(!this.bgimage.length || !this.html.length){
36432 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36434 this.timer = new Date().getTime();
36438 onTouchMove: function(e, el)
36440 this.touchmoved = true;
36443 onContextMenu : function(e,el)
36445 e.preventDefault();
36446 e.stopPropagation();
36450 onTouchEnd: function(e, el)
36452 // e.preventDefault();
36454 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36461 if(!this.bgimage.length || !this.html.length){
36463 if(this.href.length){
36464 window.location.href = this.href;
36470 if(!this.isFitContainer){
36474 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36476 window.location.href = this.href;
36479 //selection on single brick only
36480 selectBrick : function() {
36482 if (!this.parentId) {
36486 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36487 var index = m.selectedBrick.indexOf(this.id);
36490 m.selectedBrick.splice(index,1);
36491 this.el.removeClass(this.activeClass);
36495 for(var i = 0; i < m.selectedBrick.length; i++) {
36496 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36497 b.el.removeClass(b.activeClass);
36500 m.selectedBrick = [];
36502 m.selectedBrick.push(this.id);
36503 this.el.addClass(this.activeClass);
36507 isSelected : function(){
36508 return this.el.hasClass(this.activeClass);
36513 Roo.apply(Roo.bootstrap.MasonryBrick, {
36516 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36518 * register a Masonry Brick
36519 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36522 register : function(brick)
36524 //this.groups[brick.id] = brick;
36525 this.groups.add(brick.id, brick);
36528 * fetch a masonry brick based on the masonry brick ID
36529 * @param {string} the masonry brick to add
36530 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36533 get: function(brick_id)
36535 // if (typeof(this.groups[brick_id]) == 'undefined') {
36538 // return this.groups[brick_id] ;
36540 if(this.groups.key(brick_id)) {
36541 return this.groups.key(brick_id);
36559 * @class Roo.bootstrap.Brick
36560 * @extends Roo.bootstrap.Component
36561 * Bootstrap Brick class
36564 * Create a new Brick
36565 * @param {Object} config The config object
36568 Roo.bootstrap.Brick = function(config){
36569 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36575 * When a Brick is click
36576 * @param {Roo.bootstrap.Brick} this
36577 * @param {Roo.EventObject} e
36583 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36586 * @cfg {String} title
36590 * @cfg {String} html
36594 * @cfg {String} bgimage
36598 * @cfg {String} cls
36602 * @cfg {String} href
36606 * @cfg {String} video
36610 * @cfg {Boolean} square
36614 getAutoCreate : function()
36616 var cls = 'roo-brick';
36618 if(this.href.length){
36619 cls += ' roo-brick-link';
36622 if(this.bgimage.length){
36623 cls += ' roo-brick-image';
36626 if(!this.html.length && !this.bgimage.length){
36627 cls += ' roo-brick-center-title';
36630 if(!this.html.length && this.bgimage.length){
36631 cls += ' roo-brick-bottom-title';
36635 cls += ' ' + this.cls;
36639 tag: (this.href.length) ? 'a' : 'div',
36644 cls: 'roo-brick-paragraph',
36650 if(this.href.length){
36651 cfg.href = this.href;
36654 var cn = cfg.cn[0].cn;
36656 if(this.title.length){
36659 cls: 'roo-brick-title',
36664 if(this.html.length){
36667 cls: 'roo-brick-text',
36674 if(this.bgimage.length){
36677 cls: 'roo-brick-image-view',
36685 initEvents: function()
36687 if(this.title.length || this.html.length){
36688 this.el.on('mouseenter' ,this.enter, this);
36689 this.el.on('mouseleave', this.leave, this);
36692 Roo.EventManager.onWindowResize(this.resize, this);
36694 if(this.bgimage.length){
36695 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36696 this.imageEl.on('load', this.onImageLoad, this);
36703 onImageLoad : function()
36708 resize : function()
36710 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36712 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36714 if(this.bgimage.length){
36715 var image = this.el.select('.roo-brick-image-view', true).first();
36717 image.setWidth(paragraph.getWidth());
36720 image.setHeight(paragraph.getWidth());
36723 this.el.setHeight(image.getHeight());
36724 paragraph.setHeight(image.getHeight());
36730 enter: function(e, el)
36732 e.preventDefault();
36734 if(this.bgimage.length){
36735 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36736 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36740 leave: function(e, el)
36742 e.preventDefault();
36744 if(this.bgimage.length){
36745 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36746 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36761 * @class Roo.bootstrap.NumberField
36762 * @extends Roo.bootstrap.Input
36763 * Bootstrap NumberField class
36769 * Create a new NumberField
36770 * @param {Object} config The config object
36773 Roo.bootstrap.NumberField = function(config){
36774 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36777 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36780 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36782 allowDecimals : true,
36784 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36786 decimalSeparator : ".",
36788 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36790 decimalPrecision : 2,
36792 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36794 allowNegative : true,
36797 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36801 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36803 minValue : Number.NEGATIVE_INFINITY,
36805 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36807 maxValue : Number.MAX_VALUE,
36809 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36811 minText : "The minimum value for this field is {0}",
36813 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36815 maxText : "The maximum value for this field is {0}",
36817 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36818 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36820 nanText : "{0} is not a valid number",
36822 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36824 thousandsDelimiter : false,
36826 * @cfg {String} valueAlign alignment of value
36828 valueAlign : "left",
36830 getAutoCreate : function()
36832 var hiddenInput = {
36836 cls: 'hidden-number-input'
36840 hiddenInput.name = this.name;
36845 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36847 this.name = hiddenInput.name;
36849 if(cfg.cn.length > 0) {
36850 cfg.cn.push(hiddenInput);
36857 initEvents : function()
36859 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36861 var allowed = "0123456789";
36863 if(this.allowDecimals){
36864 allowed += this.decimalSeparator;
36867 if(this.allowNegative){
36871 if(this.thousandsDelimiter) {
36875 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36877 var keyPress = function(e){
36879 var k = e.getKey();
36881 var c = e.getCharCode();
36884 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36885 allowed.indexOf(String.fromCharCode(c)) === -1
36891 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36895 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36900 this.el.on("keypress", keyPress, this);
36903 validateValue : function(value)
36906 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36910 var num = this.parseValue(value);
36913 this.markInvalid(String.format(this.nanText, value));
36917 if(num < this.minValue){
36918 this.markInvalid(String.format(this.minText, this.minValue));
36922 if(num > this.maxValue){
36923 this.markInvalid(String.format(this.maxText, this.maxValue));
36930 getValue : function()
36932 var v = this.hiddenEl().getValue();
36934 return this.fixPrecision(this.parseValue(v));
36937 parseValue : function(value)
36939 if(this.thousandsDelimiter) {
36941 r = new RegExp(",", "g");
36942 value = value.replace(r, "");
36945 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36946 return isNaN(value) ? '' : value;
36949 fixPrecision : function(value)
36951 if(this.thousandsDelimiter) {
36953 r = new RegExp(",", "g");
36954 value = value.replace(r, "");
36957 var nan = isNaN(value);
36959 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36960 return nan ? '' : value;
36962 return parseFloat(value).toFixed(this.decimalPrecision);
36965 setValue : function(v)
36967 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36973 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36975 this.inputEl().dom.value = (v == '') ? '' :
36976 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36978 if(!this.allowZero && v === '0') {
36979 this.hiddenEl().dom.value = '';
36980 this.inputEl().dom.value = '';
36987 decimalPrecisionFcn : function(v)
36989 return Math.floor(v);
36992 beforeBlur : function()
36994 var v = this.parseValue(this.getRawValue());
36996 if(v || v === 0 || v === ''){
37001 hiddenEl : function()
37003 return this.el.select('input.hidden-number-input',true).first();
37015 * @class Roo.bootstrap.DocumentSlider
37016 * @extends Roo.bootstrap.Component
37017 * Bootstrap DocumentSlider class
37020 * Create a new DocumentViewer
37021 * @param {Object} config The config object
37024 Roo.bootstrap.DocumentSlider = function(config){
37025 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37032 * Fire after initEvent
37033 * @param {Roo.bootstrap.DocumentSlider} this
37038 * Fire after update
37039 * @param {Roo.bootstrap.DocumentSlider} this
37045 * @param {Roo.bootstrap.DocumentSlider} this
37051 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37057 getAutoCreate : function()
37061 cls : 'roo-document-slider',
37065 cls : 'roo-document-slider-header',
37069 cls : 'roo-document-slider-header-title'
37075 cls : 'roo-document-slider-body',
37079 cls : 'roo-document-slider-prev',
37083 cls : 'fa fa-chevron-left'
37089 cls : 'roo-document-slider-thumb',
37093 cls : 'roo-document-slider-image'
37099 cls : 'roo-document-slider-next',
37103 cls : 'fa fa-chevron-right'
37115 initEvents : function()
37117 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37118 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37120 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37121 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37123 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37124 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37126 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37127 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37129 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37130 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37132 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37133 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37135 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37136 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37138 this.thumbEl.on('click', this.onClick, this);
37140 this.prevIndicator.on('click', this.prev, this);
37142 this.nextIndicator.on('click', this.next, this);
37146 initial : function()
37148 if(this.files.length){
37149 this.indicator = 1;
37153 this.fireEvent('initial', this);
37156 update : function()
37158 this.imageEl.attr('src', this.files[this.indicator - 1]);
37160 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37162 this.prevIndicator.show();
37164 if(this.indicator == 1){
37165 this.prevIndicator.hide();
37168 this.nextIndicator.show();
37170 if(this.indicator == this.files.length){
37171 this.nextIndicator.hide();
37174 this.thumbEl.scrollTo('top');
37176 this.fireEvent('update', this);
37179 onClick : function(e)
37181 e.preventDefault();
37183 this.fireEvent('click', this);
37188 e.preventDefault();
37190 this.indicator = Math.max(1, this.indicator - 1);
37197 e.preventDefault();
37199 this.indicator = Math.min(this.files.length, this.indicator + 1);
37213 * @class Roo.bootstrap.RadioSet
37214 * @extends Roo.bootstrap.Input
37215 * Bootstrap RadioSet class
37216 * @cfg {String} indicatorpos (left|right) default left
37217 * @cfg {Boolean} inline (true|false) inline the element (default true)
37218 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37220 * Create a new RadioSet
37221 * @param {Object} config The config object
37224 Roo.bootstrap.RadioSet = function(config){
37226 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37230 Roo.bootstrap.RadioSet.register(this);
37235 * Fires when the element is checked or unchecked.
37236 * @param {Roo.bootstrap.RadioSet} this This radio
37237 * @param {Roo.bootstrap.Radio} item The checked item
37242 * Fires when the element is click.
37243 * @param {Roo.bootstrap.RadioSet} this This radio set
37244 * @param {Roo.bootstrap.Radio} item The checked item
37245 * @param {Roo.EventObject} e The event object
37252 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37260 indicatorpos : 'left',
37262 getAutoCreate : function()
37266 cls : 'roo-radio-set-label',
37270 html : this.fieldLabel
37274 if (Roo.bootstrap.version == 3) {
37277 if(this.indicatorpos == 'left'){
37280 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37281 tooltip : 'This field is required'
37286 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37287 tooltip : 'This field is required'
37293 cls : 'roo-radio-set-items'
37296 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37298 if (align === 'left' && this.fieldLabel.length) {
37301 cls : "roo-radio-set-right",
37307 if(this.labelWidth > 12){
37308 label.style = "width: " + this.labelWidth + 'px';
37311 if(this.labelWidth < 13 && this.labelmd == 0){
37312 this.labelmd = this.labelWidth;
37315 if(this.labellg > 0){
37316 label.cls += ' col-lg-' + this.labellg;
37317 items.cls += ' col-lg-' + (12 - this.labellg);
37320 if(this.labelmd > 0){
37321 label.cls += ' col-md-' + this.labelmd;
37322 items.cls += ' col-md-' + (12 - this.labelmd);
37325 if(this.labelsm > 0){
37326 label.cls += ' col-sm-' + this.labelsm;
37327 items.cls += ' col-sm-' + (12 - this.labelsm);
37330 if(this.labelxs > 0){
37331 label.cls += ' col-xs-' + this.labelxs;
37332 items.cls += ' col-xs-' + (12 - this.labelxs);
37338 cls : 'roo-radio-set',
37342 cls : 'roo-radio-set-input',
37345 value : this.value ? this.value : ''
37352 if(this.weight.length){
37353 cfg.cls += ' roo-radio-' + this.weight;
37357 cfg.cls += ' roo-radio-set-inline';
37361 ['xs','sm','md','lg'].map(function(size){
37362 if (settings[size]) {
37363 cfg.cls += ' col-' + size + '-' + settings[size];
37371 initEvents : function()
37373 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37374 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37376 if(!this.fieldLabel.length){
37377 this.labelEl.hide();
37380 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37381 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37383 this.indicator = this.indicatorEl();
37385 if(this.indicator){
37386 this.indicator.addClass('invisible');
37389 this.originalValue = this.getValue();
37393 inputEl: function ()
37395 return this.el.select('.roo-radio-set-input', true).first();
37398 getChildContainer : function()
37400 return this.itemsEl;
37403 register : function(item)
37405 this.radioes.push(item);
37409 validate : function()
37411 if(this.getVisibilityEl().hasClass('hidden')){
37417 Roo.each(this.radioes, function(i){
37426 if(this.allowBlank) {
37430 if(this.disabled || valid){
37435 this.markInvalid();
37440 markValid : function()
37442 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37443 this.indicatorEl().removeClass('visible');
37444 this.indicatorEl().addClass('invisible');
37448 if (Roo.bootstrap.version == 3) {
37449 this.el.removeClass([this.invalidClass, this.validClass]);
37450 this.el.addClass(this.validClass);
37452 this.el.removeClass(['is-invalid','is-valid']);
37453 this.el.addClass(['is-valid']);
37455 this.fireEvent('valid', this);
37458 markInvalid : function(msg)
37460 if(this.allowBlank || this.disabled){
37464 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37465 this.indicatorEl().removeClass('invisible');
37466 this.indicatorEl().addClass('visible');
37468 if (Roo.bootstrap.version == 3) {
37469 this.el.removeClass([this.invalidClass, this.validClass]);
37470 this.el.addClass(this.invalidClass);
37472 this.el.removeClass(['is-invalid','is-valid']);
37473 this.el.addClass(['is-invalid']);
37476 this.fireEvent('invalid', this, msg);
37480 setValue : function(v, suppressEvent)
37482 if(this.value === v){
37489 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37492 Roo.each(this.radioes, function(i){
37494 i.el.removeClass('checked');
37497 Roo.each(this.radioes, function(i){
37499 if(i.value === v || i.value.toString() === v.toString()){
37501 i.el.addClass('checked');
37503 if(suppressEvent !== true){
37504 this.fireEvent('check', this, i);
37515 clearInvalid : function(){
37517 if(!this.el || this.preventMark){
37521 this.el.removeClass([this.invalidClass]);
37523 this.fireEvent('valid', this);
37528 Roo.apply(Roo.bootstrap.RadioSet, {
37532 register : function(set)
37534 this.groups[set.name] = set;
37537 get: function(name)
37539 if (typeof(this.groups[name]) == 'undefined') {
37543 return this.groups[name] ;
37549 * Ext JS Library 1.1.1
37550 * Copyright(c) 2006-2007, Ext JS, LLC.
37552 * Originally Released Under LGPL - original licence link has changed is not relivant.
37555 * <script type="text/javascript">
37560 * @class Roo.bootstrap.SplitBar
37561 * @extends Roo.util.Observable
37562 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37566 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37567 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37568 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37569 split.minSize = 100;
37570 split.maxSize = 600;
37571 split.animate = true;
37572 split.on('moved', splitterMoved);
37575 * Create a new SplitBar
37576 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37577 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37578 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37579 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37580 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37581 position of the SplitBar).
37583 Roo.bootstrap.SplitBar = function(cfg){
37588 // dragElement : elm
37589 // resizingElement: el,
37591 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37592 // placement : Roo.bootstrap.SplitBar.LEFT ,
37593 // existingProxy ???
37596 this.el = Roo.get(cfg.dragElement, true);
37597 this.el.dom.unselectable = "on";
37599 this.resizingEl = Roo.get(cfg.resizingElement, true);
37603 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37604 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37607 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37610 * The minimum size of the resizing element. (Defaults to 0)
37616 * The maximum size of the resizing element. (Defaults to 2000)
37619 this.maxSize = 2000;
37622 * Whether to animate the transition to the new size
37625 this.animate = false;
37628 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37631 this.useShim = false;
37636 if(!cfg.existingProxy){
37638 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37640 this.proxy = Roo.get(cfg.existingProxy).dom;
37643 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37646 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37649 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37652 this.dragSpecs = {};
37655 * @private The adapter to use to positon and resize elements
37657 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37658 this.adapter.init(this);
37660 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37662 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37663 this.el.addClass("roo-splitbar-h");
37666 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37667 this.el.addClass("roo-splitbar-v");
37673 * Fires when the splitter is moved (alias for {@link #event-moved})
37674 * @param {Roo.bootstrap.SplitBar} this
37675 * @param {Number} newSize the new width or height
37680 * Fires when the splitter is moved
37681 * @param {Roo.bootstrap.SplitBar} this
37682 * @param {Number} newSize the new width or height
37686 * @event beforeresize
37687 * Fires before the splitter is dragged
37688 * @param {Roo.bootstrap.SplitBar} this
37690 "beforeresize" : true,
37692 "beforeapply" : true
37695 Roo.util.Observable.call(this);
37698 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37699 onStartProxyDrag : function(x, y){
37700 this.fireEvent("beforeresize", this);
37702 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37704 o.enableDisplayMode("block");
37705 // all splitbars share the same overlay
37706 Roo.bootstrap.SplitBar.prototype.overlay = o;
37708 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37709 this.overlay.show();
37710 Roo.get(this.proxy).setDisplayed("block");
37711 var size = this.adapter.getElementSize(this);
37712 this.activeMinSize = this.getMinimumSize();;
37713 this.activeMaxSize = this.getMaximumSize();;
37714 var c1 = size - this.activeMinSize;
37715 var c2 = Math.max(this.activeMaxSize - size, 0);
37716 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37717 this.dd.resetConstraints();
37718 this.dd.setXConstraint(
37719 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37720 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37722 this.dd.setYConstraint(0, 0);
37724 this.dd.resetConstraints();
37725 this.dd.setXConstraint(0, 0);
37726 this.dd.setYConstraint(
37727 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37728 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37731 this.dragSpecs.startSize = size;
37732 this.dragSpecs.startPoint = [x, y];
37733 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37737 * @private Called after the drag operation by the DDProxy
37739 onEndProxyDrag : function(e){
37740 Roo.get(this.proxy).setDisplayed(false);
37741 var endPoint = Roo.lib.Event.getXY(e);
37743 this.overlay.hide();
37746 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37747 newSize = this.dragSpecs.startSize +
37748 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37749 endPoint[0] - this.dragSpecs.startPoint[0] :
37750 this.dragSpecs.startPoint[0] - endPoint[0]
37753 newSize = this.dragSpecs.startSize +
37754 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37755 endPoint[1] - this.dragSpecs.startPoint[1] :
37756 this.dragSpecs.startPoint[1] - endPoint[1]
37759 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37760 if(newSize != this.dragSpecs.startSize){
37761 if(this.fireEvent('beforeapply', this, newSize) !== false){
37762 this.adapter.setElementSize(this, newSize);
37763 this.fireEvent("moved", this, newSize);
37764 this.fireEvent("resize", this, newSize);
37770 * Get the adapter this SplitBar uses
37771 * @return The adapter object
37773 getAdapter : function(){
37774 return this.adapter;
37778 * Set the adapter this SplitBar uses
37779 * @param {Object} adapter A SplitBar adapter object
37781 setAdapter : function(adapter){
37782 this.adapter = adapter;
37783 this.adapter.init(this);
37787 * Gets the minimum size for the resizing element
37788 * @return {Number} The minimum size
37790 getMinimumSize : function(){
37791 return this.minSize;
37795 * Sets the minimum size for the resizing element
37796 * @param {Number} minSize The minimum size
37798 setMinimumSize : function(minSize){
37799 this.minSize = minSize;
37803 * Gets the maximum size for the resizing element
37804 * @return {Number} The maximum size
37806 getMaximumSize : function(){
37807 return this.maxSize;
37811 * Sets the maximum size for the resizing element
37812 * @param {Number} maxSize The maximum size
37814 setMaximumSize : function(maxSize){
37815 this.maxSize = maxSize;
37819 * Sets the initialize size for the resizing element
37820 * @param {Number} size The initial size
37822 setCurrentSize : function(size){
37823 var oldAnimate = this.animate;
37824 this.animate = false;
37825 this.adapter.setElementSize(this, size);
37826 this.animate = oldAnimate;
37830 * Destroy this splitbar.
37831 * @param {Boolean} removeEl True to remove the element
37833 destroy : function(removeEl){
37835 this.shim.remove();
37838 this.proxy.parentNode.removeChild(this.proxy);
37846 * @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.
37848 Roo.bootstrap.SplitBar.createProxy = function(dir){
37849 var proxy = new Roo.Element(document.createElement("div"));
37850 proxy.unselectable();
37851 var cls = 'roo-splitbar-proxy';
37852 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37853 document.body.appendChild(proxy.dom);
37858 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37859 * Default Adapter. It assumes the splitter and resizing element are not positioned
37860 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37862 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37865 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37866 // do nothing for now
37867 init : function(s){
37871 * Called before drag operations to get the current size of the resizing element.
37872 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37874 getElementSize : function(s){
37875 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37876 return s.resizingEl.getWidth();
37878 return s.resizingEl.getHeight();
37883 * Called after drag operations to set the size of the resizing element.
37884 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37885 * @param {Number} newSize The new size to set
37886 * @param {Function} onComplete A function to be invoked when resizing is complete
37888 setElementSize : function(s, newSize, onComplete){
37889 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37891 s.resizingEl.setWidth(newSize);
37893 onComplete(s, newSize);
37896 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37901 s.resizingEl.setHeight(newSize);
37903 onComplete(s, newSize);
37906 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37913 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37914 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37915 * Adapter that moves the splitter element to align with the resized sizing element.
37916 * Used with an absolute positioned SplitBar.
37917 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37918 * document.body, make sure you assign an id to the body element.
37920 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37921 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37922 this.container = Roo.get(container);
37925 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37926 init : function(s){
37927 this.basic.init(s);
37930 getElementSize : function(s){
37931 return this.basic.getElementSize(s);
37934 setElementSize : function(s, newSize, onComplete){
37935 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37938 moveSplitter : function(s){
37939 var yes = Roo.bootstrap.SplitBar;
37940 switch(s.placement){
37942 s.el.setX(s.resizingEl.getRight());
37945 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37948 s.el.setY(s.resizingEl.getBottom());
37951 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37958 * Orientation constant - Create a vertical SplitBar
37962 Roo.bootstrap.SplitBar.VERTICAL = 1;
37965 * Orientation constant - Create a horizontal SplitBar
37969 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37972 * Placement constant - The resizing element is to the left of the splitter element
37976 Roo.bootstrap.SplitBar.LEFT = 1;
37979 * Placement constant - The resizing element is to the right of the splitter element
37983 Roo.bootstrap.SplitBar.RIGHT = 2;
37986 * Placement constant - The resizing element is positioned above the splitter element
37990 Roo.bootstrap.SplitBar.TOP = 3;
37993 * Placement constant - The resizing element is positioned under splitter element
37997 Roo.bootstrap.SplitBar.BOTTOM = 4;
37998 Roo.namespace("Roo.bootstrap.layout");/*
38000 * Ext JS Library 1.1.1
38001 * Copyright(c) 2006-2007, Ext JS, LLC.
38003 * Originally Released Under LGPL - original licence link has changed is not relivant.
38006 * <script type="text/javascript">
38010 * @class Roo.bootstrap.layout.Manager
38011 * @extends Roo.bootstrap.Component
38012 * Base class for layout managers.
38014 Roo.bootstrap.layout.Manager = function(config)
38016 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38022 /** false to disable window resize monitoring @type Boolean */
38023 this.monitorWindowResize = true;
38028 * Fires when a layout is performed.
38029 * @param {Roo.LayoutManager} this
38033 * @event regionresized
38034 * Fires when the user resizes a region.
38035 * @param {Roo.LayoutRegion} region The resized region
38036 * @param {Number} newSize The new size (width for east/west, height for north/south)
38038 "regionresized" : true,
38040 * @event regioncollapsed
38041 * Fires when a region is collapsed.
38042 * @param {Roo.LayoutRegion} region The collapsed region
38044 "regioncollapsed" : true,
38046 * @event regionexpanded
38047 * Fires when a region is expanded.
38048 * @param {Roo.LayoutRegion} region The expanded region
38050 "regionexpanded" : true
38052 this.updating = false;
38055 this.el = Roo.get(config.el);
38061 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38066 monitorWindowResize : true,
38072 onRender : function(ct, position)
38075 this.el = Roo.get(ct);
38078 //this.fireEvent('render',this);
38082 initEvents: function()
38086 // ie scrollbar fix
38087 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38088 document.body.scroll = "no";
38089 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38090 this.el.position('relative');
38092 this.id = this.el.id;
38093 this.el.addClass("roo-layout-container");
38094 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38095 if(this.el.dom != document.body ) {
38096 this.el.on('resize', this.layout,this);
38097 this.el.on('show', this.layout,this);
38103 * Returns true if this layout is currently being updated
38104 * @return {Boolean}
38106 isUpdating : function(){
38107 return this.updating;
38111 * Suspend the LayoutManager from doing auto-layouts while
38112 * making multiple add or remove calls
38114 beginUpdate : function(){
38115 this.updating = true;
38119 * Restore auto-layouts and optionally disable the manager from performing a layout
38120 * @param {Boolean} noLayout true to disable a layout update
38122 endUpdate : function(noLayout){
38123 this.updating = false;
38129 layout: function(){
38133 onRegionResized : function(region, newSize){
38134 this.fireEvent("regionresized", region, newSize);
38138 onRegionCollapsed : function(region){
38139 this.fireEvent("regioncollapsed", region);
38142 onRegionExpanded : function(region){
38143 this.fireEvent("regionexpanded", region);
38147 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38148 * performs box-model adjustments.
38149 * @return {Object} The size as an object {width: (the width), height: (the height)}
38151 getViewSize : function()
38154 if(this.el.dom != document.body){
38155 size = this.el.getSize();
38157 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38159 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38160 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38165 * Returns the Element this layout is bound to.
38166 * @return {Roo.Element}
38168 getEl : function(){
38173 * Returns the specified region.
38174 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38175 * @return {Roo.LayoutRegion}
38177 getRegion : function(target){
38178 return this.regions[target.toLowerCase()];
38181 onWindowResize : function(){
38182 if(this.monitorWindowResize){
38189 * Ext JS Library 1.1.1
38190 * Copyright(c) 2006-2007, Ext JS, LLC.
38192 * Originally Released Under LGPL - original licence link has changed is not relivant.
38195 * <script type="text/javascript">
38198 * @class Roo.bootstrap.layout.Border
38199 * @extends Roo.bootstrap.layout.Manager
38200 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38201 * please see: examples/bootstrap/nested.html<br><br>
38203 <b>The container the layout is rendered into can be either the body element or any other element.
38204 If it is not the body element, the container needs to either be an absolute positioned element,
38205 or you will need to add "position:relative" to the css of the container. You will also need to specify
38206 the container size if it is not the body element.</b>
38209 * Create a new Border
38210 * @param {Object} config Configuration options
38212 Roo.bootstrap.layout.Border = function(config){
38213 config = config || {};
38214 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38218 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38219 if(config[region]){
38220 config[region].region = region;
38221 this.addRegion(config[region]);
38227 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38229 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38231 parent : false, // this might point to a 'nest' or a ???
38234 * Creates and adds a new region if it doesn't already exist.
38235 * @param {String} target The target region key (north, south, east, west or center).
38236 * @param {Object} config The regions config object
38237 * @return {BorderLayoutRegion} The new region
38239 addRegion : function(config)
38241 if(!this.regions[config.region]){
38242 var r = this.factory(config);
38243 this.bindRegion(r);
38245 return this.regions[config.region];
38249 bindRegion : function(r){
38250 this.regions[r.config.region] = r;
38252 r.on("visibilitychange", this.layout, this);
38253 r.on("paneladded", this.layout, this);
38254 r.on("panelremoved", this.layout, this);
38255 r.on("invalidated", this.layout, this);
38256 r.on("resized", this.onRegionResized, this);
38257 r.on("collapsed", this.onRegionCollapsed, this);
38258 r.on("expanded", this.onRegionExpanded, this);
38262 * Performs a layout update.
38264 layout : function()
38266 if(this.updating) {
38270 // render all the rebions if they have not been done alreayd?
38271 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38272 if(this.regions[region] && !this.regions[region].bodyEl){
38273 this.regions[region].onRender(this.el)
38277 var size = this.getViewSize();
38278 var w = size.width;
38279 var h = size.height;
38284 //var x = 0, y = 0;
38286 var rs = this.regions;
38287 var north = rs["north"];
38288 var south = rs["south"];
38289 var west = rs["west"];
38290 var east = rs["east"];
38291 var center = rs["center"];
38292 //if(this.hideOnLayout){ // not supported anymore
38293 //c.el.setStyle("display", "none");
38295 if(north && north.isVisible()){
38296 var b = north.getBox();
38297 var m = north.getMargins();
38298 b.width = w - (m.left+m.right);
38301 centerY = b.height + b.y + m.bottom;
38302 centerH -= centerY;
38303 north.updateBox(this.safeBox(b));
38305 if(south && south.isVisible()){
38306 var b = south.getBox();
38307 var m = south.getMargins();
38308 b.width = w - (m.left+m.right);
38310 var totalHeight = (b.height + m.top + m.bottom);
38311 b.y = h - totalHeight + m.top;
38312 centerH -= totalHeight;
38313 south.updateBox(this.safeBox(b));
38315 if(west && west.isVisible()){
38316 var b = west.getBox();
38317 var m = west.getMargins();
38318 b.height = centerH - (m.top+m.bottom);
38320 b.y = centerY + m.top;
38321 var totalWidth = (b.width + m.left + m.right);
38322 centerX += totalWidth;
38323 centerW -= totalWidth;
38324 west.updateBox(this.safeBox(b));
38326 if(east && east.isVisible()){
38327 var b = east.getBox();
38328 var m = east.getMargins();
38329 b.height = centerH - (m.top+m.bottom);
38330 var totalWidth = (b.width + m.left + m.right);
38331 b.x = w - totalWidth + m.left;
38332 b.y = centerY + m.top;
38333 centerW -= totalWidth;
38334 east.updateBox(this.safeBox(b));
38337 var m = center.getMargins();
38339 x: centerX + m.left,
38340 y: centerY + m.top,
38341 width: centerW - (m.left+m.right),
38342 height: centerH - (m.top+m.bottom)
38344 //if(this.hideOnLayout){
38345 //center.el.setStyle("display", "block");
38347 center.updateBox(this.safeBox(centerBox));
38350 this.fireEvent("layout", this);
38354 safeBox : function(box){
38355 box.width = Math.max(0, box.width);
38356 box.height = Math.max(0, box.height);
38361 * Adds a ContentPanel (or subclass) to this layout.
38362 * @param {String} target The target region key (north, south, east, west or center).
38363 * @param {Roo.ContentPanel} panel The panel to add
38364 * @return {Roo.ContentPanel} The added panel
38366 add : function(target, panel){
38368 target = target.toLowerCase();
38369 return this.regions[target].add(panel);
38373 * Remove a ContentPanel (or subclass) to this layout.
38374 * @param {String} target The target region key (north, south, east, west or center).
38375 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38376 * @return {Roo.ContentPanel} The removed panel
38378 remove : function(target, panel){
38379 target = target.toLowerCase();
38380 return this.regions[target].remove(panel);
38384 * Searches all regions for a panel with the specified id
38385 * @param {String} panelId
38386 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38388 findPanel : function(panelId){
38389 var rs = this.regions;
38390 for(var target in rs){
38391 if(typeof rs[target] != "function"){
38392 var p = rs[target].getPanel(panelId);
38402 * Searches all regions for a panel with the specified id and activates (shows) it.
38403 * @param {String/ContentPanel} panelId The panels id or the panel itself
38404 * @return {Roo.ContentPanel} The shown panel or null
38406 showPanel : function(panelId) {
38407 var rs = this.regions;
38408 for(var target in rs){
38409 var r = rs[target];
38410 if(typeof r != "function"){
38411 if(r.hasPanel(panelId)){
38412 return r.showPanel(panelId);
38420 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38421 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38424 restoreState : function(provider){
38426 provider = Roo.state.Manager;
38428 var sm = new Roo.LayoutStateManager();
38429 sm.init(this, provider);
38435 * Adds a xtype elements to the layout.
38439 xtype : 'ContentPanel',
38446 xtype : 'NestedLayoutPanel',
38452 items : [ ... list of content panels or nested layout panels.. ]
38456 * @param {Object} cfg Xtype definition of item to add.
38458 addxtype : function(cfg)
38460 // basically accepts a pannel...
38461 // can accept a layout region..!?!?
38462 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38465 // theory? children can only be panels??
38467 //if (!cfg.xtype.match(/Panel$/)) {
38472 if (typeof(cfg.region) == 'undefined') {
38473 Roo.log("Failed to add Panel, region was not set");
38477 var region = cfg.region;
38483 xitems = cfg.items;
38488 if ( region == 'center') {
38489 Roo.log("Center: " + cfg.title);
38495 case 'Content': // ContentPanel (el, cfg)
38496 case 'Scroll': // ContentPanel (el, cfg)
38498 cfg.autoCreate = cfg.autoCreate || true;
38499 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38501 // var el = this.el.createChild();
38502 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38505 this.add(region, ret);
38509 case 'TreePanel': // our new panel!
38510 cfg.el = this.el.createChild();
38511 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38512 this.add(region, ret);
38517 // create a new Layout (which is a Border Layout...
38519 var clayout = cfg.layout;
38520 clayout.el = this.el.createChild();
38521 clayout.items = clayout.items || [];
38525 // replace this exitems with the clayout ones..
38526 xitems = clayout.items;
38528 // force background off if it's in center...
38529 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38530 cfg.background = false;
38532 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38535 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38536 //console.log('adding nested layout panel ' + cfg.toSource());
38537 this.add(region, ret);
38538 nb = {}; /// find first...
38543 // needs grid and region
38545 //var el = this.getRegion(region).el.createChild();
38547 *var el = this.el.createChild();
38548 // create the grid first...
38549 cfg.grid.container = el;
38550 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38553 if (region == 'center' && this.active ) {
38554 cfg.background = false;
38557 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38559 this.add(region, ret);
38561 if (cfg.background) {
38562 // render grid on panel activation (if panel background)
38563 ret.on('activate', function(gp) {
38564 if (!gp.grid.rendered) {
38565 // gp.grid.render(el);
38569 // cfg.grid.render(el);
38575 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38576 // it was the old xcomponent building that caused this before.
38577 // espeically if border is the top element in the tree.
38587 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38589 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38590 this.add(region, ret);
38594 throw "Can not add '" + cfg.xtype + "' to Border";
38600 this.beginUpdate();
38604 Roo.each(xitems, function(i) {
38605 region = nb && i.region ? i.region : false;
38607 var add = ret.addxtype(i);
38610 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38611 if (!i.background) {
38612 abn[region] = nb[region] ;
38619 // make the last non-background panel active..
38620 //if (nb) { Roo.log(abn); }
38623 for(var r in abn) {
38624 region = this.getRegion(r);
38626 // tried using nb[r], but it does not work..
38628 region.showPanel(abn[r]);
38639 factory : function(cfg)
38642 var validRegions = Roo.bootstrap.layout.Border.regions;
38644 var target = cfg.region;
38647 var r = Roo.bootstrap.layout;
38651 return new r.North(cfg);
38653 return new r.South(cfg);
38655 return new r.East(cfg);
38657 return new r.West(cfg);
38659 return new r.Center(cfg);
38661 throw 'Layout region "'+target+'" not supported.';
38668 * Ext JS Library 1.1.1
38669 * Copyright(c) 2006-2007, Ext JS, LLC.
38671 * Originally Released Under LGPL - original licence link has changed is not relivant.
38674 * <script type="text/javascript">
38678 * @class Roo.bootstrap.layout.Basic
38679 * @extends Roo.util.Observable
38680 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38681 * and does not have a titlebar, tabs or any other features. All it does is size and position
38682 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38683 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38684 * @cfg {string} region the region that it inhabits..
38685 * @cfg {bool} skipConfig skip config?
38689 Roo.bootstrap.layout.Basic = function(config){
38691 this.mgr = config.mgr;
38693 this.position = config.region;
38695 var skipConfig = config.skipConfig;
38699 * @scope Roo.BasicLayoutRegion
38703 * @event beforeremove
38704 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38705 * @param {Roo.LayoutRegion} this
38706 * @param {Roo.ContentPanel} panel The panel
38707 * @param {Object} e The cancel event object
38709 "beforeremove" : true,
38711 * @event invalidated
38712 * Fires when the layout for this region is changed.
38713 * @param {Roo.LayoutRegion} this
38715 "invalidated" : true,
38717 * @event visibilitychange
38718 * Fires when this region is shown or hidden
38719 * @param {Roo.LayoutRegion} this
38720 * @param {Boolean} visibility true or false
38722 "visibilitychange" : true,
38724 * @event paneladded
38725 * Fires when a panel is added.
38726 * @param {Roo.LayoutRegion} this
38727 * @param {Roo.ContentPanel} panel The panel
38729 "paneladded" : true,
38731 * @event panelremoved
38732 * Fires when a panel is removed.
38733 * @param {Roo.LayoutRegion} this
38734 * @param {Roo.ContentPanel} panel The panel
38736 "panelremoved" : true,
38738 * @event beforecollapse
38739 * Fires when this region before collapse.
38740 * @param {Roo.LayoutRegion} this
38742 "beforecollapse" : true,
38745 * Fires when this region is collapsed.
38746 * @param {Roo.LayoutRegion} this
38748 "collapsed" : true,
38751 * Fires when this region is expanded.
38752 * @param {Roo.LayoutRegion} this
38757 * Fires when this region is slid into view.
38758 * @param {Roo.LayoutRegion} this
38760 "slideshow" : true,
38763 * Fires when this region slides out of view.
38764 * @param {Roo.LayoutRegion} this
38766 "slidehide" : true,
38768 * @event panelactivated
38769 * Fires when a panel is activated.
38770 * @param {Roo.LayoutRegion} this
38771 * @param {Roo.ContentPanel} panel The activated panel
38773 "panelactivated" : true,
38776 * Fires when the user resizes this region.
38777 * @param {Roo.LayoutRegion} this
38778 * @param {Number} newSize The new size (width for east/west, height for north/south)
38782 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38783 this.panels = new Roo.util.MixedCollection();
38784 this.panels.getKey = this.getPanelId.createDelegate(this);
38786 this.activePanel = null;
38787 // ensure listeners are added...
38789 if (config.listeners || config.events) {
38790 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38791 listeners : config.listeners || {},
38792 events : config.events || {}
38796 if(skipConfig !== true){
38797 this.applyConfig(config);
38801 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38803 getPanelId : function(p){
38807 applyConfig : function(config){
38808 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38809 this.config = config;
38814 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38815 * the width, for horizontal (north, south) the height.
38816 * @param {Number} newSize The new width or height
38818 resizeTo : function(newSize){
38819 var el = this.el ? this.el :
38820 (this.activePanel ? this.activePanel.getEl() : null);
38822 switch(this.position){
38825 el.setWidth(newSize);
38826 this.fireEvent("resized", this, newSize);
38830 el.setHeight(newSize);
38831 this.fireEvent("resized", this, newSize);
38837 getBox : function(){
38838 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38841 getMargins : function(){
38842 return this.margins;
38845 updateBox : function(box){
38847 var el = this.activePanel.getEl();
38848 el.dom.style.left = box.x + "px";
38849 el.dom.style.top = box.y + "px";
38850 this.activePanel.setSize(box.width, box.height);
38854 * Returns the container element for this region.
38855 * @return {Roo.Element}
38857 getEl : function(){
38858 return this.activePanel;
38862 * Returns true if this region is currently visible.
38863 * @return {Boolean}
38865 isVisible : function(){
38866 return this.activePanel ? true : false;
38869 setActivePanel : function(panel){
38870 panel = this.getPanel(panel);
38871 if(this.activePanel && this.activePanel != panel){
38872 this.activePanel.setActiveState(false);
38873 this.activePanel.getEl().setLeftTop(-10000,-10000);
38875 this.activePanel = panel;
38876 panel.setActiveState(true);
38878 panel.setSize(this.box.width, this.box.height);
38880 this.fireEvent("panelactivated", this, panel);
38881 this.fireEvent("invalidated");
38885 * Show the specified panel.
38886 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38887 * @return {Roo.ContentPanel} The shown panel or null
38889 showPanel : function(panel){
38890 panel = this.getPanel(panel);
38892 this.setActivePanel(panel);
38898 * Get the active panel for this region.
38899 * @return {Roo.ContentPanel} The active panel or null
38901 getActivePanel : function(){
38902 return this.activePanel;
38906 * Add the passed ContentPanel(s)
38907 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38908 * @return {Roo.ContentPanel} The panel added (if only one was added)
38910 add : function(panel){
38911 if(arguments.length > 1){
38912 for(var i = 0, len = arguments.length; i < len; i++) {
38913 this.add(arguments[i]);
38917 if(this.hasPanel(panel)){
38918 this.showPanel(panel);
38921 var el = panel.getEl();
38922 if(el.dom.parentNode != this.mgr.el.dom){
38923 this.mgr.el.dom.appendChild(el.dom);
38925 if(panel.setRegion){
38926 panel.setRegion(this);
38928 this.panels.add(panel);
38929 el.setStyle("position", "absolute");
38930 if(!panel.background){
38931 this.setActivePanel(panel);
38932 if(this.config.initialSize && this.panels.getCount()==1){
38933 this.resizeTo(this.config.initialSize);
38936 this.fireEvent("paneladded", this, panel);
38941 * Returns true if the panel is in this region.
38942 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38943 * @return {Boolean}
38945 hasPanel : function(panel){
38946 if(typeof panel == "object"){ // must be panel obj
38947 panel = panel.getId();
38949 return this.getPanel(panel) ? true : false;
38953 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38954 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38955 * @param {Boolean} preservePanel Overrides the config preservePanel option
38956 * @return {Roo.ContentPanel} The panel that was removed
38958 remove : function(panel, preservePanel){
38959 panel = this.getPanel(panel);
38964 this.fireEvent("beforeremove", this, panel, e);
38965 if(e.cancel === true){
38968 var panelId = panel.getId();
38969 this.panels.removeKey(panelId);
38974 * Returns the panel specified or null if it's not in this region.
38975 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38976 * @return {Roo.ContentPanel}
38978 getPanel : function(id){
38979 if(typeof id == "object"){ // must be panel obj
38982 return this.panels.get(id);
38986 * Returns this regions position (north/south/east/west/center).
38989 getPosition: function(){
38990 return this.position;
38994 * Ext JS Library 1.1.1
38995 * Copyright(c) 2006-2007, Ext JS, LLC.
38997 * Originally Released Under LGPL - original licence link has changed is not relivant.
39000 * <script type="text/javascript">
39004 * @class Roo.bootstrap.layout.Region
39005 * @extends Roo.bootstrap.layout.Basic
39006 * This class represents a region in a layout manager.
39008 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39009 * @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})
39010 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39011 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39012 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39013 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39014 * @cfg {String} title The title for the region (overrides panel titles)
39015 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39016 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39017 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39018 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39019 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39020 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39021 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39022 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39023 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39024 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39026 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39027 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39028 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39029 * @cfg {Number} width For East/West panels
39030 * @cfg {Number} height For North/South panels
39031 * @cfg {Boolean} split To show the splitter
39032 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39034 * @cfg {string} cls Extra CSS classes to add to region
39036 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39037 * @cfg {string} region the region that it inhabits..
39040 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39041 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39043 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39044 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39045 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39047 Roo.bootstrap.layout.Region = function(config)
39049 this.applyConfig(config);
39051 var mgr = config.mgr;
39052 var pos = config.region;
39053 config.skipConfig = true;
39054 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39057 this.onRender(mgr.el);
39060 this.visible = true;
39061 this.collapsed = false;
39062 this.unrendered_panels = [];
39065 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39067 position: '', // set by wrapper (eg. north/south etc..)
39068 unrendered_panels : null, // unrendered panels.
39070 tabPosition : false,
39072 mgr: false, // points to 'Border'
39075 createBody : function(){
39076 /** This region's body element
39077 * @type Roo.Element */
39078 this.bodyEl = this.el.createChild({
39080 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39084 onRender: function(ctr, pos)
39086 var dh = Roo.DomHelper;
39087 /** This region's container element
39088 * @type Roo.Element */
39089 this.el = dh.append(ctr.dom, {
39091 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39093 /** This region's title element
39094 * @type Roo.Element */
39096 this.titleEl = dh.append(this.el.dom, {
39098 unselectable: "on",
39099 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39101 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39102 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39106 this.titleEl.enableDisplayMode();
39107 /** This region's title text element
39108 * @type HTMLElement */
39109 this.titleTextEl = this.titleEl.dom.firstChild;
39110 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39112 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39113 this.closeBtn.enableDisplayMode();
39114 this.closeBtn.on("click", this.closeClicked, this);
39115 this.closeBtn.hide();
39117 this.createBody(this.config);
39118 if(this.config.hideWhenEmpty){
39120 this.on("paneladded", this.validateVisibility, this);
39121 this.on("panelremoved", this.validateVisibility, this);
39123 if(this.autoScroll){
39124 this.bodyEl.setStyle("overflow", "auto");
39126 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39128 //if(c.titlebar !== false){
39129 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39130 this.titleEl.hide();
39132 this.titleEl.show();
39133 if(this.config.title){
39134 this.titleTextEl.innerHTML = this.config.title;
39138 if(this.config.collapsed){
39139 this.collapse(true);
39141 if(this.config.hidden){
39145 if (this.unrendered_panels && this.unrendered_panels.length) {
39146 for (var i =0;i< this.unrendered_panels.length; i++) {
39147 this.add(this.unrendered_panels[i]);
39149 this.unrendered_panels = null;
39155 applyConfig : function(c)
39158 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39159 var dh = Roo.DomHelper;
39160 if(c.titlebar !== false){
39161 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39162 this.collapseBtn.on("click", this.collapse, this);
39163 this.collapseBtn.enableDisplayMode();
39165 if(c.showPin === true || this.showPin){
39166 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39167 this.stickBtn.enableDisplayMode();
39168 this.stickBtn.on("click", this.expand, this);
39169 this.stickBtn.hide();
39174 /** This region's collapsed element
39175 * @type Roo.Element */
39178 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39179 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39182 if(c.floatable !== false){
39183 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39184 this.collapsedEl.on("click", this.collapseClick, this);
39187 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39188 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39189 id: "message", unselectable: "on", style:{"float":"left"}});
39190 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39192 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39193 this.expandBtn.on("click", this.expand, this);
39197 if(this.collapseBtn){
39198 this.collapseBtn.setVisible(c.collapsible == true);
39201 this.cmargins = c.cmargins || this.cmargins ||
39202 (this.position == "west" || this.position == "east" ?
39203 {top: 0, left: 2, right:2, bottom: 0} :
39204 {top: 2, left: 0, right:0, bottom: 2});
39206 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39209 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39211 this.autoScroll = c.autoScroll || false;
39216 this.duration = c.duration || .30;
39217 this.slideDuration = c.slideDuration || .45;
39222 * Returns true if this region is currently visible.
39223 * @return {Boolean}
39225 isVisible : function(){
39226 return this.visible;
39230 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39231 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39233 //setCollapsedTitle : function(title){
39234 // title = title || " ";
39235 // if(this.collapsedTitleTextEl){
39236 // this.collapsedTitleTextEl.innerHTML = title;
39240 getBox : function(){
39242 // if(!this.collapsed){
39243 b = this.el.getBox(false, true);
39245 // b = this.collapsedEl.getBox(false, true);
39250 getMargins : function(){
39251 return this.margins;
39252 //return this.collapsed ? this.cmargins : this.margins;
39255 highlight : function(){
39256 this.el.addClass("x-layout-panel-dragover");
39259 unhighlight : function(){
39260 this.el.removeClass("x-layout-panel-dragover");
39263 updateBox : function(box)
39265 if (!this.bodyEl) {
39266 return; // not rendered yet..
39270 if(!this.collapsed){
39271 this.el.dom.style.left = box.x + "px";
39272 this.el.dom.style.top = box.y + "px";
39273 this.updateBody(box.width, box.height);
39275 this.collapsedEl.dom.style.left = box.x + "px";
39276 this.collapsedEl.dom.style.top = box.y + "px";
39277 this.collapsedEl.setSize(box.width, box.height);
39280 this.tabs.autoSizeTabs();
39284 updateBody : function(w, h)
39287 this.el.setWidth(w);
39288 w -= this.el.getBorderWidth("rl");
39289 if(this.config.adjustments){
39290 w += this.config.adjustments[0];
39293 if(h !== null && h > 0){
39294 this.el.setHeight(h);
39295 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39296 h -= this.el.getBorderWidth("tb");
39297 if(this.config.adjustments){
39298 h += this.config.adjustments[1];
39300 this.bodyEl.setHeight(h);
39302 h = this.tabs.syncHeight(h);
39305 if(this.panelSize){
39306 w = w !== null ? w : this.panelSize.width;
39307 h = h !== null ? h : this.panelSize.height;
39309 if(this.activePanel){
39310 var el = this.activePanel.getEl();
39311 w = w !== null ? w : el.getWidth();
39312 h = h !== null ? h : el.getHeight();
39313 this.panelSize = {width: w, height: h};
39314 this.activePanel.setSize(w, h);
39316 if(Roo.isIE && this.tabs){
39317 this.tabs.el.repaint();
39322 * Returns the container element for this region.
39323 * @return {Roo.Element}
39325 getEl : function(){
39330 * Hides this region.
39333 //if(!this.collapsed){
39334 this.el.dom.style.left = "-2000px";
39337 // this.collapsedEl.dom.style.left = "-2000px";
39338 // this.collapsedEl.hide();
39340 this.visible = false;
39341 this.fireEvent("visibilitychange", this, false);
39345 * Shows this region if it was previously hidden.
39348 //if(!this.collapsed){
39351 // this.collapsedEl.show();
39353 this.visible = true;
39354 this.fireEvent("visibilitychange", this, true);
39357 closeClicked : function(){
39358 if(this.activePanel){
39359 this.remove(this.activePanel);
39363 collapseClick : function(e){
39365 e.stopPropagation();
39368 e.stopPropagation();
39374 * Collapses this region.
39375 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39378 collapse : function(skipAnim, skipCheck = false){
39379 if(this.collapsed) {
39383 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39385 this.collapsed = true;
39387 this.split.el.hide();
39389 if(this.config.animate && skipAnim !== true){
39390 this.fireEvent("invalidated", this);
39391 this.animateCollapse();
39393 this.el.setLocation(-20000,-20000);
39395 this.collapsedEl.show();
39396 this.fireEvent("collapsed", this);
39397 this.fireEvent("invalidated", this);
39403 animateCollapse : function(){
39408 * Expands this region if it was previously collapsed.
39409 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39410 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39413 expand : function(e, skipAnim){
39415 e.stopPropagation();
39417 if(!this.collapsed || this.el.hasActiveFx()) {
39421 this.afterSlideIn();
39424 this.collapsed = false;
39425 if(this.config.animate && skipAnim !== true){
39426 this.animateExpand();
39430 this.split.el.show();
39432 this.collapsedEl.setLocation(-2000,-2000);
39433 this.collapsedEl.hide();
39434 this.fireEvent("invalidated", this);
39435 this.fireEvent("expanded", this);
39439 animateExpand : function(){
39443 initTabs : function()
39445 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39447 var ts = new Roo.bootstrap.panel.Tabs({
39448 el: this.bodyEl.dom,
39450 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39451 disableTooltips: this.config.disableTabTips,
39452 toolbar : this.config.toolbar
39455 if(this.config.hideTabs){
39456 ts.stripWrap.setDisplayed(false);
39459 ts.resizeTabs = this.config.resizeTabs === true;
39460 ts.minTabWidth = this.config.minTabWidth || 40;
39461 ts.maxTabWidth = this.config.maxTabWidth || 250;
39462 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39463 ts.monitorResize = false;
39464 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39465 ts.bodyEl.addClass('roo-layout-tabs-body');
39466 this.panels.each(this.initPanelAsTab, this);
39469 initPanelAsTab : function(panel){
39470 var ti = this.tabs.addTab(
39474 this.config.closeOnTab && panel.isClosable(),
39477 if(panel.tabTip !== undefined){
39478 ti.setTooltip(panel.tabTip);
39480 ti.on("activate", function(){
39481 this.setActivePanel(panel);
39484 if(this.config.closeOnTab){
39485 ti.on("beforeclose", function(t, e){
39487 this.remove(panel);
39491 panel.tabItem = ti;
39496 updatePanelTitle : function(panel, title)
39498 if(this.activePanel == panel){
39499 this.updateTitle(title);
39502 var ti = this.tabs.getTab(panel.getEl().id);
39504 if(panel.tabTip !== undefined){
39505 ti.setTooltip(panel.tabTip);
39510 updateTitle : function(title){
39511 if(this.titleTextEl && !this.config.title){
39512 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39516 setActivePanel : function(panel)
39518 panel = this.getPanel(panel);
39519 if(this.activePanel && this.activePanel != panel){
39520 if(this.activePanel.setActiveState(false) === false){
39524 this.activePanel = panel;
39525 panel.setActiveState(true);
39526 if(this.panelSize){
39527 panel.setSize(this.panelSize.width, this.panelSize.height);
39530 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39532 this.updateTitle(panel.getTitle());
39534 this.fireEvent("invalidated", this);
39536 this.fireEvent("panelactivated", this, panel);
39540 * Shows the specified panel.
39541 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39542 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39544 showPanel : function(panel)
39546 panel = this.getPanel(panel);
39549 var tab = this.tabs.getTab(panel.getEl().id);
39550 if(tab.isHidden()){
39551 this.tabs.unhideTab(tab.id);
39555 this.setActivePanel(panel);
39562 * Get the active panel for this region.
39563 * @return {Roo.ContentPanel} The active panel or null
39565 getActivePanel : function(){
39566 return this.activePanel;
39569 validateVisibility : function(){
39570 if(this.panels.getCount() < 1){
39571 this.updateTitle(" ");
39572 this.closeBtn.hide();
39575 if(!this.isVisible()){
39582 * Adds the passed ContentPanel(s) to this region.
39583 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39584 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39586 add : function(panel)
39588 if(arguments.length > 1){
39589 for(var i = 0, len = arguments.length; i < len; i++) {
39590 this.add(arguments[i]);
39595 // if we have not been rendered yet, then we can not really do much of this..
39596 if (!this.bodyEl) {
39597 this.unrendered_panels.push(panel);
39604 if(this.hasPanel(panel)){
39605 this.showPanel(panel);
39608 panel.setRegion(this);
39609 this.panels.add(panel);
39610 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39611 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39612 // and hide them... ???
39613 this.bodyEl.dom.appendChild(panel.getEl().dom);
39614 if(panel.background !== true){
39615 this.setActivePanel(panel);
39617 this.fireEvent("paneladded", this, panel);
39624 this.initPanelAsTab(panel);
39628 if(panel.background !== true){
39629 this.tabs.activate(panel.getEl().id);
39631 this.fireEvent("paneladded", this, panel);
39636 * Hides the tab for the specified panel.
39637 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39639 hidePanel : function(panel){
39640 if(this.tabs && (panel = this.getPanel(panel))){
39641 this.tabs.hideTab(panel.getEl().id);
39646 * Unhides the tab for a previously hidden panel.
39647 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39649 unhidePanel : function(panel){
39650 if(this.tabs && (panel = this.getPanel(panel))){
39651 this.tabs.unhideTab(panel.getEl().id);
39655 clearPanels : function(){
39656 while(this.panels.getCount() > 0){
39657 this.remove(this.panels.first());
39662 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39663 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39664 * @param {Boolean} preservePanel Overrides the config preservePanel option
39665 * @return {Roo.ContentPanel} The panel that was removed
39667 remove : function(panel, preservePanel)
39669 panel = this.getPanel(panel);
39674 this.fireEvent("beforeremove", this, panel, e);
39675 if(e.cancel === true){
39678 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39679 var panelId = panel.getId();
39680 this.panels.removeKey(panelId);
39682 document.body.appendChild(panel.getEl().dom);
39685 this.tabs.removeTab(panel.getEl().id);
39686 }else if (!preservePanel){
39687 this.bodyEl.dom.removeChild(panel.getEl().dom);
39689 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39690 var p = this.panels.first();
39691 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39692 tempEl.appendChild(p.getEl().dom);
39693 this.bodyEl.update("");
39694 this.bodyEl.dom.appendChild(p.getEl().dom);
39696 this.updateTitle(p.getTitle());
39698 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39699 this.setActivePanel(p);
39701 panel.setRegion(null);
39702 if(this.activePanel == panel){
39703 this.activePanel = null;
39705 if(this.config.autoDestroy !== false && preservePanel !== true){
39706 try{panel.destroy();}catch(e){}
39708 this.fireEvent("panelremoved", this, panel);
39713 * Returns the TabPanel component used by this region
39714 * @return {Roo.TabPanel}
39716 getTabs : function(){
39720 createTool : function(parentEl, className){
39721 var btn = Roo.DomHelper.append(parentEl, {
39723 cls: "x-layout-tools-button",
39726 cls: "roo-layout-tools-button-inner " + className,
39730 btn.addClassOnOver("roo-layout-tools-button-over");
39735 * Ext JS Library 1.1.1
39736 * Copyright(c) 2006-2007, Ext JS, LLC.
39738 * Originally Released Under LGPL - original licence link has changed is not relivant.
39741 * <script type="text/javascript">
39747 * @class Roo.SplitLayoutRegion
39748 * @extends Roo.LayoutRegion
39749 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39751 Roo.bootstrap.layout.Split = function(config){
39752 this.cursor = config.cursor;
39753 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39756 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39758 splitTip : "Drag to resize.",
39759 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39760 useSplitTips : false,
39762 applyConfig : function(config){
39763 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39766 onRender : function(ctr,pos) {
39768 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39769 if(!this.config.split){
39774 var splitEl = Roo.DomHelper.append(ctr.dom, {
39776 id: this.el.id + "-split",
39777 cls: "roo-layout-split roo-layout-split-"+this.position,
39780 /** The SplitBar for this region
39781 * @type Roo.SplitBar */
39782 // does not exist yet...
39783 Roo.log([this.position, this.orientation]);
39785 this.split = new Roo.bootstrap.SplitBar({
39786 dragElement : splitEl,
39787 resizingElement: this.el,
39788 orientation : this.orientation
39791 this.split.on("moved", this.onSplitMove, this);
39792 this.split.useShim = this.config.useShim === true;
39793 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39794 if(this.useSplitTips){
39795 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39797 //if(config.collapsible){
39798 // this.split.el.on("dblclick", this.collapse, this);
39801 if(typeof this.config.minSize != "undefined"){
39802 this.split.minSize = this.config.minSize;
39804 if(typeof this.config.maxSize != "undefined"){
39805 this.split.maxSize = this.config.maxSize;
39807 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39808 this.hideSplitter();
39813 getHMaxSize : function(){
39814 var cmax = this.config.maxSize || 10000;
39815 var center = this.mgr.getRegion("center");
39816 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39819 getVMaxSize : function(){
39820 var cmax = this.config.maxSize || 10000;
39821 var center = this.mgr.getRegion("center");
39822 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39825 onSplitMove : function(split, newSize){
39826 this.fireEvent("resized", this, newSize);
39830 * Returns the {@link Roo.SplitBar} for this region.
39831 * @return {Roo.SplitBar}
39833 getSplitBar : function(){
39838 this.hideSplitter();
39839 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39842 hideSplitter : function(){
39844 this.split.el.setLocation(-2000,-2000);
39845 this.split.el.hide();
39851 this.split.el.show();
39853 Roo.bootstrap.layout.Split.superclass.show.call(this);
39856 beforeSlide: function(){
39857 if(Roo.isGecko){// firefox overflow auto bug workaround
39858 this.bodyEl.clip();
39860 this.tabs.bodyEl.clip();
39862 if(this.activePanel){
39863 this.activePanel.getEl().clip();
39865 if(this.activePanel.beforeSlide){
39866 this.activePanel.beforeSlide();
39872 afterSlide : function(){
39873 if(Roo.isGecko){// firefox overflow auto bug workaround
39874 this.bodyEl.unclip();
39876 this.tabs.bodyEl.unclip();
39878 if(this.activePanel){
39879 this.activePanel.getEl().unclip();
39880 if(this.activePanel.afterSlide){
39881 this.activePanel.afterSlide();
39887 initAutoHide : function(){
39888 if(this.autoHide !== false){
39889 if(!this.autoHideHd){
39890 var st = new Roo.util.DelayedTask(this.slideIn, this);
39891 this.autoHideHd = {
39892 "mouseout": function(e){
39893 if(!e.within(this.el, true)){
39897 "mouseover" : function(e){
39903 this.el.on(this.autoHideHd);
39907 clearAutoHide : function(){
39908 if(this.autoHide !== false){
39909 this.el.un("mouseout", this.autoHideHd.mouseout);
39910 this.el.un("mouseover", this.autoHideHd.mouseover);
39914 clearMonitor : function(){
39915 Roo.get(document).un("click", this.slideInIf, this);
39918 // these names are backwards but not changed for compat
39919 slideOut : function(){
39920 if(this.isSlid || this.el.hasActiveFx()){
39923 this.isSlid = true;
39924 if(this.collapseBtn){
39925 this.collapseBtn.hide();
39927 this.closeBtnState = this.closeBtn.getStyle('display');
39928 this.closeBtn.hide();
39930 this.stickBtn.show();
39933 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39934 this.beforeSlide();
39935 this.el.setStyle("z-index", 10001);
39936 this.el.slideIn(this.getSlideAnchor(), {
39937 callback: function(){
39939 this.initAutoHide();
39940 Roo.get(document).on("click", this.slideInIf, this);
39941 this.fireEvent("slideshow", this);
39948 afterSlideIn : function(){
39949 this.clearAutoHide();
39950 this.isSlid = false;
39951 this.clearMonitor();
39952 this.el.setStyle("z-index", "");
39953 if(this.collapseBtn){
39954 this.collapseBtn.show();
39956 this.closeBtn.setStyle('display', this.closeBtnState);
39958 this.stickBtn.hide();
39960 this.fireEvent("slidehide", this);
39963 slideIn : function(cb){
39964 if(!this.isSlid || this.el.hasActiveFx()){
39968 this.isSlid = false;
39969 this.beforeSlide();
39970 this.el.slideOut(this.getSlideAnchor(), {
39971 callback: function(){
39972 this.el.setLeftTop(-10000, -10000);
39974 this.afterSlideIn();
39982 slideInIf : function(e){
39983 if(!e.within(this.el)){
39988 animateCollapse : function(){
39989 this.beforeSlide();
39990 this.el.setStyle("z-index", 20000);
39991 var anchor = this.getSlideAnchor();
39992 this.el.slideOut(anchor, {
39993 callback : function(){
39994 this.el.setStyle("z-index", "");
39995 this.collapsedEl.slideIn(anchor, {duration:.3});
39997 this.el.setLocation(-10000,-10000);
39999 this.fireEvent("collapsed", this);
40006 animateExpand : function(){
40007 this.beforeSlide();
40008 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40009 this.el.setStyle("z-index", 20000);
40010 this.collapsedEl.hide({
40013 this.el.slideIn(this.getSlideAnchor(), {
40014 callback : function(){
40015 this.el.setStyle("z-index", "");
40018 this.split.el.show();
40020 this.fireEvent("invalidated", this);
40021 this.fireEvent("expanded", this);
40049 getAnchor : function(){
40050 return this.anchors[this.position];
40053 getCollapseAnchor : function(){
40054 return this.canchors[this.position];
40057 getSlideAnchor : function(){
40058 return this.sanchors[this.position];
40061 getAlignAdj : function(){
40062 var cm = this.cmargins;
40063 switch(this.position){
40079 getExpandAdj : function(){
40080 var c = this.collapsedEl, cm = this.cmargins;
40081 switch(this.position){
40083 return [-(cm.right+c.getWidth()+cm.left), 0];
40086 return [cm.right+c.getWidth()+cm.left, 0];
40089 return [0, -(cm.top+cm.bottom+c.getHeight())];
40092 return [0, cm.top+cm.bottom+c.getHeight()];
40098 * Ext JS Library 1.1.1
40099 * Copyright(c) 2006-2007, Ext JS, LLC.
40101 * Originally Released Under LGPL - original licence link has changed is not relivant.
40104 * <script type="text/javascript">
40107 * These classes are private internal classes
40109 Roo.bootstrap.layout.Center = function(config){
40110 config.region = "center";
40111 Roo.bootstrap.layout.Region.call(this, config);
40112 this.visible = true;
40113 this.minWidth = config.minWidth || 20;
40114 this.minHeight = config.minHeight || 20;
40117 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40119 // center panel can't be hidden
40123 // center panel can't be hidden
40126 getMinWidth: function(){
40127 return this.minWidth;
40130 getMinHeight: function(){
40131 return this.minHeight;
40145 Roo.bootstrap.layout.North = function(config)
40147 config.region = 'north';
40148 config.cursor = 'n-resize';
40150 Roo.bootstrap.layout.Split.call(this, config);
40154 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40155 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40156 this.split.el.addClass("roo-layout-split-v");
40158 //var size = config.initialSize || config.height;
40159 //if(this.el && typeof size != "undefined"){
40160 // this.el.setHeight(size);
40163 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40165 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40168 onRender : function(ctr, pos)
40170 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40171 var size = this.config.initialSize || this.config.height;
40172 if(this.el && typeof size != "undefined"){
40173 this.el.setHeight(size);
40178 getBox : function(){
40179 if(this.collapsed){
40180 return this.collapsedEl.getBox();
40182 var box = this.el.getBox();
40184 box.height += this.split.el.getHeight();
40189 updateBox : function(box){
40190 if(this.split && !this.collapsed){
40191 box.height -= this.split.el.getHeight();
40192 this.split.el.setLeft(box.x);
40193 this.split.el.setTop(box.y+box.height);
40194 this.split.el.setWidth(box.width);
40196 if(this.collapsed){
40197 this.updateBody(box.width, null);
40199 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40207 Roo.bootstrap.layout.South = function(config){
40208 config.region = 'south';
40209 config.cursor = 's-resize';
40210 Roo.bootstrap.layout.Split.call(this, config);
40212 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40213 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40214 this.split.el.addClass("roo-layout-split-v");
40219 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40220 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40222 onRender : function(ctr, pos)
40224 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40225 var size = this.config.initialSize || this.config.height;
40226 if(this.el && typeof size != "undefined"){
40227 this.el.setHeight(size);
40232 getBox : function(){
40233 if(this.collapsed){
40234 return this.collapsedEl.getBox();
40236 var box = this.el.getBox();
40238 var sh = this.split.el.getHeight();
40245 updateBox : function(box){
40246 if(this.split && !this.collapsed){
40247 var sh = this.split.el.getHeight();
40250 this.split.el.setLeft(box.x);
40251 this.split.el.setTop(box.y-sh);
40252 this.split.el.setWidth(box.width);
40254 if(this.collapsed){
40255 this.updateBody(box.width, null);
40257 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40261 Roo.bootstrap.layout.East = function(config){
40262 config.region = "east";
40263 config.cursor = "e-resize";
40264 Roo.bootstrap.layout.Split.call(this, config);
40266 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40267 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40268 this.split.el.addClass("roo-layout-split-h");
40272 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40273 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40275 onRender : function(ctr, pos)
40277 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40278 var size = this.config.initialSize || this.config.width;
40279 if(this.el && typeof size != "undefined"){
40280 this.el.setWidth(size);
40285 getBox : function(){
40286 if(this.collapsed){
40287 return this.collapsedEl.getBox();
40289 var box = this.el.getBox();
40291 var sw = this.split.el.getWidth();
40298 updateBox : function(box){
40299 if(this.split && !this.collapsed){
40300 var sw = this.split.el.getWidth();
40302 this.split.el.setLeft(box.x);
40303 this.split.el.setTop(box.y);
40304 this.split.el.setHeight(box.height);
40307 if(this.collapsed){
40308 this.updateBody(null, box.height);
40310 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40314 Roo.bootstrap.layout.West = function(config){
40315 config.region = "west";
40316 config.cursor = "w-resize";
40318 Roo.bootstrap.layout.Split.call(this, config);
40320 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40321 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40322 this.split.el.addClass("roo-layout-split-h");
40326 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40327 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40329 onRender: function(ctr, pos)
40331 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40332 var size = this.config.initialSize || this.config.width;
40333 if(typeof size != "undefined"){
40334 this.el.setWidth(size);
40338 getBox : function(){
40339 if(this.collapsed){
40340 return this.collapsedEl.getBox();
40342 var box = this.el.getBox();
40343 if (box.width == 0) {
40344 box.width = this.config.width; // kludge?
40347 box.width += this.split.el.getWidth();
40352 updateBox : function(box){
40353 if(this.split && !this.collapsed){
40354 var sw = this.split.el.getWidth();
40356 this.split.el.setLeft(box.x+box.width);
40357 this.split.el.setTop(box.y);
40358 this.split.el.setHeight(box.height);
40360 if(this.collapsed){
40361 this.updateBody(null, box.height);
40363 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40365 });Roo.namespace("Roo.bootstrap.panel");/*
40367 * Ext JS Library 1.1.1
40368 * Copyright(c) 2006-2007, Ext JS, LLC.
40370 * Originally Released Under LGPL - original licence link has changed is not relivant.
40373 * <script type="text/javascript">
40376 * @class Roo.ContentPanel
40377 * @extends Roo.util.Observable
40378 * A basic ContentPanel element.
40379 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40380 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40381 * @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
40382 * @cfg {Boolean} closable True if the panel can be closed/removed
40383 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40384 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40385 * @cfg {Toolbar} toolbar A toolbar for this panel
40386 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40387 * @cfg {String} title The title for this panel
40388 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40389 * @cfg {String} url Calls {@link #setUrl} with this value
40390 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40391 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40392 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40393 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40394 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40395 * @cfg {Boolean} badges render the badges
40396 * @cfg {String} cls extra classes to use
40397 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40400 * Create a new ContentPanel.
40401 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40402 * @param {String/Object} config A string to set only the title or a config object
40403 * @param {String} content (optional) Set the HTML content for this panel
40404 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40406 Roo.bootstrap.panel.Content = function( config){
40408 this.tpl = config.tpl || false;
40410 var el = config.el;
40411 var content = config.content;
40413 if(config.autoCreate){ // xtype is available if this is called from factory
40416 this.el = Roo.get(el);
40417 if(!this.el && config && config.autoCreate){
40418 if(typeof config.autoCreate == "object"){
40419 if(!config.autoCreate.id){
40420 config.autoCreate.id = config.id||el;
40422 this.el = Roo.DomHelper.append(document.body,
40423 config.autoCreate, true);
40427 cls: (config.cls || '') +
40428 (config.background ? ' bg-' + config.background : '') +
40429 " roo-layout-inactive-content",
40432 if (config.iframe) {
40436 style : 'border: 0px',
40437 src : 'about:blank'
40443 elcfg.html = config.html;
40447 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40448 if (config.iframe) {
40449 this.iframeEl = this.el.select('iframe',true).first();
40454 this.closable = false;
40455 this.loaded = false;
40456 this.active = false;
40459 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40461 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40463 this.wrapEl = this.el; //this.el.wrap();
40465 if (config.toolbar.items) {
40466 ti = config.toolbar.items ;
40467 delete config.toolbar.items ;
40471 this.toolbar.render(this.wrapEl, 'before');
40472 for(var i =0;i < ti.length;i++) {
40473 // Roo.log(['add child', items[i]]);
40474 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40476 this.toolbar.items = nitems;
40477 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40478 delete config.toolbar;
40482 // xtype created footer. - not sure if will work as we normally have to render first..
40483 if (this.footer && !this.footer.el && this.footer.xtype) {
40484 if (!this.wrapEl) {
40485 this.wrapEl = this.el.wrap();
40488 this.footer.container = this.wrapEl.createChild();
40490 this.footer = Roo.factory(this.footer, Roo);
40495 if(typeof config == "string"){
40496 this.title = config;
40498 Roo.apply(this, config);
40502 this.resizeEl = Roo.get(this.resizeEl, true);
40504 this.resizeEl = this.el;
40506 // handle view.xtype
40514 * Fires when this panel is activated.
40515 * @param {Roo.ContentPanel} this
40519 * @event deactivate
40520 * Fires when this panel is activated.
40521 * @param {Roo.ContentPanel} this
40523 "deactivate" : true,
40527 * Fires when this panel is resized if fitToFrame is true.
40528 * @param {Roo.ContentPanel} this
40529 * @param {Number} width The width after any component adjustments
40530 * @param {Number} height The height after any component adjustments
40536 * Fires when this tab is created
40537 * @param {Roo.ContentPanel} this
40543 * Fires when this content is scrolled
40544 * @param {Roo.ContentPanel} this
40545 * @param {Event} scrollEvent
40556 if(this.autoScroll && !this.iframe){
40557 this.resizeEl.setStyle("overflow", "auto");
40558 this.resizeEl.on('scroll', this.onScroll, this);
40560 // fix randome scrolling
40561 //this.el.on('scroll', function() {
40562 // Roo.log('fix random scolling');
40563 // this.scrollTo('top',0);
40566 content = content || this.content;
40568 this.setContent(content);
40570 if(config && config.url){
40571 this.setUrl(this.url, this.params, this.loadOnce);
40576 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40578 if (this.view && typeof(this.view.xtype) != 'undefined') {
40579 this.view.el = this.el.appendChild(document.createElement("div"));
40580 this.view = Roo.factory(this.view);
40581 this.view.render && this.view.render(false, '');
40585 this.fireEvent('render', this);
40588 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40598 /* Resize Element - use this to work out scroll etc. */
40601 setRegion : function(region){
40602 this.region = region;
40603 this.setActiveClass(region && !this.background);
40607 setActiveClass: function(state)
40610 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40611 this.el.setStyle('position','relative');
40613 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40614 this.el.setStyle('position', 'absolute');
40619 * Returns the toolbar for this Panel if one was configured.
40620 * @return {Roo.Toolbar}
40622 getToolbar : function(){
40623 return this.toolbar;
40626 setActiveState : function(active)
40628 this.active = active;
40629 this.setActiveClass(active);
40631 if(this.fireEvent("deactivate", this) === false){
40636 this.fireEvent("activate", this);
40640 * Updates this panel's element (not for iframe)
40641 * @param {String} content The new content
40642 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40644 setContent : function(content, loadScripts){
40649 this.el.update(content, loadScripts);
40652 ignoreResize : function(w, h){
40653 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40656 this.lastSize = {width: w, height: h};
40661 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40662 * @return {Roo.UpdateManager} The UpdateManager
40664 getUpdateManager : function(){
40668 return this.el.getUpdateManager();
40671 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40672 * Does not work with IFRAME contents
40673 * @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:
40676 url: "your-url.php",
40677 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40678 callback: yourFunction,
40679 scope: yourObject, //(optional scope)
40682 text: "Loading...",
40688 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40689 * 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.
40690 * @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}
40691 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40692 * @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.
40693 * @return {Roo.ContentPanel} this
40701 var um = this.el.getUpdateManager();
40702 um.update.apply(um, arguments);
40708 * 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.
40709 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40710 * @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)
40711 * @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)
40712 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40714 setUrl : function(url, params, loadOnce){
40716 this.iframeEl.dom.src = url;
40720 if(this.refreshDelegate){
40721 this.removeListener("activate", this.refreshDelegate);
40723 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40724 this.on("activate", this.refreshDelegate);
40725 return this.el.getUpdateManager();
40728 _handleRefresh : function(url, params, loadOnce){
40729 if(!loadOnce || !this.loaded){
40730 var updater = this.el.getUpdateManager();
40731 updater.update(url, params, this._setLoaded.createDelegate(this));
40735 _setLoaded : function(){
40736 this.loaded = true;
40740 * Returns this panel's id
40743 getId : function(){
40748 * Returns this panel's element - used by regiosn to add.
40749 * @return {Roo.Element}
40751 getEl : function(){
40752 return this.wrapEl || this.el;
40757 adjustForComponents : function(width, height)
40759 //Roo.log('adjustForComponents ');
40760 if(this.resizeEl != this.el){
40761 width -= this.el.getFrameWidth('lr');
40762 height -= this.el.getFrameWidth('tb');
40765 var te = this.toolbar.getEl();
40766 te.setWidth(width);
40767 height -= te.getHeight();
40770 var te = this.footer.getEl();
40771 te.setWidth(width);
40772 height -= te.getHeight();
40776 if(this.adjustments){
40777 width += this.adjustments[0];
40778 height += this.adjustments[1];
40780 return {"width": width, "height": height};
40783 setSize : function(width, height){
40784 if(this.fitToFrame && !this.ignoreResize(width, height)){
40785 if(this.fitContainer && this.resizeEl != this.el){
40786 this.el.setSize(width, height);
40788 var size = this.adjustForComponents(width, height);
40790 this.iframeEl.setSize(width,height);
40793 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40794 this.fireEvent('resize', this, size.width, size.height);
40801 * Returns this panel's title
40804 getTitle : function(){
40806 if (typeof(this.title) != 'object') {
40811 for (var k in this.title) {
40812 if (!this.title.hasOwnProperty(k)) {
40816 if (k.indexOf('-') >= 0) {
40817 var s = k.split('-');
40818 for (var i = 0; i<s.length; i++) {
40819 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40822 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40829 * Set this panel's title
40830 * @param {String} title
40832 setTitle : function(title){
40833 this.title = title;
40835 this.region.updatePanelTitle(this, title);
40840 * Returns true is this panel was configured to be closable
40841 * @return {Boolean}
40843 isClosable : function(){
40844 return this.closable;
40847 beforeSlide : function(){
40849 this.resizeEl.clip();
40852 afterSlide : function(){
40854 this.resizeEl.unclip();
40858 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40859 * Will fail silently if the {@link #setUrl} method has not been called.
40860 * This does not activate the panel, just updates its content.
40862 refresh : function(){
40863 if(this.refreshDelegate){
40864 this.loaded = false;
40865 this.refreshDelegate();
40870 * Destroys this panel
40872 destroy : function(){
40873 this.el.removeAllListeners();
40874 var tempEl = document.createElement("span");
40875 tempEl.appendChild(this.el.dom);
40876 tempEl.innerHTML = "";
40882 * form - if the content panel contains a form - this is a reference to it.
40883 * @type {Roo.form.Form}
40887 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40888 * This contains a reference to it.
40894 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40904 * @param {Object} cfg Xtype definition of item to add.
40908 getChildContainer: function () {
40909 return this.getEl();
40913 onScroll : function(e)
40915 this.fireEvent('scroll', this, e);
40920 var ret = new Roo.factory(cfg);
40925 if (cfg.xtype.match(/^Form$/)) {
40928 //if (this.footer) {
40929 // el = this.footer.container.insertSibling(false, 'before');
40931 el = this.el.createChild();
40934 this.form = new Roo.form.Form(cfg);
40937 if ( this.form.allItems.length) {
40938 this.form.render(el.dom);
40942 // should only have one of theses..
40943 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40944 // views.. should not be just added - used named prop 'view''
40946 cfg.el = this.el.appendChild(document.createElement("div"));
40949 var ret = new Roo.factory(cfg);
40951 ret.render && ret.render(false, ''); // render blank..
40961 * @class Roo.bootstrap.panel.Grid
40962 * @extends Roo.bootstrap.panel.Content
40964 * Create a new GridPanel.
40965 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40966 * @param {Object} config A the config object
40972 Roo.bootstrap.panel.Grid = function(config)
40976 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40977 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40979 config.el = this.wrapper;
40980 //this.el = this.wrapper;
40982 if (config.container) {
40983 // ctor'ed from a Border/panel.grid
40986 this.wrapper.setStyle("overflow", "hidden");
40987 this.wrapper.addClass('roo-grid-container');
40992 if(config.toolbar){
40993 var tool_el = this.wrapper.createChild();
40994 this.toolbar = Roo.factory(config.toolbar);
40996 if (config.toolbar.items) {
40997 ti = config.toolbar.items ;
40998 delete config.toolbar.items ;
41002 this.toolbar.render(tool_el);
41003 for(var i =0;i < ti.length;i++) {
41004 // Roo.log(['add child', items[i]]);
41005 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41007 this.toolbar.items = nitems;
41009 delete config.toolbar;
41012 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41013 config.grid.scrollBody = true;;
41014 config.grid.monitorWindowResize = false; // turn off autosizing
41015 config.grid.autoHeight = false;
41016 config.grid.autoWidth = false;
41018 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41020 if (config.background) {
41021 // render grid on panel activation (if panel background)
41022 this.on('activate', function(gp) {
41023 if (!gp.grid.rendered) {
41024 gp.grid.render(this.wrapper);
41025 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41030 this.grid.render(this.wrapper);
41031 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41034 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41035 // ??? needed ??? config.el = this.wrapper;
41040 // xtype created footer. - not sure if will work as we normally have to render first..
41041 if (this.footer && !this.footer.el && this.footer.xtype) {
41043 var ctr = this.grid.getView().getFooterPanel(true);
41044 this.footer.dataSource = this.grid.dataSource;
41045 this.footer = Roo.factory(this.footer, Roo);
41046 this.footer.render(ctr);
41056 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41057 getId : function(){
41058 return this.grid.id;
41062 * Returns the grid for this panel
41063 * @return {Roo.bootstrap.Table}
41065 getGrid : function(){
41069 setSize : function(width, height){
41070 if(!this.ignoreResize(width, height)){
41071 var grid = this.grid;
41072 var size = this.adjustForComponents(width, height);
41073 // tfoot is not a footer?
41076 var gridel = grid.getGridEl();
41077 gridel.setSize(size.width, size.height);
41079 var tbd = grid.getGridEl().select('tbody', true).first();
41080 var thd = grid.getGridEl().select('thead',true).first();
41081 var tbf= grid.getGridEl().select('tfoot', true).first();
41084 size.height -= tbf.getHeight();
41087 size.height -= thd.getHeight();
41090 tbd.setSize(size.width, size.height );
41091 // this is for the account management tab -seems to work there.
41092 var thd = grid.getGridEl().select('thead',true).first();
41094 // tbd.setSize(size.width, size.height - thd.getHeight());
41103 beforeSlide : function(){
41104 this.grid.getView().scroller.clip();
41107 afterSlide : function(){
41108 this.grid.getView().scroller.unclip();
41111 destroy : function(){
41112 this.grid.destroy();
41114 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41119 * @class Roo.bootstrap.panel.Nest
41120 * @extends Roo.bootstrap.panel.Content
41122 * Create a new Panel, that can contain a layout.Border.
41125 * @param {Roo.BorderLayout} layout The layout for this panel
41126 * @param {String/Object} config A string to set only the title or a config object
41128 Roo.bootstrap.panel.Nest = function(config)
41130 // construct with only one argument..
41131 /* FIXME - implement nicer consturctors
41132 if (layout.layout) {
41134 layout = config.layout;
41135 delete config.layout;
41137 if (layout.xtype && !layout.getEl) {
41138 // then layout needs constructing..
41139 layout = Roo.factory(layout, Roo);
41143 config.el = config.layout.getEl();
41145 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41147 config.layout.monitorWindowResize = false; // turn off autosizing
41148 this.layout = config.layout;
41149 this.layout.getEl().addClass("roo-layout-nested-layout");
41150 this.layout.parent = this;
41157 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41159 setSize : function(width, height){
41160 if(!this.ignoreResize(width, height)){
41161 var size = this.adjustForComponents(width, height);
41162 var el = this.layout.getEl();
41163 if (size.height < 1) {
41164 el.setWidth(size.width);
41166 el.setSize(size.width, size.height);
41168 var touch = el.dom.offsetWidth;
41169 this.layout.layout();
41170 // ie requires a double layout on the first pass
41171 if(Roo.isIE && !this.initialized){
41172 this.initialized = true;
41173 this.layout.layout();
41178 // activate all subpanels if not currently active..
41180 setActiveState : function(active){
41181 this.active = active;
41182 this.setActiveClass(active);
41185 this.fireEvent("deactivate", this);
41189 this.fireEvent("activate", this);
41190 // not sure if this should happen before or after..
41191 if (!this.layout) {
41192 return; // should not happen..
41195 for (var r in this.layout.regions) {
41196 reg = this.layout.getRegion(r);
41197 if (reg.getActivePanel()) {
41198 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41199 reg.setActivePanel(reg.getActivePanel());
41202 if (!reg.panels.length) {
41205 reg.showPanel(reg.getPanel(0));
41214 * Returns the nested BorderLayout for this panel
41215 * @return {Roo.BorderLayout}
41217 getLayout : function(){
41218 return this.layout;
41222 * Adds a xtype elements to the layout of the nested panel
41226 xtype : 'ContentPanel',
41233 xtype : 'NestedLayoutPanel',
41239 items : [ ... list of content panels or nested layout panels.. ]
41243 * @param {Object} cfg Xtype definition of item to add.
41245 addxtype : function(cfg) {
41246 return this.layout.addxtype(cfg);
41251 * Ext JS Library 1.1.1
41252 * Copyright(c) 2006-2007, Ext JS, LLC.
41254 * Originally Released Under LGPL - original licence link has changed is not relivant.
41257 * <script type="text/javascript">
41260 * @class Roo.TabPanel
41261 * @extends Roo.util.Observable
41262 * A lightweight tab container.
41266 // basic tabs 1, built from existing content
41267 var tabs = new Roo.TabPanel("tabs1");
41268 tabs.addTab("script", "View Script");
41269 tabs.addTab("markup", "View Markup");
41270 tabs.activate("script");
41272 // more advanced tabs, built from javascript
41273 var jtabs = new Roo.TabPanel("jtabs");
41274 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41276 // set up the UpdateManager
41277 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41278 var updater = tab2.getUpdateManager();
41279 updater.setDefaultUrl("ajax1.htm");
41280 tab2.on('activate', updater.refresh, updater, true);
41282 // Use setUrl for Ajax loading
41283 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41284 tab3.setUrl("ajax2.htm", null, true);
41287 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41290 jtabs.activate("jtabs-1");
41293 * Create a new TabPanel.
41294 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41295 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41297 Roo.bootstrap.panel.Tabs = function(config){
41299 * The container element for this TabPanel.
41300 * @type Roo.Element
41302 this.el = Roo.get(config.el);
41305 if(typeof config == "boolean"){
41306 this.tabPosition = config ? "bottom" : "top";
41308 Roo.apply(this, config);
41312 if(this.tabPosition == "bottom"){
41313 // if tabs are at the bottom = create the body first.
41314 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41315 this.el.addClass("roo-tabs-bottom");
41317 // next create the tabs holders
41319 if (this.tabPosition == "west"){
41321 var reg = this.region; // fake it..
41323 if (!reg.mgr.parent) {
41326 reg = reg.mgr.parent.region;
41328 Roo.log("got nest?");
41330 if (reg.mgr.getRegion('west')) {
41331 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41332 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41333 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41334 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41335 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41343 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41344 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41345 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41346 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41351 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41354 // finally - if tabs are at the top, then create the body last..
41355 if(this.tabPosition != "bottom"){
41356 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41357 * @type Roo.Element
41359 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41360 this.el.addClass("roo-tabs-top");
41364 this.bodyEl.setStyle("position", "relative");
41366 this.active = null;
41367 this.activateDelegate = this.activate.createDelegate(this);
41372 * Fires when the active tab changes
41373 * @param {Roo.TabPanel} this
41374 * @param {Roo.TabPanelItem} activePanel The new active tab
41378 * @event beforetabchange
41379 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41380 * @param {Roo.TabPanel} this
41381 * @param {Object} e Set cancel to true on this object to cancel the tab change
41382 * @param {Roo.TabPanelItem} tab The tab being changed to
41384 "beforetabchange" : true
41387 Roo.EventManager.onWindowResize(this.onResize, this);
41388 this.cpad = this.el.getPadding("lr");
41389 this.hiddenCount = 0;
41392 // toolbar on the tabbar support...
41393 if (this.toolbar) {
41394 alert("no toolbar support yet");
41395 this.toolbar = false;
41397 var tcfg = this.toolbar;
41398 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41399 this.toolbar = new Roo.Toolbar(tcfg);
41400 if (Roo.isSafari) {
41401 var tbl = tcfg.container.child('table', true);
41402 tbl.setAttribute('width', '100%');
41410 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41413 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41415 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41417 tabPosition : "top",
41419 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41421 currentTabWidth : 0,
41423 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41427 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41431 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41433 preferredTabWidth : 175,
41435 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41437 resizeTabs : false,
41439 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41441 monitorResize : true,
41443 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41445 toolbar : false, // set by caller..
41447 region : false, /// set by caller
41449 disableTooltips : true, // not used yet...
41452 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41453 * @param {String} id The id of the div to use <b>or create</b>
41454 * @param {String} text The text for the tab
41455 * @param {String} content (optional) Content to put in the TabPanelItem body
41456 * @param {Boolean} closable (optional) True to create a close icon on the tab
41457 * @return {Roo.TabPanelItem} The created TabPanelItem
41459 addTab : function(id, text, content, closable, tpl)
41461 var item = new Roo.bootstrap.panel.TabItem({
41465 closable : closable,
41468 this.addTabItem(item);
41470 item.setContent(content);
41476 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41477 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41478 * @return {Roo.TabPanelItem}
41480 getTab : function(id){
41481 return this.items[id];
41485 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41486 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41488 hideTab : function(id){
41489 var t = this.items[id];
41492 this.hiddenCount++;
41493 this.autoSizeTabs();
41498 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41499 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41501 unhideTab : function(id){
41502 var t = this.items[id];
41504 t.setHidden(false);
41505 this.hiddenCount--;
41506 this.autoSizeTabs();
41511 * Adds an existing {@link Roo.TabPanelItem}.
41512 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41514 addTabItem : function(item)
41516 this.items[item.id] = item;
41517 this.items.push(item);
41518 this.autoSizeTabs();
41519 // if(this.resizeTabs){
41520 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41521 // this.autoSizeTabs();
41523 // item.autoSize();
41528 * Removes a {@link Roo.TabPanelItem}.
41529 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41531 removeTab : function(id){
41532 var items = this.items;
41533 var tab = items[id];
41534 if(!tab) { return; }
41535 var index = items.indexOf(tab);
41536 if(this.active == tab && items.length > 1){
41537 var newTab = this.getNextAvailable(index);
41542 this.stripEl.dom.removeChild(tab.pnode.dom);
41543 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41544 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41546 items.splice(index, 1);
41547 delete this.items[tab.id];
41548 tab.fireEvent("close", tab);
41549 tab.purgeListeners();
41550 this.autoSizeTabs();
41553 getNextAvailable : function(start){
41554 var items = this.items;
41556 // look for a next tab that will slide over to
41557 // replace the one being removed
41558 while(index < items.length){
41559 var item = items[++index];
41560 if(item && !item.isHidden()){
41564 // if one isn't found select the previous tab (on the left)
41567 var item = items[--index];
41568 if(item && !item.isHidden()){
41576 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41577 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41579 disableTab : function(id){
41580 var tab = this.items[id];
41581 if(tab && this.active != tab){
41587 * Enables a {@link Roo.TabPanelItem} that is disabled.
41588 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41590 enableTab : function(id){
41591 var tab = this.items[id];
41596 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41597 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41598 * @return {Roo.TabPanelItem} The TabPanelItem.
41600 activate : function(id)
41602 //Roo.log('activite:' + id);
41604 var tab = this.items[id];
41608 if(tab == this.active || tab.disabled){
41612 this.fireEvent("beforetabchange", this, e, tab);
41613 if(e.cancel !== true && !tab.disabled){
41615 this.active.hide();
41617 this.active = this.items[id];
41618 this.active.show();
41619 this.fireEvent("tabchange", this, this.active);
41625 * Gets the active {@link Roo.TabPanelItem}.
41626 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41628 getActiveTab : function(){
41629 return this.active;
41633 * Updates the tab body element to fit the height of the container element
41634 * for overflow scrolling
41635 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41637 syncHeight : function(targetHeight){
41638 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41639 var bm = this.bodyEl.getMargins();
41640 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41641 this.bodyEl.setHeight(newHeight);
41645 onResize : function(){
41646 if(this.monitorResize){
41647 this.autoSizeTabs();
41652 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41654 beginUpdate : function(){
41655 this.updating = true;
41659 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41661 endUpdate : function(){
41662 this.updating = false;
41663 this.autoSizeTabs();
41667 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41669 autoSizeTabs : function()
41671 var count = this.items.length;
41672 var vcount = count - this.hiddenCount;
41675 this.stripEl.hide();
41677 this.stripEl.show();
41680 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41685 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41686 var availWidth = Math.floor(w / vcount);
41687 var b = this.stripBody;
41688 if(b.getWidth() > w){
41689 var tabs = this.items;
41690 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41691 if(availWidth < this.minTabWidth){
41692 /*if(!this.sleft){ // incomplete scrolling code
41693 this.createScrollButtons();
41696 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41699 if(this.currentTabWidth < this.preferredTabWidth){
41700 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41706 * Returns the number of tabs in this TabPanel.
41709 getCount : function(){
41710 return this.items.length;
41714 * Resizes all the tabs to the passed width
41715 * @param {Number} The new width
41717 setTabWidth : function(width){
41718 this.currentTabWidth = width;
41719 for(var i = 0, len = this.items.length; i < len; i++) {
41720 if(!this.items[i].isHidden()) {
41721 this.items[i].setWidth(width);
41727 * Destroys this TabPanel
41728 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41730 destroy : function(removeEl){
41731 Roo.EventManager.removeResizeListener(this.onResize, this);
41732 for(var i = 0, len = this.items.length; i < len; i++){
41733 this.items[i].purgeListeners();
41735 if(removeEl === true){
41736 this.el.update("");
41741 createStrip : function(container)
41743 var strip = document.createElement("nav");
41744 strip.className = Roo.bootstrap.version == 4 ?
41745 "navbar-light bg-light" :
41746 "navbar navbar-default"; //"x-tabs-wrap";
41747 container.appendChild(strip);
41751 createStripList : function(strip)
41753 // div wrapper for retard IE
41754 // returns the "tr" element.
41755 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41756 //'<div class="x-tabs-strip-wrap">'+
41757 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41758 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41759 return strip.firstChild; //.firstChild.firstChild.firstChild;
41761 createBody : function(container)
41763 var body = document.createElement("div");
41764 Roo.id(body, "tab-body");
41765 //Roo.fly(body).addClass("x-tabs-body");
41766 Roo.fly(body).addClass("tab-content");
41767 container.appendChild(body);
41770 createItemBody :function(bodyEl, id){
41771 var body = Roo.getDom(id);
41773 body = document.createElement("div");
41776 //Roo.fly(body).addClass("x-tabs-item-body");
41777 Roo.fly(body).addClass("tab-pane");
41778 bodyEl.insertBefore(body, bodyEl.firstChild);
41782 createStripElements : function(stripEl, text, closable, tpl)
41784 var td = document.createElement("li"); // was td..
41785 td.className = 'nav-item';
41787 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41790 stripEl.appendChild(td);
41792 td.className = "x-tabs-closable";
41793 if(!this.closeTpl){
41794 this.closeTpl = new Roo.Template(
41795 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41796 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41797 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41800 var el = this.closeTpl.overwrite(td, {"text": text});
41801 var close = el.getElementsByTagName("div")[0];
41802 var inner = el.getElementsByTagName("em")[0];
41803 return {"el": el, "close": close, "inner": inner};
41806 // not sure what this is..
41807 // if(!this.tabTpl){
41808 //this.tabTpl = new Roo.Template(
41809 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41810 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41812 // this.tabTpl = new Roo.Template(
41813 // '<a href="#">' +
41814 // '<span unselectable="on"' +
41815 // (this.disableTooltips ? '' : ' title="{text}"') +
41816 // ' >{text}</span></a>'
41822 var template = tpl || this.tabTpl || false;
41825 template = new Roo.Template(
41826 Roo.bootstrap.version == 4 ?
41828 '<a class="nav-link" href="#" unselectable="on"' +
41829 (this.disableTooltips ? '' : ' title="{text}"') +
41832 '<a class="nav-link" href="#">' +
41833 '<span unselectable="on"' +
41834 (this.disableTooltips ? '' : ' title="{text}"') +
41835 ' >{text}</span></a>'
41840 switch (typeof(template)) {
41844 template = new Roo.Template(template);
41850 var el = template.overwrite(td, {"text": text});
41852 var inner = el.getElementsByTagName("span")[0];
41854 return {"el": el, "inner": inner};
41862 * @class Roo.TabPanelItem
41863 * @extends Roo.util.Observable
41864 * Represents an individual item (tab plus body) in a TabPanel.
41865 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41866 * @param {String} id The id of this TabPanelItem
41867 * @param {String} text The text for the tab of this TabPanelItem
41868 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41870 Roo.bootstrap.panel.TabItem = function(config){
41872 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41873 * @type Roo.TabPanel
41875 this.tabPanel = config.panel;
41877 * The id for this TabPanelItem
41880 this.id = config.id;
41882 this.disabled = false;
41884 this.text = config.text;
41886 this.loaded = false;
41887 this.closable = config.closable;
41890 * The body element for this TabPanelItem.
41891 * @type Roo.Element
41893 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41894 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41895 this.bodyEl.setStyle("display", "block");
41896 this.bodyEl.setStyle("zoom", "1");
41897 //this.hideAction();
41899 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41901 this.el = Roo.get(els.el);
41902 this.inner = Roo.get(els.inner, true);
41903 this.textEl = Roo.bootstrap.version == 4 ?
41904 this.el : Roo.get(this.el.dom.firstChild, true);
41906 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41907 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41910 // this.el.on("mousedown", this.onTabMouseDown, this);
41911 this.el.on("click", this.onTabClick, this);
41913 if(config.closable){
41914 var c = Roo.get(els.close, true);
41915 c.dom.title = this.closeText;
41916 c.addClassOnOver("close-over");
41917 c.on("click", this.closeClick, this);
41923 * Fires when this tab becomes the active tab.
41924 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41925 * @param {Roo.TabPanelItem} this
41929 * @event beforeclose
41930 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41931 * @param {Roo.TabPanelItem} this
41932 * @param {Object} e Set cancel to true on this object to cancel the close.
41934 "beforeclose": true,
41937 * Fires when this tab is closed.
41938 * @param {Roo.TabPanelItem} this
41942 * @event deactivate
41943 * Fires when this tab is no longer the active tab.
41944 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41945 * @param {Roo.TabPanelItem} this
41947 "deactivate" : true
41949 this.hidden = false;
41951 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41954 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41956 purgeListeners : function(){
41957 Roo.util.Observable.prototype.purgeListeners.call(this);
41958 this.el.removeAllListeners();
41961 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41964 this.status_node.addClass("active");
41967 this.tabPanel.stripWrap.repaint();
41969 this.fireEvent("activate", this.tabPanel, this);
41973 * Returns true if this tab is the active tab.
41974 * @return {Boolean}
41976 isActive : function(){
41977 return this.tabPanel.getActiveTab() == this;
41981 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41984 this.status_node.removeClass("active");
41986 this.fireEvent("deactivate", this.tabPanel, this);
41989 hideAction : function(){
41990 this.bodyEl.hide();
41991 this.bodyEl.setStyle("position", "absolute");
41992 this.bodyEl.setLeft("-20000px");
41993 this.bodyEl.setTop("-20000px");
41996 showAction : function(){
41997 this.bodyEl.setStyle("position", "relative");
41998 this.bodyEl.setTop("");
41999 this.bodyEl.setLeft("");
42000 this.bodyEl.show();
42004 * Set the tooltip for the tab.
42005 * @param {String} tooltip The tab's tooltip
42007 setTooltip : function(text){
42008 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42009 this.textEl.dom.qtip = text;
42010 this.textEl.dom.removeAttribute('title');
42012 this.textEl.dom.title = text;
42016 onTabClick : function(e){
42017 e.preventDefault();
42018 this.tabPanel.activate(this.id);
42021 onTabMouseDown : function(e){
42022 e.preventDefault();
42023 this.tabPanel.activate(this.id);
42026 getWidth : function(){
42027 return this.inner.getWidth();
42030 setWidth : function(width){
42031 var iwidth = width - this.linode.getPadding("lr");
42032 this.inner.setWidth(iwidth);
42033 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42034 this.linode.setWidth(width);
42038 * Show or hide the tab
42039 * @param {Boolean} hidden True to hide or false to show.
42041 setHidden : function(hidden){
42042 this.hidden = hidden;
42043 this.linode.setStyle("display", hidden ? "none" : "");
42047 * Returns true if this tab is "hidden"
42048 * @return {Boolean}
42050 isHidden : function(){
42051 return this.hidden;
42055 * Returns the text for this tab
42058 getText : function(){
42062 autoSize : function(){
42063 //this.el.beginMeasure();
42064 this.textEl.setWidth(1);
42066 * #2804 [new] Tabs in Roojs
42067 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42069 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42070 //this.el.endMeasure();
42074 * Sets the text for the tab (Note: this also sets the tooltip text)
42075 * @param {String} text The tab's text and tooltip
42077 setText : function(text){
42079 this.textEl.update(text);
42080 this.setTooltip(text);
42081 //if(!this.tabPanel.resizeTabs){
42082 // this.autoSize();
42086 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42088 activate : function(){
42089 this.tabPanel.activate(this.id);
42093 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42095 disable : function(){
42096 if(this.tabPanel.active != this){
42097 this.disabled = true;
42098 this.status_node.addClass("disabled");
42103 * Enables this TabPanelItem if it was previously disabled.
42105 enable : function(){
42106 this.disabled = false;
42107 this.status_node.removeClass("disabled");
42111 * Sets the content for this TabPanelItem.
42112 * @param {String} content The content
42113 * @param {Boolean} loadScripts true to look for and load scripts
42115 setContent : function(content, loadScripts){
42116 this.bodyEl.update(content, loadScripts);
42120 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42121 * @return {Roo.UpdateManager} The UpdateManager
42123 getUpdateManager : function(){
42124 return this.bodyEl.getUpdateManager();
42128 * Set a URL to be used to load the content for this TabPanelItem.
42129 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42130 * @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)
42131 * @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)
42132 * @return {Roo.UpdateManager} The UpdateManager
42134 setUrl : function(url, params, loadOnce){
42135 if(this.refreshDelegate){
42136 this.un('activate', this.refreshDelegate);
42138 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42139 this.on("activate", this.refreshDelegate);
42140 return this.bodyEl.getUpdateManager();
42144 _handleRefresh : function(url, params, loadOnce){
42145 if(!loadOnce || !this.loaded){
42146 var updater = this.bodyEl.getUpdateManager();
42147 updater.update(url, params, this._setLoaded.createDelegate(this));
42152 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42153 * Will fail silently if the setUrl method has not been called.
42154 * This does not activate the panel, just updates its content.
42156 refresh : function(){
42157 if(this.refreshDelegate){
42158 this.loaded = false;
42159 this.refreshDelegate();
42164 _setLoaded : function(){
42165 this.loaded = true;
42169 closeClick : function(e){
42172 this.fireEvent("beforeclose", this, o);
42173 if(o.cancel !== true){
42174 this.tabPanel.removeTab(this.id);
42178 * The text displayed in the tooltip for the close icon.
42181 closeText : "Close this tab"
42184 * This script refer to:
42185 * Title: International Telephone Input
42186 * Author: Jack O'Connor
42187 * Code version: v12.1.12
42188 * Availability: https://github.com/jackocnr/intl-tel-input.git
42191 Roo.bootstrap.PhoneInputData = function() {
42194 "Afghanistan (افغانستان)",
42199 "Albania (Shqipëri)",
42204 "Algeria (الجزائر)",
42229 "Antigua and Barbuda",
42239 "Armenia (Հայաստան)",
42255 "Austria (Österreich)",
42260 "Azerbaijan (Azərbaycan)",
42270 "Bahrain (البحرين)",
42275 "Bangladesh (বাংলাদেশ)",
42285 "Belarus (Беларусь)",
42290 "Belgium (België)",
42320 "Bosnia and Herzegovina (Босна и Херцеговина)",
42335 "British Indian Ocean Territory",
42340 "British Virgin Islands",
42350 "Bulgaria (България)",
42360 "Burundi (Uburundi)",
42365 "Cambodia (កម្ពុជា)",
42370 "Cameroon (Cameroun)",
42379 ["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"]
42382 "Cape Verde (Kabu Verdi)",
42387 "Caribbean Netherlands",
42398 "Central African Republic (République centrafricaine)",
42418 "Christmas Island",
42424 "Cocos (Keeling) Islands",
42435 "Comoros (جزر القمر)",
42440 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42445 "Congo (Republic) (Congo-Brazzaville)",
42465 "Croatia (Hrvatska)",
42486 "Czech Republic (Česká republika)",
42491 "Denmark (Danmark)",
42506 "Dominican Republic (República Dominicana)",
42510 ["809", "829", "849"]
42528 "Equatorial Guinea (Guinea Ecuatorial)",
42548 "Falkland Islands (Islas Malvinas)",
42553 "Faroe Islands (Føroyar)",
42574 "French Guiana (Guyane française)",
42579 "French Polynesia (Polynésie française)",
42594 "Georgia (საქართველო)",
42599 "Germany (Deutschland)",
42619 "Greenland (Kalaallit Nunaat)",
42656 "Guinea-Bissau (Guiné Bissau)",
42681 "Hungary (Magyarország)",
42686 "Iceland (Ísland)",
42706 "Iraq (العراق)",
42722 "Israel (ישראל)",
42749 "Jordan (الأردن)",
42754 "Kazakhstan (Казахстан)",
42775 "Kuwait (الكويت)",
42780 "Kyrgyzstan (Кыргызстан)",
42790 "Latvia (Latvija)",
42795 "Lebanon (لبنان)",
42810 "Libya (ليبيا)",
42820 "Lithuania (Lietuva)",
42835 "Macedonia (FYROM) (Македонија)",
42840 "Madagascar (Madagasikara)",
42870 "Marshall Islands",
42880 "Mauritania (موريتانيا)",
42885 "Mauritius (Moris)",
42906 "Moldova (Republica Moldova)",
42916 "Mongolia (Монгол)",
42921 "Montenegro (Crna Gora)",
42931 "Morocco (المغرب)",
42937 "Mozambique (Moçambique)",
42942 "Myanmar (Burma) (မြန်မာ)",
42947 "Namibia (Namibië)",
42962 "Netherlands (Nederland)",
42967 "New Caledonia (Nouvelle-Calédonie)",
43002 "North Korea (조선 민주주의 인민 공화국)",
43007 "Northern Mariana Islands",
43023 "Pakistan (پاکستان)",
43033 "Palestine (فلسطين)",
43043 "Papua New Guinea",
43085 "Réunion (La Réunion)",
43091 "Romania (România)",
43107 "Saint Barthélemy",
43118 "Saint Kitts and Nevis",
43128 "Saint Martin (Saint-Martin (partie française))",
43134 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43139 "Saint Vincent and the Grenadines",
43154 "São Tomé and Príncipe (São Tomé e Príncipe)",
43159 "Saudi Arabia (المملكة العربية السعودية)",
43164 "Senegal (Sénégal)",
43194 "Slovakia (Slovensko)",
43199 "Slovenia (Slovenija)",
43209 "Somalia (Soomaaliya)",
43219 "South Korea (대한민국)",
43224 "South Sudan (جنوب السودان)",
43234 "Sri Lanka (ශ්රී ලංකාව)",
43239 "Sudan (السودان)",
43249 "Svalbard and Jan Mayen",
43260 "Sweden (Sverige)",
43265 "Switzerland (Schweiz)",
43270 "Syria (سوريا)",
43315 "Trinidad and Tobago",
43320 "Tunisia (تونس)",
43325 "Turkey (Türkiye)",
43335 "Turks and Caicos Islands",
43345 "U.S. Virgin Islands",
43355 "Ukraine (Україна)",
43360 "United Arab Emirates (الإمارات العربية المتحدة)",
43382 "Uzbekistan (Oʻzbekiston)",
43392 "Vatican City (Città del Vaticano)",
43403 "Vietnam (Việt Nam)",
43408 "Wallis and Futuna (Wallis-et-Futuna)",
43413 "Western Sahara (الصحراء الغربية)",
43419 "Yemen (اليمن)",
43443 * This script refer to:
43444 * Title: International Telephone Input
43445 * Author: Jack O'Connor
43446 * Code version: v12.1.12
43447 * Availability: https://github.com/jackocnr/intl-tel-input.git
43451 * @class Roo.bootstrap.PhoneInput
43452 * @extends Roo.bootstrap.TriggerField
43453 * An input with International dial-code selection
43455 * @cfg {String} defaultDialCode default '+852'
43456 * @cfg {Array} preferedCountries default []
43459 * Create a new PhoneInput.
43460 * @param {Object} config Configuration options
43463 Roo.bootstrap.PhoneInput = function(config) {
43464 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43467 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43469 listWidth: undefined,
43471 selectedClass: 'active',
43473 invalidClass : "has-warning",
43475 validClass: 'has-success',
43477 allowed: '0123456789',
43482 * @cfg {String} defaultDialCode The default dial code when initializing the input
43484 defaultDialCode: '+852',
43487 * @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
43489 preferedCountries: false,
43491 getAutoCreate : function()
43493 var data = Roo.bootstrap.PhoneInputData();
43494 var align = this.labelAlign || this.parentLabelAlign();
43497 this.allCountries = [];
43498 this.dialCodeMapping = [];
43500 for (var i = 0; i < data.length; i++) {
43502 this.allCountries[i] = {
43506 priority: c[3] || 0,
43507 areaCodes: c[4] || null
43509 this.dialCodeMapping[c[2]] = {
43512 priority: c[3] || 0,
43513 areaCodes: c[4] || null
43525 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43526 maxlength: this.max_length,
43527 cls : 'form-control tel-input',
43528 autocomplete: 'new-password'
43531 var hiddenInput = {
43534 cls: 'hidden-tel-input'
43538 hiddenInput.name = this.name;
43541 if (this.disabled) {
43542 input.disabled = true;
43545 var flag_container = {
43562 cls: this.hasFeedback ? 'has-feedback' : '',
43568 cls: 'dial-code-holder',
43575 cls: 'roo-select2-container input-group',
43582 if (this.fieldLabel.length) {
43585 tooltip: 'This field is required'
43591 cls: 'control-label',
43597 html: this.fieldLabel
43600 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43606 if(this.indicatorpos == 'right') {
43607 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43614 if(align == 'left') {
43622 if(this.labelWidth > 12){
43623 label.style = "width: " + this.labelWidth + 'px';
43625 if(this.labelWidth < 13 && this.labelmd == 0){
43626 this.labelmd = this.labelWidth;
43628 if(this.labellg > 0){
43629 label.cls += ' col-lg-' + this.labellg;
43630 input.cls += ' col-lg-' + (12 - this.labellg);
43632 if(this.labelmd > 0){
43633 label.cls += ' col-md-' + this.labelmd;
43634 container.cls += ' col-md-' + (12 - this.labelmd);
43636 if(this.labelsm > 0){
43637 label.cls += ' col-sm-' + this.labelsm;
43638 container.cls += ' col-sm-' + (12 - this.labelsm);
43640 if(this.labelxs > 0){
43641 label.cls += ' col-xs-' + this.labelxs;
43642 container.cls += ' col-xs-' + (12 - this.labelxs);
43652 var settings = this;
43654 ['xs','sm','md','lg'].map(function(size){
43655 if (settings[size]) {
43656 cfg.cls += ' col-' + size + '-' + settings[size];
43660 this.store = new Roo.data.Store({
43661 proxy : new Roo.data.MemoryProxy({}),
43662 reader : new Roo.data.JsonReader({
43673 'name' : 'dialCode',
43677 'name' : 'priority',
43681 'name' : 'areaCodes',
43688 if(!this.preferedCountries) {
43689 this.preferedCountries = [
43696 var p = this.preferedCountries.reverse();
43699 for (var i = 0; i < p.length; i++) {
43700 for (var j = 0; j < this.allCountries.length; j++) {
43701 if(this.allCountries[j].iso2 == p[i]) {
43702 var t = this.allCountries[j];
43703 this.allCountries.splice(j,1);
43704 this.allCountries.unshift(t);
43710 this.store.proxy.data = {
43712 data: this.allCountries
43718 initEvents : function()
43721 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43723 this.indicator = this.indicatorEl();
43724 this.flag = this.flagEl();
43725 this.dialCodeHolder = this.dialCodeHolderEl();
43727 this.trigger = this.el.select('div.flag-box',true).first();
43728 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43733 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43734 _this.list.setWidth(lw);
43737 this.list.on('mouseover', this.onViewOver, this);
43738 this.list.on('mousemove', this.onViewMove, this);
43739 this.inputEl().on("keyup", this.onKeyUp, this);
43740 this.inputEl().on("keypress", this.onKeyPress, this);
43742 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43744 this.view = new Roo.View(this.list, this.tpl, {
43745 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43748 this.view.on('click', this.onViewClick, this);
43749 this.setValue(this.defaultDialCode);
43752 onTriggerClick : function(e)
43754 Roo.log('trigger click');
43759 if(this.isExpanded()){
43761 this.hasFocus = false;
43763 this.store.load({});
43764 this.hasFocus = true;
43769 isExpanded : function()
43771 return this.list.isVisible();
43774 collapse : function()
43776 if(!this.isExpanded()){
43780 Roo.get(document).un('mousedown', this.collapseIf, this);
43781 Roo.get(document).un('mousewheel', this.collapseIf, this);
43782 this.fireEvent('collapse', this);
43786 expand : function()
43790 if(this.isExpanded() || !this.hasFocus){
43794 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43795 this.list.setWidth(lw);
43798 this.restrictHeight();
43800 Roo.get(document).on('mousedown', this.collapseIf, this);
43801 Roo.get(document).on('mousewheel', this.collapseIf, this);
43803 this.fireEvent('expand', this);
43806 restrictHeight : function()
43808 this.list.alignTo(this.inputEl(), this.listAlign);
43809 this.list.alignTo(this.inputEl(), this.listAlign);
43812 onViewOver : function(e, t)
43814 if(this.inKeyMode){
43817 var item = this.view.findItemFromChild(t);
43820 var index = this.view.indexOf(item);
43821 this.select(index, false);
43826 onViewClick : function(view, doFocus, el, e)
43828 var index = this.view.getSelectedIndexes()[0];
43830 var r = this.store.getAt(index);
43833 this.onSelect(r, index);
43835 if(doFocus !== false && !this.blockFocus){
43836 this.inputEl().focus();
43840 onViewMove : function(e, t)
43842 this.inKeyMode = false;
43845 select : function(index, scrollIntoView)
43847 this.selectedIndex = index;
43848 this.view.select(index);
43849 if(scrollIntoView !== false){
43850 var el = this.view.getNode(index);
43852 this.list.scrollChildIntoView(el, false);
43857 createList : function()
43859 this.list = Roo.get(document.body).createChild({
43861 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43862 style: 'display:none'
43865 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43868 collapseIf : function(e)
43870 var in_combo = e.within(this.el);
43871 var in_list = e.within(this.list);
43872 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43874 if (in_combo || in_list || is_list) {
43880 onSelect : function(record, index)
43882 if(this.fireEvent('beforeselect', this, record, index) !== false){
43884 this.setFlagClass(record.data.iso2);
43885 this.setDialCode(record.data.dialCode);
43886 this.hasFocus = false;
43888 this.fireEvent('select', this, record, index);
43892 flagEl : function()
43894 var flag = this.el.select('div.flag',true).first();
43901 dialCodeHolderEl : function()
43903 var d = this.el.select('input.dial-code-holder',true).first();
43910 setDialCode : function(v)
43912 this.dialCodeHolder.dom.value = '+'+v;
43915 setFlagClass : function(n)
43917 this.flag.dom.className = 'flag '+n;
43920 getValue : function()
43922 var v = this.inputEl().getValue();
43923 if(this.dialCodeHolder) {
43924 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43929 setValue : function(v)
43931 var d = this.getDialCode(v);
43933 //invalid dial code
43934 if(v.length == 0 || !d || d.length == 0) {
43936 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43937 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43943 this.setFlagClass(this.dialCodeMapping[d].iso2);
43944 this.setDialCode(d);
43945 this.inputEl().dom.value = v.replace('+'+d,'');
43946 this.hiddenEl().dom.value = this.getValue();
43951 getDialCode : function(v)
43955 if (v.length == 0) {
43956 return this.dialCodeHolder.dom.value;
43960 if (v.charAt(0) != "+") {
43963 var numericChars = "";
43964 for (var i = 1; i < v.length; i++) {
43965 var c = v.charAt(i);
43968 if (this.dialCodeMapping[numericChars]) {
43969 dialCode = v.substr(1, i);
43971 if (numericChars.length == 4) {
43981 this.setValue(this.defaultDialCode);
43985 hiddenEl : function()
43987 return this.el.select('input.hidden-tel-input',true).first();
43990 // after setting val
43991 onKeyUp : function(e){
43992 this.setValue(this.getValue());
43995 onKeyPress : function(e){
43996 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44003 * @class Roo.bootstrap.MoneyField
44004 * @extends Roo.bootstrap.ComboBox
44005 * Bootstrap MoneyField class
44008 * Create a new MoneyField.
44009 * @param {Object} config Configuration options
44012 Roo.bootstrap.MoneyField = function(config) {
44014 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44018 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44021 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44023 allowDecimals : true,
44025 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44027 decimalSeparator : ".",
44029 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44031 decimalPrecision : 0,
44033 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44035 allowNegative : true,
44037 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44041 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44043 minValue : Number.NEGATIVE_INFINITY,
44045 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44047 maxValue : Number.MAX_VALUE,
44049 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44051 minText : "The minimum value for this field is {0}",
44053 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44055 maxText : "The maximum value for this field is {0}",
44057 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44058 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44060 nanText : "{0} is not a valid number",
44062 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44066 * @cfg {String} defaults currency of the MoneyField
44067 * value should be in lkey
44069 defaultCurrency : false,
44071 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44073 thousandsDelimiter : false,
44075 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44086 getAutoCreate : function()
44088 var align = this.labelAlign || this.parentLabelAlign();
44100 cls : 'form-control roo-money-amount-input',
44101 autocomplete: 'new-password'
44104 var hiddenInput = {
44108 cls: 'hidden-number-input'
44111 if(this.max_length) {
44112 input.maxlength = this.max_length;
44116 hiddenInput.name = this.name;
44119 if (this.disabled) {
44120 input.disabled = true;
44123 var clg = 12 - this.inputlg;
44124 var cmd = 12 - this.inputmd;
44125 var csm = 12 - this.inputsm;
44126 var cxs = 12 - this.inputxs;
44130 cls : 'row roo-money-field',
44134 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44138 cls: 'roo-select2-container input-group',
44142 cls : 'form-control roo-money-currency-input',
44143 autocomplete: 'new-password',
44145 name : this.currencyName
44149 cls : 'input-group-addon',
44163 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44167 cls: this.hasFeedback ? 'has-feedback' : '',
44178 if (this.fieldLabel.length) {
44181 tooltip: 'This field is required'
44187 cls: 'control-label',
44193 html: this.fieldLabel
44196 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44202 if(this.indicatorpos == 'right') {
44203 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44210 if(align == 'left') {
44218 if(this.labelWidth > 12){
44219 label.style = "width: " + this.labelWidth + 'px';
44221 if(this.labelWidth < 13 && this.labelmd == 0){
44222 this.labelmd = this.labelWidth;
44224 if(this.labellg > 0){
44225 label.cls += ' col-lg-' + this.labellg;
44226 input.cls += ' col-lg-' + (12 - this.labellg);
44228 if(this.labelmd > 0){
44229 label.cls += ' col-md-' + this.labelmd;
44230 container.cls += ' col-md-' + (12 - this.labelmd);
44232 if(this.labelsm > 0){
44233 label.cls += ' col-sm-' + this.labelsm;
44234 container.cls += ' col-sm-' + (12 - this.labelsm);
44236 if(this.labelxs > 0){
44237 label.cls += ' col-xs-' + this.labelxs;
44238 container.cls += ' col-xs-' + (12 - this.labelxs);
44249 var settings = this;
44251 ['xs','sm','md','lg'].map(function(size){
44252 if (settings[size]) {
44253 cfg.cls += ' col-' + size + '-' + settings[size];
44260 initEvents : function()
44262 this.indicator = this.indicatorEl();
44264 this.initCurrencyEvent();
44266 this.initNumberEvent();
44269 initCurrencyEvent : function()
44272 throw "can not find store for combo";
44275 this.store = Roo.factory(this.store, Roo.data);
44276 this.store.parent = this;
44280 this.triggerEl = this.el.select('.input-group-addon', true).first();
44282 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44287 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44288 _this.list.setWidth(lw);
44291 this.list.on('mouseover', this.onViewOver, this);
44292 this.list.on('mousemove', this.onViewMove, this);
44293 this.list.on('scroll', this.onViewScroll, this);
44296 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44299 this.view = new Roo.View(this.list, this.tpl, {
44300 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44303 this.view.on('click', this.onViewClick, this);
44305 this.store.on('beforeload', this.onBeforeLoad, this);
44306 this.store.on('load', this.onLoad, this);
44307 this.store.on('loadexception', this.onLoadException, this);
44309 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44310 "up" : function(e){
44311 this.inKeyMode = true;
44315 "down" : function(e){
44316 if(!this.isExpanded()){
44317 this.onTriggerClick();
44319 this.inKeyMode = true;
44324 "enter" : function(e){
44327 if(this.fireEvent("specialkey", this, e)){
44328 this.onViewClick(false);
44334 "esc" : function(e){
44338 "tab" : function(e){
44341 if(this.fireEvent("specialkey", this, e)){
44342 this.onViewClick(false);
44350 doRelay : function(foo, bar, hname){
44351 if(hname == 'down' || this.scope.isExpanded()){
44352 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44360 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44364 initNumberEvent : function(e)
44366 this.inputEl().on("keydown" , this.fireKey, this);
44367 this.inputEl().on("focus", this.onFocus, this);
44368 this.inputEl().on("blur", this.onBlur, this);
44370 this.inputEl().relayEvent('keyup', this);
44372 if(this.indicator){
44373 this.indicator.addClass('invisible');
44376 this.originalValue = this.getValue();
44378 if(this.validationEvent == 'keyup'){
44379 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44380 this.inputEl().on('keyup', this.filterValidation, this);
44382 else if(this.validationEvent !== false){
44383 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44386 if(this.selectOnFocus){
44387 this.on("focus", this.preFocus, this);
44390 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44391 this.inputEl().on("keypress", this.filterKeys, this);
44393 this.inputEl().relayEvent('keypress', this);
44396 var allowed = "0123456789";
44398 if(this.allowDecimals){
44399 allowed += this.decimalSeparator;
44402 if(this.allowNegative){
44406 if(this.thousandsDelimiter) {
44410 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44412 var keyPress = function(e){
44414 var k = e.getKey();
44416 var c = e.getCharCode();
44419 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44420 allowed.indexOf(String.fromCharCode(c)) === -1
44426 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44430 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44435 this.inputEl().on("keypress", keyPress, this);
44439 onTriggerClick : function(e)
44446 this.loadNext = false;
44448 if(this.isExpanded()){
44453 this.hasFocus = true;
44455 if(this.triggerAction == 'all') {
44456 this.doQuery(this.allQuery, true);
44460 this.doQuery(this.getRawValue());
44463 getCurrency : function()
44465 var v = this.currencyEl().getValue();
44470 restrictHeight : function()
44472 this.list.alignTo(this.currencyEl(), this.listAlign);
44473 this.list.alignTo(this.currencyEl(), this.listAlign);
44476 onViewClick : function(view, doFocus, el, e)
44478 var index = this.view.getSelectedIndexes()[0];
44480 var r = this.store.getAt(index);
44483 this.onSelect(r, index);
44487 onSelect : function(record, index){
44489 if(this.fireEvent('beforeselect', this, record, index) !== false){
44491 this.setFromCurrencyData(index > -1 ? record.data : false);
44495 this.fireEvent('select', this, record, index);
44499 setFromCurrencyData : function(o)
44503 this.lastCurrency = o;
44505 if (this.currencyField) {
44506 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44508 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44511 this.lastSelectionText = currency;
44513 //setting default currency
44514 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44515 this.setCurrency(this.defaultCurrency);
44519 this.setCurrency(currency);
44522 setFromData : function(o)
44526 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44528 this.setFromCurrencyData(c);
44533 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44535 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44538 this.setValue(value);
44542 setCurrency : function(v)
44544 this.currencyValue = v;
44547 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44552 setValue : function(v)
44554 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44560 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44562 this.inputEl().dom.value = (v == '') ? '' :
44563 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44565 if(!this.allowZero && v === '0') {
44566 this.hiddenEl().dom.value = '';
44567 this.inputEl().dom.value = '';
44574 getRawValue : function()
44576 var v = this.inputEl().getValue();
44581 getValue : function()
44583 return this.fixPrecision(this.parseValue(this.getRawValue()));
44586 parseValue : function(value)
44588 if(this.thousandsDelimiter) {
44590 r = new RegExp(",", "g");
44591 value = value.replace(r, "");
44594 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44595 return isNaN(value) ? '' : value;
44599 fixPrecision : function(value)
44601 if(this.thousandsDelimiter) {
44603 r = new RegExp(",", "g");
44604 value = value.replace(r, "");
44607 var nan = isNaN(value);
44609 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44610 return nan ? '' : value;
44612 return parseFloat(value).toFixed(this.decimalPrecision);
44615 decimalPrecisionFcn : function(v)
44617 return Math.floor(v);
44620 validateValue : function(value)
44622 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44626 var num = this.parseValue(value);
44629 this.markInvalid(String.format(this.nanText, value));
44633 if(num < this.minValue){
44634 this.markInvalid(String.format(this.minText, this.minValue));
44638 if(num > this.maxValue){
44639 this.markInvalid(String.format(this.maxText, this.maxValue));
44646 validate : function()
44648 if(this.disabled || this.allowBlank){
44653 var currency = this.getCurrency();
44655 if(this.validateValue(this.getRawValue()) && currency.length){
44660 this.markInvalid();
44664 getName: function()
44669 beforeBlur : function()
44675 var v = this.parseValue(this.getRawValue());
44682 onBlur : function()
44686 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44687 //this.el.removeClass(this.focusClass);
44690 this.hasFocus = false;
44692 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44696 var v = this.getValue();
44698 if(String(v) !== String(this.startValue)){
44699 this.fireEvent('change', this, v, this.startValue);
44702 this.fireEvent("blur", this);
44705 inputEl : function()
44707 return this.el.select('.roo-money-amount-input', true).first();
44710 currencyEl : function()
44712 return this.el.select('.roo-money-currency-input', true).first();
44715 hiddenEl : function()
44717 return this.el.select('input.hidden-number-input',true).first();
44721 * @class Roo.bootstrap.BezierSignature
44722 * @extends Roo.bootstrap.Component
44723 * Bootstrap BezierSignature class
44724 * This script refer to:
44725 * Title: Signature Pad
44727 * Availability: https://github.com/szimek/signature_pad
44730 * Create a new BezierSignature
44731 * @param {Object} config The config object
44734 Roo.bootstrap.BezierSignature = function(config){
44735 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44741 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44748 mouse_btn_down: true,
44751 * @cfg {int} canvas height
44753 canvas_height: '200px',
44756 * @cfg {float|function} Radius of a single dot.
44761 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44766 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44771 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44776 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44781 * @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.
44783 bg_color: 'rgba(0, 0, 0, 0)',
44786 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44788 dot_color: 'black',
44791 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44793 velocity_filter_weight: 0.7,
44796 * @cfg {function} Callback when stroke begin.
44801 * @cfg {function} Callback when stroke end.
44805 getAutoCreate : function()
44807 var cls = 'roo-signature column';
44810 cls += ' ' + this.cls;
44820 for(var i = 0; i < col_sizes.length; i++) {
44821 if(this[col_sizes[i]]) {
44822 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44832 cls: 'roo-signature-body',
44836 cls: 'roo-signature-body-canvas',
44837 height: this.canvas_height,
44838 width: this.canvas_width
44845 style: 'display: none'
44853 initEvents: function()
44855 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44857 var canvas = this.canvasEl();
44859 // mouse && touch event swapping...
44860 canvas.dom.style.touchAction = 'none';
44861 canvas.dom.style.msTouchAction = 'none';
44863 this.mouse_btn_down = false;
44864 canvas.on('mousedown', this._handleMouseDown, this);
44865 canvas.on('mousemove', this._handleMouseMove, this);
44866 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44868 if (window.PointerEvent) {
44869 canvas.on('pointerdown', this._handleMouseDown, this);
44870 canvas.on('pointermove', this._handleMouseMove, this);
44871 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44874 if ('ontouchstart' in window) {
44875 canvas.on('touchstart', this._handleTouchStart, this);
44876 canvas.on('touchmove', this._handleTouchMove, this);
44877 canvas.on('touchend', this._handleTouchEnd, this);
44880 Roo.EventManager.onWindowResize(this.resize, this, true);
44882 // file input event
44883 this.fileEl().on('change', this.uploadImage, this);
44890 resize: function(){
44892 var canvas = this.canvasEl().dom;
44893 var ctx = this.canvasElCtx();
44894 var img_data = false;
44896 if(canvas.width > 0) {
44897 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44899 // setting canvas width will clean img data
44902 var style = window.getComputedStyle ?
44903 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44905 var padding_left = parseInt(style.paddingLeft) || 0;
44906 var padding_right = parseInt(style.paddingRight) || 0;
44908 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44911 ctx.putImageData(img_data, 0, 0);
44915 _handleMouseDown: function(e)
44917 if (e.browserEvent.which === 1) {
44918 this.mouse_btn_down = true;
44919 this.strokeBegin(e);
44923 _handleMouseMove: function (e)
44925 if (this.mouse_btn_down) {
44926 this.strokeMoveUpdate(e);
44930 _handleMouseUp: function (e)
44932 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44933 this.mouse_btn_down = false;
44938 _handleTouchStart: function (e) {
44940 e.preventDefault();
44941 if (e.browserEvent.targetTouches.length === 1) {
44942 // var touch = e.browserEvent.changedTouches[0];
44943 // this.strokeBegin(touch);
44945 this.strokeBegin(e); // assume e catching the correct xy...
44949 _handleTouchMove: function (e) {
44950 e.preventDefault();
44951 // var touch = event.targetTouches[0];
44952 // _this._strokeMoveUpdate(touch);
44953 this.strokeMoveUpdate(e);
44956 _handleTouchEnd: function (e) {
44957 var wasCanvasTouched = e.target === this.canvasEl().dom;
44958 if (wasCanvasTouched) {
44959 e.preventDefault();
44960 // var touch = event.changedTouches[0];
44961 // _this._strokeEnd(touch);
44966 reset: function () {
44967 this._lastPoints = [];
44968 this._lastVelocity = 0;
44969 this._lastWidth = (this.min_width + this.max_width) / 2;
44970 this.canvasElCtx().fillStyle = this.dot_color;
44973 strokeMoveUpdate: function(e)
44975 this.strokeUpdate(e);
44977 if (this.throttle) {
44978 this.throttleStroke(this.strokeUpdate, this.throttle);
44981 this.strokeUpdate(e);
44985 strokeBegin: function(e)
44987 var newPointGroup = {
44988 color: this.dot_color,
44992 if (typeof this.onBegin === 'function') {
44996 this.curve_data.push(newPointGroup);
44998 this.strokeUpdate(e);
45001 strokeUpdate: function(e)
45003 var rect = this.canvasEl().dom.getBoundingClientRect();
45004 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45005 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45006 var lastPoints = lastPointGroup.points;
45007 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45008 var isLastPointTooClose = lastPoint
45009 ? point.distanceTo(lastPoint) <= this.min_distance
45011 var color = lastPointGroup.color;
45012 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45013 var curve = this.addPoint(point);
45015 this.drawDot({color: color, point: point});
45018 this.drawCurve({color: color, curve: curve});
45028 strokeEnd: function(e)
45030 this.strokeUpdate(e);
45031 if (typeof this.onEnd === 'function') {
45036 addPoint: function (point) {
45037 var _lastPoints = this._lastPoints;
45038 _lastPoints.push(point);
45039 if (_lastPoints.length > 2) {
45040 if (_lastPoints.length === 3) {
45041 _lastPoints.unshift(_lastPoints[0]);
45043 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45044 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45045 _lastPoints.shift();
45051 calculateCurveWidths: function (startPoint, endPoint) {
45052 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45053 (1 - this.velocity_filter_weight) * this._lastVelocity;
45055 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45058 start: this._lastWidth
45061 this._lastVelocity = velocity;
45062 this._lastWidth = newWidth;
45066 drawDot: function (_a) {
45067 var color = _a.color, point = _a.point;
45068 var ctx = this.canvasElCtx();
45069 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45071 this.drawCurveSegment(point.x, point.y, width);
45073 ctx.fillStyle = color;
45077 drawCurve: function (_a) {
45078 var color = _a.color, curve = _a.curve;
45079 var ctx = this.canvasElCtx();
45080 var widthDelta = curve.endWidth - curve.startWidth;
45081 var drawSteps = Math.floor(curve.length()) * 2;
45083 ctx.fillStyle = color;
45084 for (var i = 0; i < drawSteps; i += 1) {
45085 var t = i / drawSteps;
45091 var x = uuu * curve.startPoint.x;
45092 x += 3 * uu * t * curve.control1.x;
45093 x += 3 * u * tt * curve.control2.x;
45094 x += ttt * curve.endPoint.x;
45095 var y = uuu * curve.startPoint.y;
45096 y += 3 * uu * t * curve.control1.y;
45097 y += 3 * u * tt * curve.control2.y;
45098 y += ttt * curve.endPoint.y;
45099 var width = curve.startWidth + ttt * widthDelta;
45100 this.drawCurveSegment(x, y, width);
45106 drawCurveSegment: function (x, y, width) {
45107 var ctx = this.canvasElCtx();
45109 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45110 this.is_empty = false;
45115 var ctx = this.canvasElCtx();
45116 var canvas = this.canvasEl().dom;
45117 ctx.fillStyle = this.bg_color;
45118 ctx.clearRect(0, 0, canvas.width, canvas.height);
45119 ctx.fillRect(0, 0, canvas.width, canvas.height);
45120 this.curve_data = [];
45122 this.is_empty = true;
45127 return this.el.select('input',true).first();
45130 canvasEl: function()
45132 return this.el.select('canvas',true).first();
45135 canvasElCtx: function()
45137 return this.el.select('canvas',true).first().dom.getContext('2d');
45140 getImage: function(type)
45142 if(this.is_empty) {
45147 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45150 drawFromImage: function(img_src)
45152 var img = new Image();
45154 img.onload = function(){
45155 this.canvasElCtx().drawImage(img, 0, 0);
45160 this.is_empty = false;
45163 selectImage: function()
45165 this.fileEl().dom.click();
45168 uploadImage: function(e)
45170 var reader = new FileReader();
45172 reader.onload = function(e){
45173 var img = new Image();
45174 img.onload = function(){
45176 this.canvasElCtx().drawImage(img, 0, 0);
45178 img.src = e.target.result;
45181 reader.readAsDataURL(e.target.files[0]);
45184 // Bezier Point Constructor
45185 Point: (function () {
45186 function Point(x, y, time) {
45189 this.time = time || Date.now();
45191 Point.prototype.distanceTo = function (start) {
45192 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45194 Point.prototype.equals = function (other) {
45195 return this.x === other.x && this.y === other.y && this.time === other.time;
45197 Point.prototype.velocityFrom = function (start) {
45198 return this.time !== start.time
45199 ? this.distanceTo(start) / (this.time - start.time)
45206 // Bezier Constructor
45207 Bezier: (function () {
45208 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45209 this.startPoint = startPoint;
45210 this.control2 = control2;
45211 this.control1 = control1;
45212 this.endPoint = endPoint;
45213 this.startWidth = startWidth;
45214 this.endWidth = endWidth;
45216 Bezier.fromPoints = function (points, widths, scope) {
45217 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45218 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45219 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45221 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45222 var dx1 = s1.x - s2.x;
45223 var dy1 = s1.y - s2.y;
45224 var dx2 = s2.x - s3.x;
45225 var dy2 = s2.y - s3.y;
45226 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45227 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45228 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45229 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45230 var dxm = m1.x - m2.x;
45231 var dym = m1.y - m2.y;
45232 var k = l2 / (l1 + l2);
45233 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45234 var tx = s2.x - cm.x;
45235 var ty = s2.y - cm.y;
45237 c1: new scope.Point(m1.x + tx, m1.y + ty),
45238 c2: new scope.Point(m2.x + tx, m2.y + ty)
45241 Bezier.prototype.length = function () {
45246 for (var i = 0; i <= steps; i += 1) {
45248 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45249 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45251 var xdiff = cx - px;
45252 var ydiff = cy - py;
45253 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45260 Bezier.prototype.point = function (t, start, c1, c2, end) {
45261 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45262 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45263 + (3.0 * c2 * (1.0 - t) * t * t)
45264 + (end * t * t * t);
45269 throttleStroke: function(fn, wait) {
45270 if (wait === void 0) { wait = 250; }
45272 var timeout = null;
45276 var later = function () {
45277 previous = Date.now();
45279 result = fn.apply(storedContext, storedArgs);
45281 storedContext = null;
45285 return function wrapper() {
45287 for (var _i = 0; _i < arguments.length; _i++) {
45288 args[_i] = arguments[_i];
45290 var now = Date.now();
45291 var remaining = wait - (now - previous);
45292 storedContext = this;
45294 if (remaining <= 0 || remaining > wait) {
45296 clearTimeout(timeout);
45300 result = fn.apply(storedContext, storedArgs);
45302 storedContext = null;
45306 else if (!timeout) {
45307 timeout = window.setTimeout(later, remaining);